Closed36

「Monolith to Microservices」読書メモ

KIDANI AkitoKIDANI Akito

p12 3つのモノリス:シングルプロセス(下位区分としてモジュール化されたモノリス)、分散モノリス、サードパーティシステム

KIDANI AkitoKIDANI Akito

p15 モノリスをレガシーと同義と捉える人々がいる、これは問題だ。モノリスは選択肢のひとつであり、マイクロサービス同様、全ての場合に正しいわけではない

KIDANI AkitoKIDANI Akito

p17 凝集性=一緒に変化するコードは一緒にある
p17 他方で、結合度が上がると、一緒に変化するコードは増える

KIDANI AkitoKIDANI Akito

p18 「実装の結合」はしばしば見られるが容易に削減できる
p19 良くあるのはデータベース共有だが、サービス間にAPIを立てれば良い
p20 あるいは内部テーブルとは別に公開用テーブルを作っても良い
p21 いずれにせよどの情報を公開するかはユーザーに聞いて決めるべし。内部実装よりも先に。

KIDANI AkitoKIDANI Akito

p22 一時的な結合:同期的なhttp呼び出し、全てのサービスが同時に起動していないとダメ。解決策としてはキャッシュ、メッセージブローカーによる非同期呼び出しがある
p23 デプロイの結合:シングルプロセスのモノリスでよくある。デプロイにはリスクがあるので、デプロイ対象は減らすべき。フィードバックも速く得られる。
p23 デプロイの分割にはマイクロサービスだけではない。Erlangのようにホットデプロイができるものもある

KIDANI AkitoKIDANI Akito

p24 ドメインの結合:サービスに不要な情報を隠すことで結合を減らせる。どのサービスにもクレジットカード情報が必要だろうか?
p26 idベースで連携してあとでAPIを呼び出すくらいなら最初から情報を渡してしまえばよい
p27 あるいはイベント駆動にすることで依存性を逆転できる

KIDANI AkitoKIDANI Akito

p28 マイクロサービスを考える上で重要となるドメイン駆動開発の考え方をまとめておく
p29 集合(aggregates):マイクロサービスがそのライフサイクルを管理するデータのまとまり、ドメイン上の概念(注文、送状、在庫など)。

KIDANI AkitoKIDANI Akito

p31 境界づけられたコンテキスト(bounded context):サービス間の実装の詳細を隠す
p31 このふたつにより、凝集性が高まり、よく定義されたインターフェースが実現できる

KIDANI AkitoKIDANI Akito

Chapter 2 Planning a Migration

p33 技術的でない部分から始めよう
p33 マイクロサービスはゴールではない、マイクロサービスを実現したら勝ちではない:既存システムで実現できないことを実現するためのものだ
p35 3つの重要な問い

  • マイクロサービスによって何を達成したい?
  • マイクロサービス以外の代替手段を考えたか?
  • マイクロサービスへの移行が機能していると判断する基準は?
KIDANI AkitoKIDANI Akito

p35 なんのためにマイクロサービスを選択するか?
p35 ひとつにはチームの自律性を高めるため
p36 それにより権限移譲され仕事が速く終わる
p36 そのためにはマイクロサービスでなく、モジュール化されたモノリスでもよい
p37 もうひとつは、市場への時間を短縮するため
p37 本番環境にアイデアが投入されるまでのボトルネックを確認しよう:そもそもプロダクトオーナーがボトルネックかも?

KIDANI AkitoKIDANI Akito

p37 また、コスト効率のよいスケールを実現するため
p38 モノリスでも水平または垂直スケールによる代替が効果的
p38 また、堅牢なアプリにするため
p39 とはいえこれもロードバランサーやキューによって代替できる
p40 より信頼できるハードウェアやソフトウェアへの投資でも代替可能

KIDANI AkitoKIDANI Akito

p40 ひとつには、開発者の人数を増やすためにマイクロサービスを選択している。代替手段として、モジュール化されたモノリスがある程度有効。単にマイクロサービスにするだけではなくチーム間の自律性が不可欠。
p41 ひとつには、新しい技術を採用するためにマイクロサービスを選択している。代替手段として、動作環境がJVMであれば新しい言語を採用しやすい。より良い結果を顧客に提供できるし、開発者も新しい技術をマスターできてハッピー。

