フルJSアーキテクトで作るairCloset

松本雄太氏(以下、松本):株式会社エアークローゼットから来ました、松本と申します。よろしくお願いします。「@YutamaKotaro」というTwitterネームでやっているので、もしよかったらフォローしてください。社内ではJimmyと呼ばれています。「フルJSアーキテクトで作るairCloset」というタイトルで発表させていただきます。

まず、簡単に弊社の紹介をします。airClosetは「『発想』と『IT』で人々の日常に新しいワクワクを創造する」ことをミッションにしております。

サービスの紹介をさせていただきます。まず、「airCloset」というサービスをやっております。

airClosetはオンラインのファッションレンタルサービスで、プロのスタイリストが服を選びます。そして、お客様はその届いた服を着ていただいて、返していただきますと、また次の服が送られてくるといった、ファッションレンタルのサービスになっています。気に入ったら購入していただくことも可能です。

他には、airClosetはオンラインの場ですが、リアルでスタイリストさんに会う場として「airCloset×ABLE」という実店舗を経営しております。実店舗は表参道にあります。

最後に、「pickss」というサービスもやっています。

airClosetはファッションレンタルですが、pickssの場合はECの側面が強く、スタイリストさんが服を選んでくれるところまでは一緒で、それがお客様の手元に届いたときに試着していただいて、気に入ったら購入していただくというサービスになっています。

(スライドを指して)airClosetのサイトはこちらです。こういった感じでスタイリストさんが選んで、それを何回でも借りることができます。返す時のクリーニングや洗濯は不要です。

pickssは、レンタルではなく、試着して気に入ったら買うという仕組みのサービスになっております。

自己紹介ですが、airClosetのプロダクトグループの中にある、CAEチーム(注:2018年11月13日時点での名称)のリーダーをやっております。チームの解説はあとでさせていただきます。

僕自身はWebのフロントエンドもそれなりにやるんですが、一番得意なのはReact Nativeという、アプリを作る技術とかが得意なエンジニアです。最近はちょっとデータサイエンス寄りになってきていて、データサイエンスのこともいろいろと勉強していたり、そういった仕事もやっています。でも、SQLとかは苦手です。

コマース的な紹介も挟んでいいですかね(笑)。去年ぐらいにReact Nativeの同人誌も出しています。

今はReact Nativeというフロントエンドの技術で、アプリケーションを作ることができていて、Skypeとかもそうですし、FacebookやInstagramといった、よく使われるようなアプリとかも、実はReact Nativeでできています。

いろんな会社さんで実際に使われている中で、「どういうふうに使っていますか?」「なぜ使っていますか?」とか、「どういうライブラリを使ってますか?」というところを取り上げて、本を出しております。私のTwitterのリンクからいけるので、もしよかったら見てみてください。

プロダクトの紹介と開発体制

プロダクトの紹介をさせていただきます。プロダクトは大きく分けて4つあります。

そのうちの2つは先ほど見ていただいた、「airCloset」と「pickss」というサービスで、あと「airCloset-console」という、スタイリストさんが社内で使う、お客様の服を選ぶためのWebサービスがあります。他には「airClosetBatch」という、サービスを運用するためのバッチがあります。

だいたいのサービスの概要はこんな感じです。

Webサイトでお客様に登録していただいて、登録した情報をもとに、「スタイルカルテ作成」や「選定」ということをやっていくのがairCloset-consoleになります。

サイト上でお客様が登録をしたり、(服を)受け取ったり、気に入ったら買っていただいたり、感想を書いていただくところが、airClosetのサービスになっております。

僕らがサービスをどういうふうに開発しているのかを説明していきます。

(スライドを指して)このCAEというチームに僕はいて、エンジニアは5人います。

僕らはフロントエンドのチームとか、バックエンドのチームみたいに分かれていなくて、みんながみんな同じようにやっているんですが、CAEはフロントエンドとアプリが得意な人が多いです。でも、ちょっと変な人と、うるさいやつがけっこう多くて、人事の方から怒られることがしばしばあります。

あと、BAEというチーム(注:2018年11月13日時点での名称)が社内向けのWebサービスを開発していて、SQLが得意な人が多いです。たまにSlackを見ると、SQLだけで会話している時とかがあったりして、僕らはそれを見ながら、「あっ、こいつらやばいな」という話をこそこそしています。

Creativeというチームはデザイナーさんが所属しているチームで、こちらは3人います。Webサイトやアプリだけじゃなく、いろいろと物理のところもデザインしています。このスライドも僕が作るとダサいので、「ちょっとテンプレートを作ってください」と言って、作ってもらったりしています。

あと、ディレクションのチームがあります。組織体制の変更とかがあって、1人になってしまいましたが、ディレクターが1人います。特にここはWantedなので、もしやりたい方がいらっしゃったら、ぜひお声がけいただけると幸いです。

こんな感じで開発をしております。バッチの話とかはバックエンド寄りの話になってしまうので、airClosetとpickssのお客様向け(部分)についてのアーキテクトを話していきたいと思います。

airClosetのアーキテクト

