体はドクペで出来ている

インフラ、Goの割合が多い技術ブログ

書評 マイクロサービスパターン [実践的システムデザインのためのコード解説]

はじめに

この本はマイクロサービスアーキテクチャーで開発・運用していく中で普遍的に存在する課題とその解決法の選択肢を紹介する書籍です。

book.impress.co.jp

Food to Go, inc. (FTGO)という架空のフードデリバリー会社(既に日本でもお馴染みになっているアレがモデルかと思われます)がモノリシック地獄の苦境に陥っており、これを如何に脱却するか……というストーリー仕立てで解説が始まります。但しストーリーはあくまで背景設定程度であり各章の冒頭で思い出したように触れられる程度で、全体としてはハードな技術書であり然程とっつきやすくはありません。

モノリシックからのアプリケーション分割、分割したサービス間(=プロセス間)の通信方法、トランザクション管理、データの集約方法、テスト、運用、デプロイ等々マイクロサービスを扱うにあたり発生する配慮が必要なポイントが網羅されています。チーム運営の課題等はほぼ触れられず純粋に技術的な内容なのでそちらの深い知見が欲しい人は別の書籍を当たった方がよいでしょう。

また書中で紹介されるコードはほぼ全てJavaであり(原著者がJavaに強い人らしい)Java経験が無い人は雰囲気で読むことになりますが、コードの書き方はあまり本質的なところではないので特に問題は無いかと思います。実際私もJava経験は無く「Goだったらこう書くかなぁ」となんとなく想像しながら斜め読みしましたが特に理解において支障はありませんでした。

特に必須となる前提知識は無いように思いましたが、強いて言うならば基本的なDDDの用語と個人開発で構わないので自分でインフラ構築とアプリケーションコードの開発をしデプロイ、運用した一通りの経験がある方が望ましいでしょう。

読書メモ

Chapert 1 モノリシック地獄からの脱出

  • モノリシック地獄の兆候
    • 複雑性が増して開発ペースが遅い
    • 開発完了からデプロイまでが遅い
    • スケールしにくい
    • 技術スタックが陳腐化している
  • マイクロサービスアーキテクチャーだけでなく、DevOps・小規模で自主性と自律性のあるチームも必要

Chapert 2 サービスへの分割

  • 分割アプローチ
    • Decompose by business capability
    • Decompose by subdomain
  • 神クラスはドメインモデルで分割して除去する

Chapert 3 マイクロサービスアーキテクチャーで使われるプロセス間通信

  • マイクロサービスアーキテクチャーは分散システムでありプロセス間通信の設計が非常に重要
  • 要件に応じて適切なプロセス間通信方式を使い分ける
    • 同期的
      • request / response
    • 非同期的
      • async request / response
      • one-way notification
      • publish / subscribe
      • publish / async response
  • APIファースト設計は必須
  • 同期通信する場合はリクエスト先のトラブルを考慮した実装にする
  • データベースとメッセージングで不整合が起きないよう配慮する
    • Transactional outbox と Polling publisher か Transaction log tailingを活用する

Chapert 4 サーガによるトランザクションの管理

  • 分散トランザクションは使うべきではない
  • Sagaパターンで整合性を維持する
    • システムの状態単位でローカルトランザクションを実行していくシーケンスを構築する
    • 進行不能な状況になった場合は補償トランザクションで取り消し処理を行う
    • ACIDのIsolation(分離性)は失われるが最終的な整合性は担保できる
  • Sagaの実装
    • Choreography
      • Sagaに参加するサービスに次のステップの判断を委ねる
      • 各サービス間はイベントをトリガーにして駆動する
    • Orchestration
      • Sagaを制御するオーケストレーターを別途用意する
      • オーケストレーターはステップ毎に各マイクロサービスに命令を送る
  • Isolationが失われるのでACIDトランザクションでいうところのAnomaryが発生するため対策(Counter measure)が必要
    • Semantic lock
    • Commutative updates
    • Pessimistic view
    • Reread value
    • Version file
    • By value

Chapert 5 マイクロサービスアーキテクチャーにおけるビジネスロジックの設計

  • Transaction scriptパターン
  • Domain modelパターン
    • 状態・動作を併せ持ったモデルでロジックを記述する
    • 複雑な処理向け

Chapert 6 イベントソーシングを使ったビジネスロジックの開発

  • ドメインイベントを使うことでChoreography Sagaを実装する
    • 各イベントはアグリゲートの状態変更を表現する
    • イベント毎に永続化、監査ログ、次のイベントをPublishする
  • イベントのやり取りはイベントストアを介して行う
    • データベースとメッセージブローカーの中間的存在

Chapert 7 マイクロサービスアーキテクチャーでのクエリー実装

  • UIに必要なデータが分散して保管されているため何らかの方法で集約する必要がある
  • API compositionパターン
    • 集約用コンポーネントが各マイクロサービスに問い合わせを行い結合してレスポンスする
    • シンプルで実装しやすい
    • 問い合わせ先が増えるとオーバーヘッドが高く可用性が下がる
    • データ整合性が担保できない可能性がある
  • Command Query Responsibility Segregation (CQRS) パターン
    • 予め各マイクロサービスのドメインイベントをSubscribeしビューを形成しておく
    • 必要なデータ構造を予め用意しておけるのでハイパフォーマンス
    • オリジンに問い合わせの負荷をかけず、且つ読み取りの関心事を分離できる
    • アーキテクチャーは複雑になる

