🐝

精読「マイクロサービスアーキテクチャ 第2版」(第三部 人 - 第16章 進化的アーキテクト)

に公開


マイクロサービスアーキテクチャ 第2版
マイクロサービスの設計、実装、運用に必要なベストプラクティスや最新技術を解説した、実践的なガイドブックです。これを読めば、マイクロサービスに関してそれっぽい会話もできますよ。

関連記事

マイクロサービスには多くの選択肢があり、それに応じた決定をする必要がある。例えば、どの技術を使うか、チームごとに異なるプログラミングイディオムを採用すべきか、サービスを分割または統合すべきかといった問題。こうした決定をどのように行うべきか、マイクロサービスアーキテクチャがもたらす流動的な環境では、アーキテクトの役割も変わる。本章では、アーキテクトの役割に関する見解を示し、象牙の塔への最終的なアプローチを提案する。

「名前が何だと言うの」

ソフトウェアアーキテクトの役割について。アーキテクトはシステムの技術的構想を統合し、顧客が必要とするソフトウェアを提供するための責任を負う。しかし、アーキテクトの役割は非常に曖昧であり、企業内での理解が不足していることが多い。特に、ソフトウェア開発は他の業界と比べて歴史が浅く、アーキテクトという職業の定義も確立していない。

アーキテクトは、システムの品質や労働環境、組織の変化に直接影響を与えるが、その重要性が広く理解されていないことが問題。建築家と異なり、ソフトウェアアーキテクトは予測不可能な未来を考慮し、システムの変更に柔軟に対応できることが求められる。ソフトウェア開発は、建築と異なり、変更が可能であり、柔軟性が高いことが特徴。このため、ソフトウェアアーキテクチャには、建築環境のアーキテクチャとは異なる考慮が必要であり、さらに明確に定義する必要がある。

ソフトウェアアーキテクチャとは

ソフトウェアアーキテクチャは、システムをどのように構成し、コンポーネント間がどのように対話するかを示す共通理解。Ralph Johnsonの定義に基づくと、「アーキテクチャは重要なことに関することであり、システムの設計や開発において他の部分とは異なる重要な領域を示す」。さらに、Martin Fowlerの見解によると、アーキテクチャは「変更するのが難しいもの」とも定義され、変更を可能にする柔軟性も重要。

変更を可能にする

シーグラムビルの設計は、内部構造壁をなくし、設備を吊り天井やダクトで配置することで、フロアの空間を柔軟に再構成できる特徴がある。この考え方は、マイクロサービスアーキテクチャにも似ており、システムを小さな独立したサービスに分割することで、将来の変更や拡張に柔軟に対応できる。

進化するアーキテクト像

ソフトウェアアーキテクトは、完璧な最終製品を作るのではなく、進化し続けるシステムを構築することに注力すべき。アーキテクトは都市計画家のように、大まかな方向性を定め、詳細な実装に過剰に関与することなく、変化を受け入れ、適応する役割を担う。

システム境界の定義

アーキテクトがシステムの「ゾーン」または「サービス境界」に関する責任について。ゾーンは、マイクロサービスの大まかなグループであり、アーキテクトはその内部で起こることよりも、異なるゾーン間でのインタラクションや全体的な正常性に注力すべき。各ゾーン内では、担当チームが自由に変更を行えるスペースを提供することで、システム全体を壊すことなく変更できる空間を確保する。具体的には、マイクロサービス間での通信方法を維持しつつ、各サービスは自立して進化できるように設計されている。さらに、チームが異なる技術スタックやデータストアを選ぶことが許容され、各ゾーン内での柔軟性が保たれる。しかし、異なる技術間での統合には慎重さが必要で、複数の通信スタイルのサポートが複雑さを増すことがある。

アーキテクチャの成功は、ユーザーのニーズに応じた変更を可能にすることだけでなく、開発者が作業しやすい環境を作ることにもつながる。

社会的構成概念

アーキテクチャは単なる計画ではなく、実際に進行中に形成されるもので、現実に適応するためには変更が必要。成功するアーキテクチャの実現には、チーム全体の協力と、技術的判断が関わる社会的側面が重要。