airClosetのアーキテクトについて説明します。

(スライドを指して)全体的にはこういう感じで、いろんな技術を使っています。

APIでいくと、バックエンドのところですね、Node.jsを使って開発をしていて、Koaと、ORMとしてSequelizeを使っています。WebのサービスサイトのほうはSSRをやっていてReactで開発をしています。アプリはReact Nativeで作っています。

airClosetのアプリについては、今リリースされているものはSwift製なんですが、今Reactive Nativeに置き換えているので、今日はそちらを紹介させていただきます。

あと、基本的にはESLintやMochaなどを使っています。PrettierとFlowについては一部使っています。Flowは最近の事情からいくと、TypeScriptに変えたほうがいいかなと思っているので、置き換えの予定ではあるんですが、あまり時間が取れていないので浸透はさせていません。

CIのフローとしては、GitHub上でプッシュあるいは、マージされたあとに、ESLintとFlowとMochaのテストを通して、通ったらビルドしてビルドしたコードをS3にアップロードしています。

リリースする時はAnsibleを使っていて、AnsibleでS3からビルドしたソースを、上げたところから取得して、EC2のインスタンスを立てたり、S3に静的なファイル等をアップするということをやっていて、そのあとにCloudfrontのキャッシュを削除しています。

フルJSのメリット

ここまでの説明でおわかりいただけたかもしれませんが、API、Web、アプリをやっていますが、全部JSを使っています。フルJSでやっています。

フルJSのメリットを話していきたいと思います。フルJSのなにがいいかというと、まず「スキル的にできない」ということがないです。なんでかというと、そもそも全部JSなので、「1つできたら、だいたい他のやつも触れるよね」ということもあって、学習コストが低いです。

エディタやツールも全部使い回せます。例えば、フロントエンドのエディタだと、WebStormやVS Codeなどが、どのプロジェクトでも使えますし、ESLintやPrettierといったとツールも使い回せます。

なので、全員がフロントエンドとバックエンドの両方を開発するというスタイルをとっています。また、「こういう時はこう書くといいよ」というナレッジも共有しやすいです。

あと、「使えない」ということもあまりないです。

そもそも同じ言語なので、処理をまとめさえすれば、他のプロダクトでも使えます。まぁ実際のところ、nodeの環境やフロントの環境、あとReact Native独特の環境とかがあったりするので、気を使って作ってあげる必要はあります。

pickssでの共有化

今しゃべっていた「使えない」という部分であったり、共用化のところでどういうことができるかは、pickssの方が強くやっているので、そこについて紹介させていただきます。

pickssでは、LernaというMonorepoのツールを使っています。

Monorepoが何かというと、例えば1つプロダクトがあったとして、アプリがあったり、APIがあったり、バッチがあって、普通はそれぞれリポジトリを立てると思うんですが、それを全部まとめて1つのリポジトリとして扱うという手法になります。

なので、お客様向けと社内向けのアプリケーションを、全部まとめて1つのリポジトリとして開発していきます。なぜいいかについては、あとで説明させていただきます。

同じリポジトリにまとまっているので、開発のしやすさと共有のしやすさは、とてもやりやすいんですが、CIテストの時間とかがけっこうかかってしまうデメリットがあります。

いろいろと省略させていただきますが、具体的にはクライアントのアプリケーションがあります。そしてクライアントのAPIがあります。あとは社内向けのWebサービスがあります。社内向けのAPIがあります。その他に、例えば在庫管理サービスや認証サービスを、Monorepo内のリポジトリとして独立させて作っています。

こんな感じで、アプリケーションとサービスがMonorepoの中に複数含まれています。こうするとなにがいいかというと、要はフロントエンドがやることは基本的にフロントエンド向けのサービスを呼び出すだけになります。バックエンドがやることも、アプリケーションはサービスを呼び出すだけになります。

スライドに書いてあるのは、在庫サービスに在庫があるかを問い合わせています。基本的にAPIとかであれば、中に情報を取ってきて、判定したりとかが入ってくるんですが、stockServiceというのに全部まとまっているので、このstockServiceを使って、レスポンスを返してあげるだけみたいな構造にすることができます。

共有化がもたらしたもの

「どう共有化が役に立っているか?」について、在庫の例で話していきます。

(スライドを指して)これはpickss画面ですが、カレンダーの日付が出ていて、色がついているところとグレイアウトしているところがあります。これは在庫がある日とない日で分かれています。

pickssは「スタイリングされた服を指定日にお届けする」という試着サービスです。また、スタイリストさんを指名することもできます。このサービスの特性上、在庫がある日にしかお届けができません。

そもそもstockServiceの方で何をやっているかというと、「お客様が登録されている服の在庫があるか」「発送先がどこになっていて、何日かかるから〇日に送ることができるよ」とか、スタイリストを指名した場合であれば、「そのスタイリストさん空いてるよね?」「空いていないよね?」という空き情報も含めて、細かな条件がたくさんあります。