KIDANI AkitoKIDANI Akito

p42 マイクロサービスが悪手となるシーン
p43 - 不明瞭なドメインの場合:サービス境界を間違えると高コスト。最初からマイクロサービスをやるよりは、既存のコードを分解する方がはるかに楽。
p43 - スタートアップの場合:マーケットフィットを得ていない場合は特に悪い。また、マイクロサービスが増えたときの運用コストも考慮すること。
p44 - 顧客が管理するソフトウェアの場合:顧客に同じ運用スキルを期待してはいけない
p45 - ゴールが明確でない場合:マイクロサービスによって何を得たいのか?

KIDANI AkitoKIDANI Akito

p47 ゆえに、何を達成したいのか、目的を組織のメンバーと共有することが重要

KIDANI AkitoKIDANI Akito

p48 John Kotter博士の組織的変化の8ステップ

  • 危機意識を高める
  • 連帯チームを作る
  • ビジョンと戦略を生み出す
  • 変革のビジョンを伝える
  • 従業員に変革の権限を与える
  • 短期的な勝利を実現する
  • 成果を生かしてさらなる変化を生み出す
  • 新しい方法を文化として根付かせる
KIDANI AkitoKIDANI Akito

p48 危機意識を高める:マイクロサービスはゴールではない。何を達成したいのかを共有しよう

KIDANI AkitoKIDANI Akito

p49 連帯チームを作る:信頼が重要だ。小さく素早い勝利を経験していれば、自然とあなたの大きな考えを指示してくれるだろう。
p49 ビジョンと戦略を生み出す:ビジョンは現実的かつ野心的を両立することが重要。特定の戦略に過度に入れ込んではいけない、サンクコストに注意。

KIDANI AkitoKIDANI Akito

p50 変革のビジョンを伝える:小さくはじめよう。オフィスのトイレにビラを貼るとか。ビジョンの共有には対面のコミュニケーションがより効果的。

KIDANI AkitoKIDANI Akito

p51 従業員に変革の権限を与える:障害を取り除こう。具体的な問題を解決するために技術を利用しよう。完璧なマイクロサービスプラットフォームを探して時間を無駄にしないこと。ペインポイントを探して、学んで、改善のために投資しよう。

KIDANI AkitoKIDANI Akito

p52 短期的な勝利を実現する:全社的に拡大するのは、小さな成功を手に入れてからで良い。失敗はつきものだが、そこから素早く学ぶことが重要。
p52 成果を生かしてさらなる変化を生み出す:小さな成功に甘んじることなく、ふりかえりを行い、変化を拡大しよう。違う部署では違うやり方が必要かもしれない。
p52 新しい方法を文化として根付かせる:成功と、失敗の物語を共有しよう。組織内で継続的に共有することが変化をスケールさせ定着させる。

KIDANI AkitoKIDANI Akito

p53 漸進的な変化の重要性:少しずつマイクロサービスについて学ぶことができる、失敗のスコープも小さくて済む。人間は失敗するので、失敗のサイズを小さくする工夫が必要。良いアイデアは小さくはじめよう。

KIDANI AkitoKIDANI Akito

p54 本番環境がすべて:重要な教訓は本番環境でしか学べないことが多い。トラブルシューティング、トレーシング、レイテンシ、連鎖的な事故など。
p54 ジェフ・ベゾスの言葉:可逆的な決定と不可逆的な決定がある、後者は注意深く決定すべきだが、大半の決定は可逆的であり、個人や小さなチームによって素早く行われるべき。
p55 実際には、可逆的と不可逆的のスペクトラムになっている
p55 マイクロサービスについての決定は可逆的と位置付けられることが多い:ロールバックすればよいので。

KIDANI AkitoKIDANI Akito

