Slide 1

Slide 1 text

freee 株式会社 Railsで実装されている機能の マイクロサービスへの切り離し 2018.12.14

Slide 2

Slide 2 text

2014年、新卒として五反田の会社へ入社。 いくつかの位置情報ゲームの開発に携わった。 2016年、同じく五反田のfreeeへ入社。 会計freeeの開発を行なっている。 今一番好きな言語はRust。 あるお店のハンバーガーが好きすぎて勝手に注文 システムを作ってる。 @mihyaeru21 ミヒャエル freee株式会社 2

Slide 3

Slide 3 text

3 話す内容 ● なぜ切り離すのか ● 切り離す境界の選定 ● 実装の置き換え ● 気をつけたポイント ● 厳しかったこと

Slide 4

Slide 4 text

01 なぜ切り離すのか 4 Section

Slide 5

Slide 5 text

5 1つのアプリケーションが大きくなりすぎた ● 開発者の増大 ● コードの複雑さの増大 ● 機能が不用意に密結合になってしまう ● デプロイの粒度が大きい

Slide 6

Slide 6 text

6 噴出する問題 不用意な密結合 ● 規約では治安を維持しきれない ● 一度不用意な密結合が生まれると、そ れが前例となりなかなか排除できない ● 変更の影響範囲が想像以上に広がる ことで開発難易度が上昇 デプロイ巨大化 ● 毎日大量のプルリクがマージされる ● 1つのアプリケーションを大勢で開発す る限り避けられない

Slide 7

Slide 7 text

7 マイクロサービス化を進めることで 不用意な密結合 ● 機能が強制的に分離され、仕組み上密 結合が防がれる ● 機能の境界が明確に定義され、変更に よる影響が見えやすい デプロイ巨大化 ● 個別にデプロイができる ● デプロイの粒度が小さくなる

Slide 8

Slide 8 text

8 マイクロサービス化を進めることで ● 今回の切り離しを実施した機能は、他のアプリケーションからも利用する

Slide 9

Slide 9 text

9 マイクロサービス化にはデメリットもある ● 移行自体にかかるコストがかかる ● 間にネットワークが入る ○ 実装方法によってはパフォーマンス問題が発生する ○ ネットワーク起因のエラー ● トランザクションを使い整合性を担保できない部分がでてくる ○ 機能の実装が難しくなる

Slide 10

Slide 10 text

メリットとデメリットを天秤にかけた結果 やっていく気持ち

Slide 11

Slide 11 text

02 切り離す境界の選定 11 Section

Slide 12

Slide 12 text

12 切り離し対象機能 ※これらは実際に切り離した機能の関係に似せたダミーです 販売の状態を管理 ● 自動車 ● バイク ● おもちゃ ● etc マーケティングチャネルの設定 ● 販 売 状 態は、あらかじめ設 定したチャ ネルを利用する

Slide 13

Slide 13 text

13 リソースに分解して境界を見つける アプリケーション 自動車 バイク おもちゃ 販売状態 チャネル ● 機能を構成する要素を関連するいく つかのリソースとして分解する ● アプリケーションに固 有の要 素なの かを検討する ● 固有でないものを切り離し対象とする

Slide 14

Slide 14 text

14 リソースに分解して境界を見つける アプリケーション マイクロサービス 自動車 バイク おもちゃ 販売状態 チャネル ● 今回は2つのリソースが切り離し対象 に決定 ● DB的には10個程度のテーブルが該 当

Slide 15

Slide 15 text

15 この境界でデータの整合性は大丈夫? チャネル ● マイクロサービス側のみで作成・更新・削除処理を実行する ● マイクロサービスの中でトランザクションを使えばOK

Slide 16

Slide 16 text

16 この境界でデータの整合性は大丈夫? 販売状態 ● アプリケーション側で自動車販売リソースを作成・削除した時、マイクロサービス側で も販売状態リソースを作成・削除する ● 自動車販売リソースの更新と販売状態リソースの更新は、ほぼ独立している ● マイクロサービス側の実装を冪等性を持つように実装できれば、厳密なトランザク ションでなくともほぼ問題なく状態を同期できる

Slide 17

Slide 17 text

この境界で分離しても大丈夫そう

Slide 18

Slide 18 text

18 マイクロサービス側のインタフェース Protocol Buffersで作成 ● 先行していたマイクロサービスでの機能開発でgRPCを利用しており、内部 的な通信にはgPRCを使っていくことが既定路線 ● 切り離す対象のリソースを操作するユースケースを元にRPCを洗い出す ● 実装していくうえで足りないものは見えてくるので、この時点では大雑把に

Slide 19

Slide 19 text

19 マイクロサービス側のインタフェース インタフェースの整理を決意 ● 移行前のコードはActiveRecordで前提になっている ● ロジックをそのままコピペして切り離すだけだとサービスとしての使い勝手が悪い ● 独立したサービスとしてのあるべきインタフェースが必要

Slide 20

Slide 20 text

03 実装の置き換え 20 Section

Slide 21

Slide 21 text

アプリケーション 21 切り離し前のイメージ モデル DB コントローラ

Slide 22

Slide 22 text

マイクロサービス 22 切り離し後のイメージ gRPCサービス アプリケーション モデル DB コントローラ ユースケース DB

Slide 23