居住性

「居住性」という概念は、ソフトウェア開発においても重要で、システムやコードが開発者にとって快適で理解しやすいものであるべきだという考え。アーキテクトは、自らが作成した環境が作業しやすいものであるように設計し、チームと一緒に作業をすることでその環境の居住性を高める必要がある。ペアプログラミングやアンサンブルプログラミングに参加することで、アーキテクトはチームの作業に影響を与えずに共同作業の重要性を理解し、システムの構築を効果的に支援する。

原則に基づいたアプローチ

システム設計における判断はトレードオフであり、マイクロサービスアーキテクチャには多くのトレードオフが含まれる。例えば、スケーリングに優れた経験が少ないプラットフォームを選ぶか、複数の技術スタックを導入するかといった選択。最も簡単な判断は、完全に情報が揃っている場合に下すものだが、不十分な情報で判断しなければならない場合には、フレーミングが有効。フレーミングとは、目標に基づいて判断を導くための原則やプラクティスを定義すること。

戦略的目標

アーキテクトの役割は非常に困難だが、通常、戦略的目標を決定する責任はない。戦略的目標は、会社が目指す方向性や顧客満足を追求する方法を示すもので、技術的な内容を含まないこともある。例えば、「東南アジアに展開し、新しい市場を開拓する」や「顧客がセルフサービスで実現できるようにする」といった目標が挙げられる。重要なのは、技術がこの戦略的目標に沿うように調整すること。そのため、技術的な構想を定義する場合、ビジネス部門とのコミュニケーションや協力が不可欠。

原則

原則は、より大きな目標に一致させるために作成された規則で、変更されることもある。例えば、戦略的目標が新機能の市場投入時間の短縮であれば、デリバリチームがソフトウェアのライフサイクルを完全に制御する原則を定義するかもしれない。また、国外での提供拡大が目標の場合、システムを現地でデプロイできるようにするため、移植可能にする原則が必要。しかし、原則は多すぎない方が良いとされている。10個未満で、覚えやすく、重複や矛盾を避けることが重要。HerokuのTwelve Factorsのように、適切に機能するための設計原則を定めることが有用だが、制約と原則を明確に区別することも重要です。

プラクティス

プラクティスは、原則を確実に実行するための方法であり、具体的で実用的な指針を提供する。これらは、技術的な性質が強く、開発者が理解できるような低水準の指針であるべき。例えば、コーディング指針やログデータの集中保存、HTTP/RESTの標準的な統合形式の使用などが含まれる。プラクティスは技術固有であり、原則よりも頻繁に変更されることがある。また、組織の制約を反映し、実際の選択(例えば、クラウドプラットフォームとしてAzureを選ぶ)もプラクティスに組み込まれる。プラクティスは原則を支えるものであり、デリバリチームがフルライフサイクルを制御する原則を支えるために、マイクロサービスを単独のAWSアカウントにデプロイするなどの実行方法が実例として挙げられる。

原則とプラクティスの結合

原則とプラクティスは密接に関連していますが異なる。原則はシステムの進化方法を示し、プラクティスはその実行方法。重要なのは、システムの進化方法を示す包括的な考え方を持ち、その実装方法が理解できるような詳細を把握していること。小規模チームでは両者を結合しても問題ないが、大規模組織では異なる技術に合わせたプラクティスを持つことがある。

実世界の例

プラクティスは変更されるが、原則はほぼ不変であること。原則とプラクティスを1枚の用紙にまとめて共有することが便利で、プラクティスを実装するコードやドキュメントも役立つ。プラットフォームは、プラクティスに従いやすく、変更に対応できるべき。

進化的アーキテクチャへの指針

進化的アーキテクチャにおいて、アーキテクチャが静的ではなく、進化し続けることを確認するためには、アーキテクチャの「適応度」(fitness)を測定することが重要。適応度関数を使用して、システムの重要な特性が維持されているかを評価し、適切な方向に進んでいるかを判断する。例えば、サービスのパフォーマンス要件(100ミリ秒以下のレスポンス)を確認するために、テスト環境で適応度関数を実装し、実際のデータを収集する。このようなデータは、アーキテクチャが正しい方向に進んでいるか、どこで介入が必要かを把握するために役立つ。