今この画面を出す時に、stockServiceというものを使っていたんですが、stockServiceを使うのは、お客様の画面だけではなくて、例えばCSサポートの方が社内向けのツールを使って、お客様の配送希望日を変更するとか、お客様から問い合わせがあった時に配送可能日を確認するとか、プロジェクトチームが在庫状況を確認するところでも、stockServiceは呼び出されて使われています。

こんな感じでstockServiceを作ってしまえば、いろんなところで使い回すことができます。また、このstockServiceというものが、このサービスの仕様を表します。つまり、このコードがサービスの仕様を表すようにできます。

例えば、お客様向けのサービスを作るCAEというチームと、社内向けのサービスを作るBAEというチームで、チームは違いますが、仕様を確認しなくても、ちゃんとコードを見てあげれば意思疎通ができます。あと、「1回作ってしまえばみんな使えるようになるから、みんな幸せだよね」というところがあったり、効率的に修正できるようになります。

今pickssのWebサイトは止めているんですが、昔はWebとアプリで、ドメインロジックの共有というのもやっていました。今はUX向上のため、アプリに注力していますが、1回作ったものをガンガン使い回していけるというスタイルで、素早い開発を実現しています。

この仕様を追加した時も、Monorepoなので、普通に書いてまとめてプルリクを出せます。subModuleとか、普通に別モジュールとして切り出してあげると、バージョンの差異があったりとか、自分で修正する時も大変だったりしますが、Monorepoを使った場合はそういうストレスがないです。

フルJSのデメリット

メリットをたくさん話していますが、もちろんデメリットもあります。まぁ、やっぱり飽きます。毎日JSばかりを触っていると、「またこれかよ……」ということがあったりして、飽きてきます。

あと、技術修練が進まないことも課題として感じてはいます。実際のところ、フロントエンドのところはJavaScriptを使っているし、APIのところもいろいろやってはいますが、実際にReact Nativeとかを使っている場合、Objective-CやSwift、Javaの知識が必要になったりします。

他の言語には、他の言語の特色や良さがあったりもします。もちろんパフォーマンスの良さもあるし、シンタックスの良さもあるんですが、そういったことがあったりするので、JSだけに頼ってしまうと、あまり(技術修練が)進まないかなと感じています。

これもJS界隈あるあるなんですが、パッチレベルのアップデートがきつい時があります。普通にメジャーアップデートで仕様がガラッと変わることはあるんですが、パッチレベルでも仕様が変わることがけっこうあります。

要は早いし、変更の度合いがワイルド過ぎたりして、パッチレベルのアプデで動かなくなったりします。「これバージョンを完全に固定しまくるしかないな」と思うこともあります。

チームマネジメントについて

あとは、チームマネジメント的なところで、レビューを密にやっています。それぞれのプロダクトについて、いろいろな設計概念があったりして、基本的には設計概念に沿わない実装はNGです。

期日的にやばい場合を除いて、再利用するモジュールのテストは、かなりみっちり書きます。ほかのチームが変更を加えることもあるので、そこで仕様変更になってしまうようなものは、必ずテストで検出できるようにしています。

僕は「テストは第2の仕様書としての機能も持っている」という考えを持っているので、それを実装させて、「仕様だけじゃなくて、そのモジュールの使い方が開発者にわかるようにしよう」ということもやっています。

例えば、(スライドを指して)これは passwordCreate という関数を作っているんですが、その仕様について、どういうことが行われるかとか、「できてくるインスタンスってメンバーのインスタンスだよね」「空のデータを渡して作ってあげた時は、デフォルトの値が使われますよ。デフォルトの値はこうです」みたいなところも、全部書いていたりします。

こういった感じで使い方がわかるように書くところも、かなり入念に徹底してやっています。

新しい技術で取り外し可能なものについては、積極的に入れています。最近だと、CSS Modulesを使っていたところに、またstyled-componentを入れたりして、「これ使いやすいよね」「使いにくいよね」というディスカッションをしたりしました。

部分的に組み込めるものはガンガン組み込んでいって、実践的にやってみた結果、どうだったかを見て、良ければ全面的に普及させていくというかたちになっています。

新しい技術でも取り外しが難しいものもあったりします。フロントエンド界隈でいくと、データフローを制御するReduxやMobXとかは、ちょっと取り外しが難しかったりするので、ちゃんと吟味した上で、可能であれば局所的に試してみるという感じでやっています。

フルJSアーキテクトは「早い、安い、美味い」

まとめに入ります。はっきり言って、フルJSのアーキテクトは、早いし、安いし、美味いと思っています。

Node.jsとReact Nativeもかなり優秀なので、別に安かろう悪かろうではないです。

だけど、毎日同じものばっかりやってたら飽きるので、「味付けを変える必要があるよね」ということで、新しい技術を積極的に導入しています。

メンバーからの提案も積極的に受けて、僕もいろいろと考えはしますが、「入れてみてもいいよね」と思うものはだいたい許可しています。

逆にこういった新しい技術に取り組みやすいというのも、みんなが各々JSを極めている状態にあって、JSの修練度が高いがゆえに許容できるのかなと思います。。

以上です。ご清聴ありがとうございました。

(会場拍手)