p56 実験しやすいところからはじめよう:データベース分割に手を出すのではなく、ホワイトボードで思考実験をしてみよう
p56 DDDを活用してサービス境界を決定する
p58 ドメインモデルは、継続的に更新すべきもの
p58 技術者ではないステークホルダーも読んで、「イベントストーミング」によってドメインモデルをともに作ろう
p58 重要なのはエクササイズによってモデルが定義されるだけでなく、理解が共有されることだ
p62 ソフトウェアのアーキテクチャと組織の構造を揃えておくことが重要
p63 運用をするチームではなく、他の(開発)チームが運用をできるようにすることが重要:自律的なチームが、ソフトウェアのデリバリーサイクルにより責任を持つようになる
p64 Spotifyモデルが有名だが、あれは2012年のスナップショットにすぎない。いまのSpotifyがSpotifyモデルを使っているわけではない。ベストな組織構造はコンテキストによって異なるので、他の組織の答えをコピーするのではなく、問いをコピーすべきだ。
p68 スキルに関するThe Guardian紙の開発者たちの事例:自分でスキルを5段階で評価、メンターと共有、自身の目標を設定すべきであって、全員が全員最高レベルを目指すべきではない
p69 チーム全体のスキルレベルをみるのに良い。匿名化して集計すると、個人がチームをどのように助けられるか理解する助けになる。
p70 短期的には、足りないスキルがあれば外からスキルのある人を連れてくるのがよい

KIDANI AkitoKIDANI Akito

p73 サンクコストにとらわれることなく定期的にプロジェクトの進行をチェックし、必要な場合には方向を変えよう:変化は一度きりのものではない

KIDANI AkitoKIDANI Akito

Chapter 3 Splitting the Monolith

p77 モノリスのコードがMVCなどの技術的なパッケージ名に分けられていると、マイクロサービス化のためのビジネスドメイン毎の分割は困難である
p77 Working Effectively with Legacy Code by Michael Feathers (Prentice Hall, 2004)のseam(接ぎ目):Javaならjar、Rubyならgemと、モジュール化されたモノリスへ分けるところから始める
p78 モジュール化された段階で大体の組織の抱える問題は解決される
p78 ビッグバン・リライトはしない、少しずつ書き換えて顧客に提供していく
p79 ストラングラーパターン:新旧のシステムがはじめは同時に存在し、徐々に置き換わっていく
p80 デプロイとリリースは別概念:本番環境にデプロイしても顧客に使われていないならリリースされていない。本番にデプロイしてリリース前に検証しよう
p80 旧システムは生きている:失敗は必ず起きるのでその場合はロールバックしよう
p83 ストラングラーパターンにHTTPリバースプロキシを活用する
p84 プロキシがなければまずはプロキシのみデプロイする:しばらく様子を見てネットワークホップが増えた影響を評価する。レイテンシに問題があれば調査して解決しよう
p85 新しいサービスをデプロイする:まずは全リクエストに501 Not Implementedを返す、徐々に機能追加して何度もデプロイしていく。これはリリースではない、顧客には使われていない状態のまま。
p85 必要な全機能をデプロイしたら、プロキシでリダイレクトさせる:ここでも失敗したらロールバックすればよい
p86 リリースの際はカナリア方式や並行稼働方式を利用できる
p86 nginxのようなプロキシを利用するのがよい
p87 自作するとパフォーマンス問題などがおきる
p87 リクエストのボデイベースのマッピングが必要で、URIパスによるマッピングが利用できない場合でも、nginxのlua拡張などで実装するのがよい
p90 プロキシを利用してSOAPからgRPCへのプロトコル変更も可能

KIDANI AkitoKIDANI Akito

p91 プロキシにプロトコル変換ロジックを埋め込むと、プロキシが複雑化し、デプロイの独立性を妨げるようになる:共有ミドルウェアは機能開発を遅延させる
p91 むしろプロトコル変換ロジックはサービス内部に持たせるのがよい
p92 セントラルプロキシではなく、サービスメッシュ、ローカルプロキシという考え方で、共有ミドルウェアを避ける
p98 機能をマイクロサービスへ移行する場合、動作が変わらぬよう注意せよ。新規機能やバグ修正は移行が完了するまで待つべし。なぜならロールバックが困難になるから。
p98 UI合成パターン:既存モノリスとマイクロサービス をつなぎ合わせる
p99 ページ単位の合成、ウィジェット単位の合成
p100 サーバーサイドでApacheのEdge-Side Includesなどが利用されきたが、最近ではブラウザやネイティブアプリで行われることが多い
p102 モバイルアプリの場合、Apple App StoreやGoogle Play storeへの事前提出が障害となる
p103 この意味で、モバイルアプリはモノリスである:Web Componentsを利用したマイクロフロントエンドと呼ばれる手法が採用されてきている
p104 抽象化によるブランチパターン
p105 既存機能を抽象化するレイヤを作り、既存機能の呼び出し元からその抽象化レイヤを呼び出し、抽象化レイヤを新規機能で実装し、新規機能の実装へと切り替えを行い、既存実装(と抽象化部分)を削除する

