13章 マイクロサービスのリファクタリング
- ついに最終章である13章
- モノリシックアーキテクチャをマイクロサービスアーキテクチャへとリファクタリングする方法について
- 著者の主張は、最初はモノリシックで動くものをサクッとつくってから、メンテナンス性向上のためにマイクロサービスに変えるのが良いとしています
- なので必然的に、モノリシックからマイクロサービスへのリファクタリングは必須となります。
マイクロサービスへのリファクタリングの概要
- まず、システムを0から作り直す「ビッグバンリライト」は止めた方がいいとのこと
- これは元と同じ機能を再現するだけで数年かかってしまうのでビジネス的になりたたないから
- 私の実体験からいってもこれは確かにその通り。ビジネス的に新たな価値を提供することができないので、会社的にも見放されてしまうかもしれません。
- ストラングラーアプリケーション
- 元のシステムから少しずつ一部を切り出してサービス化していって、最後には全体がマイクロサービスになるというやり方。著者的にはこちらがおすすめ。
- このやり方ならマイクロサービス化した部分は生産性が高くなるので、ビジネス的に必要な機能を取り込みやすくなります
- 移行過程では、(生産性の低い)モノリスの部分に対する変更量がなるべく少なくなるようにするのが大事
- そう入ってもサービスを切り出すのには、データベースリファクタリングも必要ですけど
モノリスをマイクロサービスに移行させるための方法
3つ挙げられています
- 新機能をサービスとして実装する
- プレゼンテーションティアとバックエンドを切り離す
- モノリスから機能を抽出してサービスで置き換える
新機能をサービスとして実装する
- 分析的にみて、新たなサブドメインの追加になるケース
- 手順としては大体以下
- 新たなサブドメイン用のサービスを立ち上げる
- APIゲートウェイで、モノリスと新サービスの振り分けを行う
- モノリスと新サービスで通信できるような仕組みを双方に入れる。これをインテグレーショングルーと呼ぶ
- 通信がリクエスト・レスポンスなのかイベントなのか、同期なのか非同期なのかは設計次第
プレゼンテーションティアとバックエンドを切り離す
ここは文字通りなので省略
モノリスから機能を抽出してサービスで置き換える
ここがメインです。
- 手順
- 分析レベルでドメインモデルを分割・抽出する
- それに対応するようにデータベースリファクタリングする(別テーブルあるいは別データベースに分割する)
- あとは新機能をサービスとして実装する場合と大体同じ。インテグレーショングルーも必要に応じて作成する
問題はテーブルの分割に伴って、それを参照する実装コードを全面的に書き換えるというところです。
これを緩和する手段として、OrderテーブルからDeliveryを抽出する例が説明されています。
- Orderテーブルの中で、Deliveryに関するフィールドを特定する
- Deliveryテーブルを作成する
- Deliveryテーブルへの書き込みはトリガーを使って、Orderテーブルの該当フィールドに反映する
- モノリスはOrderテーブルをread/writeするが、Delivery部分はread onlyで扱う
- Deliveryサービスは、Deliveryテーブルをread/writeする
こうするとOrderテーブル側は、修正してないので、ORマッパーなどは変更なしで済みます
これを行う上で重要な点が2つ
- 腐敗防止層
- インテグレーショングルーによりモノリスとサービスが通信するにせよ、トリガーによるテーブルの同期にせよモノリス側の言葉(ユビキタス言語)をそのままサービス側に見せてしまったら、せっかく抽出したらサービス側が正しいドメインモデルになりません
- なのでインテグレーショングルーには、モノリス側のユビキタス言語とサービス側のユビキタス言語を変換する層が必要になります
- ひらたく言えば、フィールド名の変換や、値の変換などを行います
- モノリスからのイベントパブリッシュ
- 真正面からこれをやろうとするとモノリス側の変更量が多くなってしまうので、モノリス側のテーブルのトランザクションログをテーリングしてイベントを発行するとよい。内容が低レベルになってしまっても大丈夫ならモノリス側の変更量が抑えられる
データ整合性
- モノリスからサービスを抽出すると、当然、トランザクションによるデータ整合性の保証ができなくなります
- なのでサーガパターンによるトランザクションのシーケンスに乗り換えることになります
- 問題はサーガによるトランザクションシーケンスの先頭の方にモノリス側のトランザクションがあると、ロールバックするときにモノリス側に打ち消し用の処理を追加しなければならなくなります
- こういうときは、サービスの抽出順を変えて、なるべくモノリス側での変更を少なくするとよい
- 例えば、FTGOの例では、サーガの先頭でOrderを作成したりするので、Orderを最初にサービスとして抽出すれば、モノリス側でOrderの打ち消し処理をやらなくて済む
- 逆に例えばTicketを先にサービスにしてしまうと、Orderがモノリスに残るので、モノリス側でOrderの打ち消し処理が必須になってしまう
認証認可
- モノリスならセッションだけで済んでいただろうけど、サービス側はJWTなどが必要になるので、移行途中はセッション+JWTの併用にするとのこと
- この変更は、変更箇所が局所化されているので、やればできるレベルではあるでしょう。
感想
- 原理上、可能なのは分かるけど、どれだけ頑張ってもあまりにも修正量が多いと言わざるを得ない
- 稼働中のシステムを、このやり方でマイクロサービスに移行させるとして、途中の段階でリリースするためにいったいどれだけ膨大なテストが必要なのだろうと思う
- もともとの著者の主張である「最初はモノリスで作って、途中からマイクロサービスに移行する」は本当に正しいのか?
- 「プロトがモノリスで、製品はマイクロサービス」ならまだ理解できる
- この主張の背景には、「マイクロサービスアーキテクチャは素早くはじめることができない」があると思うが、こういうところこそ、今後のテクノロジーの進化で改善してほしいと思う
この本全体を通して
- いままで慣れ親しんできたモノリスとのギャップはあまりにも大きい
- トランザクションの代わりとなるサーガや、全面的なイベント発行の採用など、やればそこそこ動くであろうことは想像できる
- 一方で、不具合なくものが作れるか?不具合があったときにデバッグできるか?再現性に乏しいデータ整合性の不具合を抑え込むことができるか?など実際に製品開発で採用して大丈夫だろうかという懸念が残った
- 実装技術もさることながら、分析レベルで「粗結合であることが自然」と思えるような、すばらしいモデルが作れないと厳しいかな?