改良を見込んだ設計
改良を見込んだ設計は、継続的なイノベーションのための鍵
いかによくできたアプリケーションであっても、長く使用していくうちに、バグを修正したり、新機能を追加したり、新しいテクノロジを導入したり、既存のシステムのスケーラビリティや回復性を改善したりするために、変更が加えられていきます。 アプリケーションのすべての部分が緊密に結合されていると、システムに変更を加えることが非常に困難になります。 アプリケーションの 1 か所に変更を加えることで、別の部分が機能しなくなったり、コードベース全体に変更の影響が波及する可能性があります。
この問題は、モノリシックなアプリケーションにだけ起こることではありません。 アプリケーションが複数のサービスに分かれていても、結合性が高ければ、システムが固定的になり、機能が故障しやすくなります。 しかし、将来の改良を見込んだ方法でサービスが設計されていれば、イノベーションを導入しながら、継続的に新しい機能を提供していくことができます。
マイクロサービスは、以下の考慮事項の多くに対応しているため、改良に耐える設計を実現するための一般的な手段になりつつあります。
推奨事項
高凝集と疎結合を徹底する。 サービスが高凝集であるというのは、その機能に論理的なまとまりがあるという意味です。 サービスが疎結合であるというのは、1 つのサービスを変更しても、他のサービスを変更する必要が生じないという意味です。 高凝集は通常、1 つの機能を変更すると、他の関連機能を変更する必要があり、関連するすべての機能が 1 つのサービスに存在することを意味します。 あるサービスを更新するために他のサービスも連携的に更新する必要がある場合、それはサービスが高凝集でないことの表れかもしれません。 ドメイン ベース デザイン (DDD) の目的の 1 つは、それらの境界を特定することです。
ドメイン ナレッジをカプセル化する。 クライアントがサービスを利用する際、ドメインのビジネス ルールを実施する責任をクライアントに負わせることは適切ではありません。 代わりに、サービス側で当該のドメイン ナレッジをすべてカプセル化するようにしましょう。 そうしないと、各クライアントでビジネス ルールを実施しなければならなくなり、ドメイン ナレッジがアプリケーションのさまざまな部分に分散することになります。
非同期メッセージングを使用する。 非同期メッセージングは、メッセージ プロデューサーをコンシューマーから分離するための手段です。 プロデューサーは、コンシューマーがメッセージに応答したり、特定のアクションを実行することに依存しません。 パブリッシュ/サブスクライブ アーキテクチャでは、メッセージを消費するユーザーをプロデューサー側で把握できないこともあります。 新しいサービスでは、プロデューサーを変更することなく、メッセージを簡単に消費できます。
ドメイン ナレッジをゲートウェイ内に構築しない。 マイクロサービス アーキテクチャでは、要求のルーティング、プロトコル変換、負荷分散、認証などにゲートウェイが役に立ちます。 ただし、ゲートウェイの使用は、この種のインフラストラクチャ機能だけに制限する必要があります。 依存性が大きくなるのを避けるため、ゲートウェイにはドメイン ナレッジを実装しないようにしてください。
オープン インターフェイスを公開する。 サービス間にカスタム変換レイヤーを作成することは避けてください。 代わりに、サービスでは、API コントラクトが適切に定義された API を公開するようにしましょう。 また、API をバージョン管理して、旧バージョンとの互換性を維持しながら改良を加えていけるようにしましょう。 そうすることで、依存するすべての上位サービスを更新しなくても、サービスを更新できるようになります。 パブリックに公開されるサービスでは、HTTP 経由で RESTful API を公開するようにしましょう。 バックエンド サービスでは、パフォーマンス上の理由で、RPC スタイルのメッセージング プロトコルが使用される場合もあります。
サービス コントラクトを設計し、テストする。 適切に定義された API がサービスで公開されていれば、それらの API に準じた開発やテストが可能になります。 そうすることで、すべての依存サービスに混乱をもたらすことなく、個々 のサービスを開発し、テストできるようになります (当然ながら、統合とロードのテストは実際のサービスに対して実行する必要があります)。
適応度関数を使用する。 適応度関数では、結果を測定して、最適な解決策に近付いているか、さらに遠のいているかを確認します。 適応度関数は、経時的な変化が発生したときにアーキテクチャの特性を保護するために役立ちます。 適応度関数は、アーキテクチャ特性の客観的な整合性評価を提供するメカニズムです。 この評価には、メトリック、単体テスト、カオス エンジニアリングなどのさまざまなメカニズムが含まれることがあります。 たとえばアーキテクトが、ページの読み込み時間が重要な特性であると考えているとします。 その場合、このワークロードには、ページの読み込み時間をテストするための適応度関数を追加して、継続的インテグレーションの一環としてテストを実行する必要があります。
抽象インフラストラクチャをドメイン ロジックと分離する。 ドメイン ロジックをインフラストラクチャ関連の機能 (メッセージングや永続化など) と関係させることは避けてください。 そうしないと、ドメイン ロジックが変更された場合に、インフラストラクチャ レイヤーでも更新が必要になります (逆も同様)。
複数のサービスにまたがる機能を専用のサービスにオフロードする。 たとえば、複数のサービスで要求を認証する必要がある場合には、その機能を専用のサービスに移動するなどしてください。 そうすれば、認証を使用するサービスに手を加えなくても、認証サービスを改良できるようになります (新しい認証フローを追加するなど)。
サービスを個別にデプロイする。 DevOps チームがサービスをデプロイする際、アプリケーション内の他のサービスと切り離して作業できるようにすれば、更新もすばやく安全に行えるようになります。 バグ修正や新機能も、より定期的な間隔で展開できるようになります。 アプリケーションとリリース プロセスのどちらについても、独立した更新をサポートできる設計を行いましょう。