KIDANI AkitoKIDANI Akito

p.112 フォールバックとして既存実装を呼び出す抽象化によるブランチ検証パターンもある
p.113 同時実行パターン:抽象化によるブランチパターンのように既存・新規のどちらかだけを呼び出すのではなく、両方呼び出して結果を比較する
p.116 非機能的な側面も同時に検証すべき:ネットワーク越しの呼び出しになるので、タイムアウトや失敗率も要確認
p.116 スパイの利用:通知マイクロサービスと既存の通知ロジックが同時実行されると、通知が2通飛んでしまうので、これを避ける必要がある
p.117 同時実行をサポートするライブラリとしてScientistがある

p.118 デコレーティング・コラボレーター・パターン
p.119 メソッド呼び出しをキャッチするプロキシを利用して、別の処理を挟む

p.120 変更データキャプチャ・パターン:デコレーティングの手法は、メソッドの呼び出しまたは戻り値に、新しいマイクロサービスを呼び出すのに必要な情報がすべて含まれている場合に利用できる、そうでない場合には変更データをキャプチャする手法が必要となる
p.122 実装方法1:DBのトリガーを利用する。Oracle DatabaseはWeb APIを呼び出したり、Javaコードを実行したりできる。しかし、数が多くなるとシステムの全容把握が困難になるデメリットも。
p.123 実装方法2:トランザクションログをポーリングする。しかし、DBによって実装方法が異なるので利用可能なツールも異なる。制限は色々あるが、これがもっともきちんとしたやり方。
p.124 実装方法3:バッチデータコピー。定期的にデータを取得していく。データに更新日時を含める必要があることも。

p.124 この章のまとめ:ひとつの方法ですべての場面を賄うことはできないので、複数の手法を混ぜて利用していることがほとんど。

KIDANI AkitoKIDANI Akito

Chapter 4 Decomposing the Database

p.125 モノリスからマイクロサービスへ移行するなら、データベースを分割する必要がある:移行中のデータ同期、論理的・物理的スキーマ分割、トランザクションの一貫性、結合、レイテンシなどの問題が生まれる

p.125 共有データベース・パターン:スキーマのどの部分を安全に変更できるか理解しづらい
p.126 ビューを利用することで緩和できるが、これも完全な解決方法ではない
p.126 共有データベースは、我々の目指す高凝集な業務機能の全く反対のものである

KIDANI AkitoKIDANI Akito

p.127 共有データベースをマイクロサービスで使用してよい状況は2つ:静的参照データ(例:通貨コードや郵便番号)、もしくはデータベースをエンドポイントとして公開している場合
p.127 理想的には、新しいサービスは独立したスキーマをもつべき
p.128 ※データベース≒論理的に分離されたスキーマ
p.128 他のチームメンバーに、解決方法がわからなくても解決したい問題について話してみよう:いずれ新しいスキルや経験を得て簡単に思えるかもしれない

p.128 データベース・ビュー・パターン
p.131 ビューは元のテーブルから限定的な情報のみを公開することで、情報隠蔽information hidingを実現している
p.131 ビューがいつ更新されるか問題:古いデータを見ている危険性
p.131 ビューの制限:読み取り専用、元のデータベースと同じエンジンでなくてはいけない=デプロイの結合度を高める

p.132 データベース・ラッピングサービス・パターン
p.132 データベースへの依存関係を、サービスへの依存関係に移行する

p.135 サービスのインターフェースとしてのDBパターン
p.135 読み取り専用に設計されたデータベース、レポート用など
p.136 サービス本体のDBとは別に、外部用DBとの変換用エンジンを設ける:やはり古いデータを見ている危険性がある
p.136 変換処理をバッチ処理にすると、実行に時間がかかったり起動しなかったりして問題になる:変更データをキャプチャする仕組みの方がよい