ストリームアラインド組織でのアーキテクチャ

『Team Topologies』の16章では、ストリームアラインドチームとイネイブリングチームの役割が説明されている。アーキテクトは、複数のチームにまたがって技術的な方向性を示し、全体像を支援する役割を持つ。理想的には、少数の専任アーキテクトがイネイブリングチームに配置され、テックリードが各チームに加わることで、技術的な合意と自由な情報交換が促進される。アーキテクトは、グループの判断に従いつつ、長期的な戦略を考慮した意思決定を行うことが求められる。

チームの構築

システムの技術的構想を実現するためには、技術的判断だけでなく、チームの成長と積極的な参加が不可欠である。特にマイクロサービスアーキテクチャでは、開発者が独立したサービスを「所有」し、その責任を担うことが重要。このプロセスは開発者のキャリア成長を促進し、チーム全体の負荷を軽減する方法として有効。優れたソフトウェアは、優れた人々によって作られるという信念に基づき、技術だけでなく人材の成長にも注力することが求められている。

必要な標準

マイクロサービス間の可変性と標準プラクティスのバランスを取ることが重要。特に、システム全体を管理しやすくし、1つのサービスの不具合がシステム全体に影響を与えないようにするために、どのような機能が必要かを考える必要がある。また、優れたサービスは一貫した振る舞いを持ち、個々のサービスが持つべき明確な特性を定義することが、バランスを保つために不可欠。全体像を見失うことなく、個々のマイクロサービスの自律性を最適化することが求められている。

監視

マイクロサービスの監視システムの正常性を把握するために、サービス横断的でわかりやすいビューを作成することが重要。個々のマイクロサービスの正常性を知ることは有用だが、より広範な問題を診断したり、動向を把握するためには、システム全体を俯瞰するビューが必要。そのためには、すべてのマイクロサービスで同じ方法で正常性や監視関連メトリックを出力し、標準化することが推奨されている。データは中央に送信されるべきで、監視システムの変更を避けるようにする。ロギングも同様に、同じカテゴリに分類し、1か所に集めるべき。

インターフェース

インタフェースの定義について、統一された技術を使用することが推奨されている。複数の異なる統合形式があると、システムが複雑になり、管理が難しくなります。したがって、標準化されたインタフェース(例:1つまたは2つ)を選ぶことが重要。例えば、HTTP/RESTを選択する場合、使用する動詞や名詞、リソースの変更やエンドポイントのバージョン管理についての取り決めも、統一的なルールを設ける必要がある。

アーキテクチャ上の安全性

不適切な振る舞いをするマイクロサービスがシステム全体に悪影響を与えないように、各マイクロサービスは異常な下流の呼び出しに対して適切に対処する必要がある。これには、サーキットブレーカーなどのパターンを導入し、サービス間通信のルールを守ることが重要。また、HTTPレスポンスコードを適切に処理し、エラーを早期に検出できるようにすることで、システムの脆弱性を防ぐ。規則を守らないと、最終的にシステムが脆弱になってしまうという警告。

ガバナンスと舗装道路

ガバナンスとアーキテクトの役割について。ガバナンスは、企業の目標達成のために、利害関係者のニーズを評価し、意思決定を通じて方向性を設定し、パフォーマンスや進捗を監視すること。アーキテクトは、技術的構想が実現されるように、ガバナンスを支援し、必要に応じて構想を進化させる。ガバナンスは、チームで協力し、作業を共有することが重要で、公式および非公式な場で議論し、必要な変更を加える。アーキテクトの役割は、構想を明確にし、実行しやすい方法にすること。舗装道路の例では、アーキテクトはプラットフォームの要件を具体化し、チームが正しいことを行いやすくするための支援を行う。

見本