Chapert 8 外部APIパターン

  • 外部にAPIを提供する際は各マイクロサービスのAPIを直接公開するのではなく仲介を用意する
    • カプセル化しバックエンドの変更でリクエスト元に影響を及ぼさないようにする
    • フロントからのリクエスト発行数を減らすため
  • 外部からの問い合わせに最適なプロトコルが内部でも最適とは限らない
  • キャッシュや利用状況のメトリクス等の付加機能を実装しやすい
  • 柔軟な問い合わせが必要な場合はGraphQLを検討する

Chapert 9, 10 マイクロサービスのテスト(前後編)

  • マイクロサービスはコンポーネント疎結合なので一元的にテストを実施するのが難しい
  • ユニットテスト、インテグレーションテストのような低レイヤーのテストを厚くする
    • パターンを網羅しても高速に実行でき安定している
  • 反対にE2E等の高レイヤーのテストは必要最小限に抑える
    • 低速、複雑で他所の変更で壊れやすいため
  • 低レイヤーのテストでは依存先コンポーネント(マイクロサービスやデータベース等)のモックやスタブを用意する

Chapert 11 本番環境に耐えられるサービスの開発

  • 設定可能なサービス
    • push model
      • デプロイ時に環境変数や設定ファイル等を挿入する
      • 既存の仕組みを活用してわかりやすく確実に動作する
      • 変更時は再デプロイが必須になる
    • pull model
      • デプロイ時にインスタンスが自ら設定サーバー等に取得しにいく
      • 設定を確実に一箇所に集約でき実装によっては再デプロイ無しで設定変更可能になる
      • アーキテクチャーが複雑になる
  • 横断的関心事はフレームワークとして用意する
    • microservice chassis
      • エラーレポート、ロギング、ヘルスチェック、サーキットブレーカー等どのサービスでも利用する非機能要件部分をまとめて提供する
    • service mesh
      • ロードバランス、分散トレーシング、ディスカバリー等ネットワークレイヤーでの共通機能を提供する

Chapert 12 マイクロサービスのデプロイ

  • サービスの要件を満たすものの中からなるべく抽象度の高いデプロイ先を検討する
    • Serverless > Container > VM > 言語固有パッケージ
  • service meshを使うとデプロイとリリースを分離しリスクを低減できる

Chapert 13 マイクロサービスへのリファクタリング

  • モノリスから少しずつマイクロサービスへ機能を切り出して行くのがよい
    • strangler application
    • 絞め殺しパターン
  • やりやすい部分ではなく効果的な部分から取り組みビジネスサイドへ価値を認識してもらう
    • 数ヶ月〜数年がかりのプロジェクトになる
  • 始めたらモノリスへの変更は必要最小限に抑える
    • 新機能はサービスとして実装する
  • マイクロサービスに移管したデータをモノリスレプリケーションするとスキーマ変更のリスクを小さくできる
  • モノリスを巻き込んだSagaは実装が困難になるので極力避ける

感想

まず私にとって実務で抱えている課題の解決策になりそうな良い示唆がたくさんあった書籍というのが第一印象です。特にChapter 4, 5, 6では素直に開発しているとすぐに直面する分散システムにおいて複雑怪奇なビジネスロジックトランザクション管理を如何に実装するかという事に焦点が当てられており、非常に示唆に富んでいました。2PCによる分散トランザクションが推奨されない理由、ドメインイベントによるSagaの実装、イベントを使ったビジネスロジックの実装、の辺りは実務で複雑な処理を実装しようとすると起きる問題について解決法の候補になり得る要素なので頭に入れておくとよいでしょう。

全体的には以前書評を書いたオライリーの「マイクロサービスアーキテクチャー」と似たような雰囲気ですが抽象度は少し下がっています。より技術的で実務に近い領域なため重量級ではありますが、脳にかかる負荷は比較的低く比較的スムーズに読み進められたと感じました(冒頭に書いた通り決してとっつきやすいわけではありませんが)。

dokupe.hatenablog.com

一方で具体的なコードについてはあまり参考になりませんでした。前述の通り文中にJavaのサンプルコードがたくさんありますが、Java経験が無いということを差し引いても特段文章の理解を補助するものとは思えません。方法論を実際のコードに落とし込むという観点においては不十分な書籍であると言わざるを得ないと思います。

総括すると、いくつかの難点はありつつも全体としてはマイクロサービスアーキテクチャーの開発で必要になる考慮事項がよくまとまっており種々の課題を解決するための基本的な選択肢をインプットするにはよい書籍だと思いました(今の会社に入る前に読んで理解しておきたかったなぁ)。

蛇足

Kindle版は文章の幅が固定されており端末の幅を無視し(文字ではなく画像をPDFでまとめたような具合)とても読み辛いのでおすすめしません。 物理本にすると今度は500ページ超の重量級書籍なのでそれはそれで読みにくそうな気もします