Slide 23 text

23 どうやる? ● マイクロサービスを作ってからアプリケーション側を入れ替える? ● アプリケーション 側 で 新 しいインタフェースを 満 たすようにリファクタリングしてか ら、裏側をマイクロサービスに入れ替える? ● データはどこに持つ? ● 入れ替えるときは全部一気に?一部ずつ? ● ……

Slide 24

Slide 24 text

実際に行なった手順

Slide 25

Slide 25 text

アプリケーション 25 初期状態 モデル DB コントローラ

Slide 26

Slide 26 text

マイクロサービス 26 マイクロサービス側を実装 gRPCサービス アプリケーション モデル DB コントローラ

Slide 27

Slide 27 text

アプリケーション マイクロサービス 27 ユースケースを実装 gRPCサービス モデル DB コントローラ ユースケース

Slide 28

Slide 28 text

アプリケーション マイクロサービス 28 ユースケースで2つの実装を切り替え gRPCサービス モデル DB コントローラ ユースケース 実装を 切り替え

Slide 29

Slide 29 text

アプリケーション マイクロサービス 29 旧ロジックを使用する実装を削除 gRPCサービス モデル DB コントローラ ユースケース

Slide 30

Slide 30 text

マイクロサービス 30 DBを分離 gRPCサービス アプリケーション モデル DB コントローラ ユースケース DB

Slide 31

Slide 31 text

マイクロサービス 31 完了! gRPCサービス アプリケーション モデル DB コントローラ ユースケース DB

Slide 32

Slide 32 text

04 気をつけたポイント 32 Section

Slide 33

Slide 33 text

33 少しずつ置き換えていく 1. 最も独立しているチャネルの読み込みコントローラのみ 2. チャネルの書き込みコントローラまで拡大 3. 販売状態の読み込みを伴う自動車販売のコントローラのみ 4. 販売状態の書き込みを伴う自動車販売のコントローラまで拡大 5. バイク販売、おもちゃ販売のコントローラまで拡大 6. その他のリソースに関するコントローラまで拡大 7. コントローラ単位ではないモデルレベルでの参照を置き換え 8. 古い実装を削除

Slide 34

Slide 34 text

34 リソースを操作するクラス群を導入 ● マイクロサービス側のリソースに関わる操 作に対 する責務を負う ○ マイクロサービス 側 のユースケースと、アプリ ケーション側のユースケースの間を埋める ● マイクロサービス側、アプリケーション側の実装を 切り替える役目も兼ねる ● リソースの数 x 操作の種類 の掛け算でたくさん マイクロサービス gRPCサービス アプリケーション モデル DB コントローラ ユースケース DB

Slide 35

Slide 35 text

35 リソースのアダプタ+α ● マイクロサービスから取 得したリソースそのままで はちょっと困る ○ JSONにシリアライズする部分 ○ マイクロサービス側リソースではidしか持ってい ないデータ ■ ユーザの名前など ● アダプタ兼、追加のデータを効率良く取得するため にラッパー マイクロサービス gRPCサービス アプリケーション モデル DB コントローラ ユースケース DB

Slide 36

Slide 36 text

05 厳しかったこと 36 Section

Slide 37

Slide 37 text

37 ActiveRecord系Gemの置き換え ● マイクロサービスはGolangで実装した ● ActiveRecordで実装されたGemを切り離し対象モデルで使用していた ● 幸い、必要な機能はGemで提供されているうちの一握りだった ● 必要な機能のみをGolangで実装した

Slide 38

Slide 38 text

38 モデルのvalidationが難しい ● 自動車販売やおもちゃ販売を表現するモデルの validationが販売状態の状態に一部依存していた ● 該当するvalidationはユースケースのクラス経由で 行うようにして対応した ● その 他 は、マイクロサービス 側 、アプリケーション 側でそれぞれvalidationを実行する アプリケーション マイクロサービス 自動車販売 バイク販売 おもちゃ販売 販売状態 チャネル アプリケーション マイクロサービス gRPCサービス モデル DB コントローラ ユースケース 実装を 切り替え

Slide 39

Slide 39 text

39 事故 ● マイクロサービス側が古い状態なのにアプリケーション側を新しくしてしまい、呼び出 そうとしたRPCが存在せず…… ● リリースの回数が多すぎて混乱してた ● 小出しにした事自体は良かったはず ● マイクロサービス側に処理を切り替えるタイミングで、マイクロサービスのバージョン をチェックするようにした

Slide 40

Slide 40 text

実はまだ移行完了していません

Slide 41

Slide 41 text

41 まだ実装切り替え状態 ● コントローラ単位での移行は全て完了済み ● モデルの関連経由で呼び出されている部分が少し 残ってる ● 意外な部分で関連が見つかると駆逐完了が遠のく アプリケーション マイクロサービス gRPCサービス モデル DB コントローラ ユースケース 実装を 切り替え

Slide 42

Slide 42 text

まとめ 42

Slide 43

Slide 43 text

43 まとめ ● マイクロサービス化のメリットとデメリットを検討 ● リソース単位で分離の境界を選定した ● 切り戻しを意識しながら小さな単位で段階的に移行した ● アプリケーション側のユースケースに該当するクラスを作成した ● 移行の方法はいろいろあるはずなので懇親会でお話しましょう!