ドキュメントとコードの重要性について。ドキュメントは価値があり、開発者にとって有用だが、開発者はコードを好む。なぜなら、コードは実行して確認できるから。推奨する標準やベストプラクティスがある場合、開発者に見本を示すことが便利で、システムの優れた部分を模倣することで、大きなミスを避けることができる。理想的には、見本は単なる「完璧な例」ではなく、システム内で適切に機能する実際のマイクロサービスであるべき。見本を実際に使用することで、原則を理にかなったものにすることができる。

カスタマイズされたマイクロサービステンプレート

少ない労力で全開発者がほとんどの指針に従い、必要なコードがすぐに使える状態で提供されることで、開発効率が大きく向上する可能性がある。Spring Bootのようなフレームワークを使って、簡単にマイクロサービスを起動し、組織用のテンプレートを標準化することが考えられる。これにより、サーキットブレーカーやJWT認証など、共通機能を統一的に管理できる。

一方で、カスタマイズされたテンプレートを強制することによって、開発者の自由度や士気が損なわれる可能性もある。再利用性を促進するためにフレームワークが過度に巨大化し、システムの結合度が高くなりすぎる危険がある。さらに、テンプレートが必須でなく、使いやすさを最優先することが重要です。もしフレームワークへの変更を強制するのであれば、開発者がその改善に貢献できるような社内オープンソースモデルを採用することが有効。

コードの再利用がもたらすリスク、特にマイクロサービス間の結合を招く可能性があるため、慎重に考慮する必要がある。ある組織では、マイクロサービステンプレートのコードを手動でコピーして使用しており、アップグレードの手間をかけても、結合のリスクを避けることを優先している。

大規模での舗装道路

大規模な組織では、共通のマイクロサービステンプレートやフレームワークを使うことで、新規マイクロサービスの作成を効率化できる。例えば、NetflixやMonzoは技術スタックを標準化しており、共通のツールを利用している。しかし、異なる技術スタックを採用する場合、対応するテンプレートが必要となり、選択肢に制約を加える可能性がある。Netflixでは、フォールトトレランスのためのクライアントライブラリやツールを提供し、新しい技術スタックの導入には追加作業が必要。サービスメッシュを使用することで、異なるプログラミング言語で書かれたマイクロサービス間の振る舞いの一貫性が向上し、テンプレートの負担が軽減される。

技術的負債

技術的負債は、短期的な利点を追求する結果として発生し、長期的には継続的なコストがかかる。負債は、近道を取ることだけでなく、システム設計の変更に対応しないことでも生じる。アーキテクトは全体像を見て、負債のバランスを理解し、どこで関与するかを判断することが重要。組織によっては、負債の追跡や返済方法をチームに委ねる場合もあれば、定期的に見直しを行う負債ログを維持することが求められる場合もある。

例外処理

例外処理は、システムが原則から逸脱する場合に重要。逸脱が発生した場合、その決定を記録しておき、将来的に参照できるようにする。十分な例外が集まれば、その経験を反映させて原則やプラクティスを変更することが意味を持つ場合がある。例えば、特定のストレージに関するプラクティスを変更するケース。開発チームの自律性が高ければ、例外の追跡は不要な場合もあるが、自由度が低い組織では例外の追跡が不可欠な場合もある。マイクロサービスは、自由度を与え、目の前の問題に焦点を当てるために有効だが、制約の多い組織には向かないこともある。

まとめ

本章のまとめとして、進化的アーキテクトの主な責務は以下の通り

  • 構想: 顧客や組織の要件を満たす技術的構想を明確に伝える。
  • 共感: 顧客や同僚への影響を理解する。
  • 協調: 構想の定義、改良、実行を支援するため、仲間や同僚と関わる。
  • 適応性: 顧客や組織の要求に応じて技術的構想を変更する。
  • 自律性: チームに標準化と自律性のバランスを見出す。
  • ガバナンス: 実装されているシステムを技術的構想に合わせ、正しいことをしやすくする。

進化的アーキテクトは、これらのバランスを取る行為であることを理解し、柔軟な対応が求められる。考え方を硬直させず、変化に適応することが重要。特にマイクロサービスでは、さらに多くの判断が必要となる。

参考

Discussion