2017/07/18 Service Dev Meetup #1 の資料です。
会場は Speee さんに提供していただきました。ありがとうございました。
自己紹介
●FUJI Goro /@__gfx__
●ビットジャーニーのエンジニア
●最近のスコープ: Ruby on Rails / TypeScript / React / GraphQL
●情報共有サービス Kibela を開発してます
●最近の発表:
● RailsエンジニアがReactを始めてSSRとReduxとTypeScriptを導入するまで
今日の話
●Kibelaにおける技術選定とは ●やらないこと ●やったこと ●これからやることKibelaにおける技術選定とは
●自分︵@gfx︶にとっては技術的挑戦は精神衛生上必要なこと ●興味のある分野に限るが… ●スタートアップにとってはサービスの成長が最も重要 ●技術的挑戦によって機能を磨いて差別化にも繋げられればそれがベストしかし、コスパの悪い挑戦はしない
●リスクが未知数 ●新しすぎて時期尚早 ●作業量が膨大挑戦しない例
❌ ウェブエディタのフルスクラッチ開発
●エディタで気持ちよく書けるのはサービスとして非常に重要 ●では、 :star2: 最高のエディタ:star2: をフルスクラッチ開発するか? ●❌ No! ●自由度はあがる ●しかしKibelaにおける﹁理想のエディタ﹂がどんなものかもまだ見えていない ●WYSIWIG editor vs plain text editor? ●現時点だとリスクもコストもベネフィットも未知数すぎる ●いまはまだやるべきじゃない ●フルスクラッチでのエディタ開発をやっているところはある ●LINE BLOGアプリ開発で contenteditable と戦った話 : LINE Engineering Blog ●contenteditableはritch text editorを作るブラウザ組み込みの仕組みだが、使い方が難しい ●KibelaではFacebookのDraftJSを採用 ●しかしDraftJSはいろいろ問題があった ●カーソルが飛んで行方不明になることがある ●入力できなくなることがある ●スマホでの日本語を入力できないことがある ●DraftJSは開発も停滞しておりFacebookも本番導入予定はなさそう ●つまりフルスクラッチ開発に踏み切るは﹁Facebookにできなかったことが我々ならできる﹂という確信が必要 ●KibelaのエディタはいまCodeMirrorに置き換え作業中 ●設計は古いが実装は枯れている ●GitHubが採用しているのもCodeMirrorなので安心❌ SPA: Single Page Application
●UX向上の見込みはあるが、今はまだ開発コストに見合わないという判断 ●﹁UX向上 > コスト﹂となる画面遷移はありそうなのでそこはいずれSPAにする ●たとえばホーム画面からエントリ画面への遷移はSPAにすると高速になると期待できる挑戦したこと
TypeScript
●Microsoftが開発している静的型をもったaltJS ●構文はほぼJavaScriptと同じTypeScriptのメリット
●TS導入により 生産性があがり、バグが少なくなる ことを期待できる ●静的型は﹁制約によりコードの整合性を保つためのLint﹂として振る舞う ●TypeScriptコンパイラが提供する language-service が優秀で、メジャーなエディタだと補完やリファクタが効く ●RubyMineでブラウザのFile
objectのメソッドやプロパティをを補完している様子
TypeScriptに挑戦した理由
●TypeScriptはリリース当初︵2012年︶から目を付けていて、情報を追っていたのでリスクについてはある程度予想できた ●初期はハマりどころがものたくさんあってオススメできる感じではなかった ●ES 2015の策定とそれを踏まえた TypeScript 2.0 ︵2016年︶によって不満点がほぼなくなったので、導入のきっかけ探していた ●KibelaにReact Reduxの導入するタイミングで静的型がほしくなり、導入を決意したTypeScriptとの付き合い方
●TypeScriptについては過度に期待しないのがよい ●ランタイムとしては良くも悪くもただのJavaScript ●実行時に影響をおよぼす特別な機能はない ●型付けは がんばらない方針。よくわからなければanyでいい ●e.g. がんばらないTypeScriptのはじめ方 - 角待ちは対空 ︵はてなの事例︶ ●型定義ファイル (@types/*
) にもこだわらない。別に使わなくていいし、使っていてもおかしければすぐ捨ててよい
●TSとRailsとの統合でいくつか足りない機能があったのでそのへんは作った
Railsのルーティングヘルパーと仲良くする
●Railsのルーティングヘルパー:wiki_path(blo
g)
⇢ "/wikis/42"
●簡単なクエリビルダつき: wikis_path(page:
1, per: 20)
⇢ "/wikis?page=1
0&per=20"
●当然ながらJavaScriptからは利用できない
●作った: https://github.com/bitjourney/ts_routes-rails
import { wikisPath, wikiPath } as Routes from './generated/routes';
console.log(wikiPath(42)); // "/wikis/42"
console.log(wikisPath({ page: 1, per: 20 })); // "/wikis?page=1&per=20"
●ある程度静的型が付いて、補完も効きやすいので最高
Railsの画像アセットと仲良くする
●Railsの画像アセットはdigest hashがつく ●例:/assets/kibela_logo-f3e74a
6f5c9f46cc4e8b920cb.svg
●これをJavaScriptから参照するのは難しい
●作った: https://github.com/bitjourney/ts_assets-rails
import * as React from 'react'; import { ImageRubyIcon } from './generated/assets'; class MyComponent extends React.Component<any, any> { render() { return ( <ImageRubyIcon // for app/assets/images/ruby-icon.svg className='rubyLogo' /> ); } }
これから挑戦したいこと
●GraphQL ●これから話す ●React Native ●今日は話しませんなぜGraphQLに注目したか
●モバイルアプリを開発するにあたってWeb APIがほしい ●将来的にWeb APIを公開したい ●なるべくコスパよく!GraphQL
●RESTの次の地位を狙う Web APIのリクエストフォーマット を定めるもの ●インターフェイスの定義はエンジニアが記述する ●DBアクセスはActiveRecordを通じて︵Railsの場合︶ ●検索はElasticsearchへのアクセスを実装したモデルを叩き ●モデルの関連︵association︶も表現できる ●実サービスの例としては GitHub API v4 のAPI consoleを触るとよい ●https://developer.github.com/v4/explorer/ ●たまに動かなくなるので時期尚早感は否めない ●Incognito Windowだと動いたりするので相性の悪いChrome extensionがあるっぽい ●まだKibelaには導入していないものの、仕様にグッときたので導入を決意 ●RESTの延長にある雰囲気が気に入った ●GitHubやFacebookという大手が使っているのも導入の決め手例: GitHub API v4
request:query { # トップレベルは必ず "query" または "mutation"
viewer { # ログインユーザから見れるデータで…
# ログインユーザが管理するリポジトリの最初の3個をudpated_atでソートして
repositories(first: 3, orderBy: { field: UPDATED_AT, direction: DESC }) {
nodes { # data nodeのリスト
name # リポジトリの名前をリクエストする
description # リポジトリの説明をリクエストする
}
}
}
}
response:
{ "data": { "viewer": { "repositories": { "nodes": [ { "name": "ts_routes-rails", "description": "Exports Rails URL helpers to TypeScript" }, { "name": "rouge", "description": "A pure-ruby code highlighter" }, { "name": "pygments.rb", "description": "pygments syntax highlighting in ruby" } ] } } } }●クエリの基本はシンプル ●コレクションの操作が独特なので習熟が必要 ●API consoleの実装であるGraphiQL ︵グラフィクル︶が優秀 説明付きでフィールドを補完してくれたり‥ 内蔵されているドキュメントビューアも優秀で、クエリをいじりながらすぐに検索できる。とはいえ、慣れると補完だけでほぼいけるようになる。
GraphQLに関するよくある誤解
●レイヤ的にはあくまでも﹁アプリケーションのインターフェイス﹂を定めるもの ●RDBMSやNoSQLなどのストレージのレイヤではない ● というのは﹁ RESTでアクセスできるRDBMS﹂というのと同じ水準の話 ●MongoDBはHTTP interfaceがあるのでありえない話ではないが… :innocent: ●Graph DB とは関係ない ●﹁ N+1が心配﹂⇢ RESTに置き換えて考える ●1 resource : 1 endpoint厳守のRESTだと簡単にクライアントサイドで N+1 になる ●GraphQLの場合、サーバーサイドで完結する部分が多く工夫の余地があるぶんマシ ●﹁複雑なクエリで過負荷にならないか心配﹂⇢ RESTに置き換えて考える ●RESTだとRate-Limitで制限するが、GraphQLは別の方法で制限する必要がある ●gRPCなどのRPC frameworkと何が違う? ●GraphQLは﹁必要最小限なデータを1リクエストで取得する﹂ことに集中している ●取得するフィールドを最低限に ●幾つかの型のリソースをまとめて ●GraphQLの比較対象はRESTで、RPC frameworkではなさそうコード例
KibelaのGraphQL pull-request︵開発中︶より抜粋‥ たとえば単一のリソースの取得:# トップレベルの "query" 型 (トップレベルはquery or mutation) Types::QueryType = GraphQL::ObjectType.define do name "Query" # GraphQLは仕様としてdescriptionを書ける # descriptionはドキュメントビューアで使われる description "The query root of the schema" # 「blog」というリソースを宣言する field :blog do type Types::BlogType # blog リソースを取得するにはただひとつの必須引数idが必要 argument :id, !types.ID resolve ->(_object, args, context) { # current_userがアクセス可能なblogをidで検索する # 戻り値はtypeで宣言したTypes::BlogTypeで宣言されたフィールドを持つ Blog.accessible_for(context[:current_user]).find_by!(id: args["id"]) } end end●結局、パラメータがあってそれを元にActiveRecord経由でデータを検索するという点においてRESTと違いはない ●全てのパラメータとレスポンスのフィールドを明示的に宣言しなければならないので、作業量としてはそれなりにある ●とはいえ新規開発するなら作業内容が変わるだけといえる ●最終的には routes.rb からJSON APIを一掃できるのでスッキリ ●内部API / モバイルアプリ用API / 外部APIを実装上区別する必要がないのは大きい ●認証方法︵それに伴うrate-limit的な制限︶の違いはあるのでエンドポイントは変えるかも