p.137 データの所有権を移動する
p.137 共有データベースのことばかり考えてきたが、問題のデータがどこにあるべきかを考える必要がある

p.138 集約を公開するモノリス・パターン
p.138 マイクロサービス:状態と振る舞いの組み合わせ
p.139 必要なデータの集約をモノリスが公開するとき、将来的なマイクロサービスのサービス境界に近づいている

p.141 データ所有権の変更パターン
p.141 モノリスにあるデータが新しくできたサービスの支配下にあるべき場合
p.141 データのライフサイクルを管理するのが誰か
p.142 外部キー制約やトランザクション境界などの問題に取り組む必要がある

KIDANI AkitoKIDANI Akito

p.143 データ同期
p.144 モノリスのデータベースと新しいマイクロサービスのデータベースの間でどれだけの一貫性が必要か?
p.144 共有データベースは超短期的な手法としてのみ検討すべき(データ移行の一部として):期間が長くなればそれだけ大きな痛みを伴う

p.145 アプリケーションでのデータ同期パターン
p.146 ステップ1:バッチ処理で裏で同期する
p.147 ステップ2:アプリが旧DBからデータを読み書き、新DBにもデータを書き込み
p.147 ステップ3:アプリが旧DBにもデータを書きこみ、新DBからデータを読み書き
p.148 アプリケーションコードを分割する前にDBを分割するときに有効なパターン

p.149 トレーサー書き込みパターン
p.150 アプリケーションでのデータ同期パターンの変形:新DBではなく新サービスにも書き込む
p.151 書き込むデータの種類を徐々に増やしていく
p.152 旧DBからモノリスが読み書きするデータと、新サービスから読み書きするデータが存在する
p.153 データが旧DBと新サービスの間で一貫性がないタイミングが生じる:ある程度結果整合性を受け入れざるをえない
p.153 単純なSQLクエリなどでデータ同期の結果を確認できるようにするのが重要

p.154 Squareの注文データの事例
p.154 食品デリバリーの注文という概念が複数のワークフローから参照されていた:個々のワークフローの変更時に関連するデータやコードが必要になる->機能提供時にチーム間で競合を引き起こす
p.155 配達とレストランで参照する部分だけ注文処理サービスに切り出す:注文処理サービスのデータはもともとの注文データの部分集合を表す
p.156 イベントドリブンなアーキテクチャであればこの移行は多少容易になる

KIDANI AkitoKIDANI Akito

p.158 データベース分割:物理的分割か、論理的分割か
p.159 物理分割と論理分割は達成するゴールが異なる

  • 論理的分割は情報秘匿や独立した変更を容易にしてくれる(一方でSPOFになる可能性も)
  • 物理的分割はシステムの堅牢性を改善し、スループットやレイテンシを改善するためのリソース競合を排除するのに役立つ

p.160 分割するのはデータベースが先か、コードが先か?
p.160 マイクロサービス化の完了の定義:アプリケーションコードが独自のサービスとして動作し、そのサービスが管理するデータが論理的に独立したデータベースに保管されている状態
p.161 データベースが先でもコードが先でも、それぞれメリットデメリットがある

p.161 データベースを先に分割する場合:モノリスから二つのデータベーススキーマにアクセスする
p.161 問題があれば戻しやすいが、短期的なメリットは薄い

p.162 境界づけられたコンテキストごとのリポジトリ・パターン
p.163 テーブル間の外部キー制約など、データベースレベルでの制約が障害となりうる
p.163 ドメイン境界にそってコードを分割することで、将来的なマイクロサービス間のつなぎ目を理解しやすくなる

p.163 境界づけられたコンテキストごとのデータベース・パターン
p.164 コンテキストごとに独立したスキーマを持たせる:それぞれのコンテキストはjarファイルの形態で、モジュール化されたモノリスの好例。あとで分割できるが、必要ないことも多い。
p.165 既存システムの焼き直しでなはく新規サービスを作る場合にオススメ。新規プロダクトやスタートアップでマイクロサービスを実装するのは好ましくない。なぜならドメインの理解が成熟しておらず境界を見誤るから。特にスタートアップはドメインが変化しやすい。

p.165 コードを先に分割する場合
p.166 コードを先に分割すると、どのデータが新しいサービスで必要か理解しやすくなる。デプロイも独立してできるので良い。

p.166 データアクセス層としてのモノリス・パターン
p.166 モノリスにAPIを追加する:モノリスは死んで無用の長物というわけではない
p.167 マイクロサービスのデータの基本:状態と、その状態の繊維を管理するコードをカプセル化する
→ モノリスがデータの状態遷移を管理しているならマイクロサービスが状態にアクセスする際モノリスを経由すべき

p.170 データベースとコードを同時に分割する場合:避けた方が良い

p.170 パフォーマンスやデータの一貫性の影響が気になるなら、スキーマを先に分割する:でなければ、コードを先に分割してデータの所有権に関する理解を深める

p.171 テーブル分割パターン
p.171 同一テーブルの別カラムを別サービスが更新している場合:それぞれのテーブルに分割しやすい
p.172 同じカラムを別サービスが更新している場合:状態管理と状態を結合させる原則からいくと、状態管理のための新しいサービスが必要

p.173 外部キー制約をコードに移すパターン
p.174 考えるべき問題は2つ

  • もう片方のテーブルのデータを結合しなければならないときにどうするか?
  • データの一貫性がなくなるのにどのように対処するか?
    p.175 データの結合をコードで置き換える:データを管理するサービスのAPIを呼び出す
    p.176 速度劣化が問題になるなら一括処理やキャッシュで緩和できる

p.176 データの一貫性:別スキーマなのでそのような制約はなくなる
p.176 データを削除する前にチェックする:外部のサービスへの依存が生まれてしまう、データの利用者が増えれば増えるほど問題になる
p.176 グレースフル・デリート:外部サービスが、データがない場合の取り扱いを考慮する。データ削除時のイベントをサブスクライブしてローカルに保存する、なども可。
p.177 削除しない:ソフトデリート(削除フラグ)を実装して、特定機能では該当データを利用しないようにする

KIDANI AkitoKIDANI Akito

p.178 静的データ共有の事例

p.179 静的参照データの重複パターン
p.179 各サービスが国コードのテーブルを持つ
p.180 データが各サービス内でのみ利用されているなら、サービス間でのデータの一貫性は問題にならない:同じデータを参照する必要性があれば問題になる
p.180 データの重複を受け入れることで、サービス間の結合を回避できているとも取れる

p.181 参照専用テーブルパターン
p.181 共有データベースの一種
p.181 SPOFではあるがjoin句を利用できるメリットも

p.182 静的参照データライブラリパターン
p.182 enumなどの形で実装し、jarファイルなどの形で共有する
p.182 利用言語が異なる場合には採用できない
p.183 全サービスで同じデータを更新する必要性がある場合、デプロイの結合が発生してしまう
p.183 各サービスが異なるバージョンを利用することを許容できる場合には最適

p.184 静的参照データサービスパターン
p.184 新しいサービスを簡単に構築できる場合には有効な選択肢
p.184 そうでない場合には「やり過ぎ」に思われがち
p.185 FaaSに最適(AWS Lambdaとか
p.186 データの一貫性が必要ないなら共有ライブラリで十分、必要なら専用のサービスを立てよう

p.187 トランザクション
p.188 データベースを分割してもACID特性を限定的に利用できる

KIDANI AkitoKIDANI Akito

p.190 2フェーズコミット(問題を解決するどころかさらに混乱をもたらすかも)
p.190 =投票フェーズ+コミットフェーズ
p.191 投票フェーズで、投票を受けたサービスはデータをロックしてコミットできるようにする
p.191 コミットフェーズで変更を行い、ロックを解除する
p.192 ACIDの定義にある、中間状態を見ないという独立性(Isolation)が失われている
p.192 参加するサービスが増える、レイテンシが増えると問題が増加する
p.193 分散トランザクションにはノーと言おう:不可分性(Atomicity)と一貫性(Consistency)が必要ならデータを分散させないこと

p.193 分散トランザクションとは別のアプローチ:サーガ
p.193 関連するステップを独立して実行可能な単位(個々のサービス内のトランザクション)にモデリングする
p.193 いわゆる長期トランザクション(Long lived transaction)
p.194 サーガは不可分性を与えてくれるわけではない:サーガがどの状態にあるか知るための情報は手に入る

p.195 サーガの障害モード
p.195 サーガに障害が発生した場合に、どのようにリカバリするかを考える必要がある
p.195 後方リカバリ:障害を無かったことにして、ロールバックする
p.195 前方リカバリ:処理を継続する
p.195 後方リカバリと前方リカバリを組み合わせることもある

p.196 サーガのロールバック
p.197 補償トランザクション:過去にコミットされたトランザクションを巻き戻す。完全になかったことにはできないので、データは残しつつ、データの意味の上でなかったことにする

p.198 ロールバックを減らすためにステップの順序を再考する:ワークフローをシンプルに保つ

p.199 サーガを実装する:オーケストレーションとコレオグラフィー
p.200 オーケストレーション:中央集権的なオーケストレータとしてのサービスが存在する。障害時の補償アクションも実行する
p.201 ビジネスプロセスが1つのオーケストレータ内に存在するので理解しやすい:一方で、結合が発生してしまっている

p.202 コレオグラフィー:サーガの操作の責務を複数のサービスに分散させる
p.203 サービス間の協調にイベントを利用することが多い:サービス間の結合は少なくなる
p.204 サーガの進行状況が見えにくくなる問題もある
p.204 相関IDを利用して、全イベントの状態を表示するだけの専用サービスを利用して解消できる

KIDANI AkitoKIDANI Akito

p.205 サーガ vs 分散トランザクション
p.205 分散トランザクションは挑戦的であり、特定の場合を除いて避けるもの
p.205 サーガを活用して、ビジネスプロセスをシステムで一番の概念として扱えばメリットも大きい

第5章 成長の痛み
p.207 サービスの数が増えるにつれ問題も増える

p.209 所有権のスケール
p.209 コードの所有権:誰がコードを変更するか。プルリクエストを送らなければいけないケースもあれば、チームに話を通せば直接変更をプッシュできるケースもある。
p.210 開発者の数が増えるにつれて、強い統制が必要になる(誰もがコードを変更できる状態を維持するのは大変)

p.210 破壊的な変更
p.210 マイクロサービスはより大きなシステムの一部
p.210 自分たちのサービスを利用するサービスを破壊しないよう注意が必要
p.211 開発が複数チームで行われる場合に発生しがち
p.212 破壊的な変更をしないために

  • 偶発的な破壊的変更を避ける:テストで検知する
  • 破壊的な変更を避けられないか十分に考える:新旧2つのバージョンをサポートする
  • 破壊的な変更が避けられない場合は移行期間を設ける:2つのバージョンのインスタンスを維持するよりは、2つのバージョンのAPIを1つのインスタンスで動かす方が良い

p.215 サービスを利用するチームが変更するチームと同じなら、同時に変更をデプロイすることも不可能ではない:常用すべきではない
p.215 会社の組織が小さいうちに、マイクロサービス間の破壊的変更を排除する方法を見つけておかないと、より大きな組織への成長は見込めない

p.215 レポーティング
p.215 モノリスの場合データベースもモノリス:レポーティングのためにJOINのSQLを投げる
p.215 マイクロサービスの場合データが分散しており難しくなっている
p.216 レポーティングの問題は、マイクロサービス開発が進んでから明らかになることが多い
p.217 元のスキーマと一致するレポーティング専用のデータベースを設け、各サービスからデータをプッシュすることで解決できる

p.217 監視とトラブルシューティング
p.218 マイクロサービスのアーキテクチャが成長するにつれて、監視とトラブルシューティングの方法も変更する必要がある
p.218 必要なタイミングを知るのは難しい:事前に優先的に取り組んでおくべき
p.218 ログは集約せよ
p.219 マイクロサービスを実装する前にまずログを集約すべし:運用方法を変更する組織的能力の有無が、その後のマイクロサービスの成否の見極めに役立つ

KIDANI AkitoKIDANI Akito

p.219 トレーシング
p.219 APIゲートウェイやサービスメッシュで相関IDを生成するのが一般的
p.220 OSSではJaegerなど

p.221 本番環境でのテスト
p.221 デプロイ前の自動テストが素早くフィードバックを与えてくれる:本番でも同様のフィードバックが必要!
p.221 例:定期的にフェイクの顧客データでサインアッププロセスを最初から最後まで実行する
p.222 可観測性:事前には分からないので、とにかく情報を集めて、アドホックなクエリを実行できるようなツールを選択しよう

p.222 ローカルの開発者体験
p.222 JVMのようなリソースを消費するマイクロサービスをローカルのノートPCで10も20も起動できるだろうか?
p.223 ビルド時間が長くなり開発者はより大きなマシンを要求するようになる
p.223 ソースコードの集合的所有権がある場合、この問題は顕著になる
p.223 他のマシンで実行されているサービスのインスタンスを利用する仕組みが必要
p.224 マイクロサービスが増えても開発者が可能な限り生産的でい続けられるように継続的に投資する必要がある

p.224 実行するものが多すぎる
p.224 デプロイやインスタンスのマネジメントを手動でやるには大変すぎる
p.224 Kubernetesがよく選択される
p.225 しかし、まずはコンテナよりもサーバレス(FaaS)を採用すべき:コンテナに比べて制約はあるものの、運用コストは劇的に削減できる
p.225 みんながやっているから、というだけでKubernetesを採用したり、マイクロサービスアーキテクチャを採用すべきではない:現在のやりかたで問題が出てきたときに考えれば良い

p.226 E2Eテスト
p.226 アプリケーションにより自信を持てるようになる:他方で、対象範囲が広がると時間がかかり、失敗したときに原因を探すのが難しくなる
p.227 機能の自動テストの範囲を制限する:チーム内で管理するサービスにとどめるべき
p.227 CDC(コンシューマ駆動契約)を利用する:Pactなど
p.227 リリースの自動修正や漸進的デリバリ:Spinnaker(Netflixのツール)
p.228 フィードバックサイクルの継続的改善:新しいテストを追加するだけでなく間違ったテストを置き換えたり削除しよう

p.229 全体最適と局所最適
p.229 各サービスで異なるDBを利用することもできるが、組織全体としてそれぞれのライセンスを支払ったりスキルを獲得するのが本当に効率的だろうか
p.229 チームを跨いだコミュニティがあればより早期にこうした問題にきづける
p.230 ソースの集合的所有権を認めている場合には、サービス間でのある程度の一貫性が重要なので、こうした問題を避けやすくなるだろう

KIDANI AkitoKIDANI Akito

p.230 解決策:変更コストが高いほどインパクトも大きくなる
p.231 意思決定の際にチームの境界の外側のひとびとを巻き込むことが重要
p.231 チーム横断的な技術グループがあると良い(トップにCTOなど会社全体に責任を持てる人)
p.231 会社の文化的にコミュニケーションが盛んであれば、よりアドホックな、非公式のプロセスでも良い
p.231 要はバランス:自治権が大きくなれば問題解決方法の一貫性が低くなるが、中央集権的にやると合意を得るのに時間がかかる

p.232 堅牢性と回復性
p.232 ネットワークパケットはロストする、ネットワーク呼び出しはタイムアウトする、マシンは停止しうる
p.232 こうした問題は本番環境の設定でこと起こりやすい:開発環境で起きても見逃されがち
p.232 ネットワーク呼び出しがどのように失敗するか知っているだろうか?失敗したときにどうすべきか知っているだろうか?
p.233 本番環境でおこった問題から学んだことをドキュメントにまとめておこう

p.233 孤児サービス
p.233 会社の誰も所有権や責任を持っていないサービス:どうしていいか分からないし、変更するのが恐ろしい
p.233 そもそもソースコードがどこにあるのか分からない場合もある
p.234 サービスカタログを作って、メタデータで管理しておくと良い:The Financial Timesの事例

p.236 状況は会社、チームによって異なる:本書を読むことで備えることはできる

第6章 おわりに
p.237 合理的な決定を下すための正しい情報を集めよう
p.237 他の人のやり方をそのまま真似してはいけない:自身の問題、コンテキストを考え、選択肢を評価し、実行中の変更にもオープンでいよう
p.237 マイクロサービスや関連する技術やプラクティスを段階的に取り入れよう
p.237 マイクロサービスはすべての人に適しているわけではない

このスクラップは2021/05/16にクローズされました