コンテナ化に適したアーキテクチャ

現在では、図のようなアーキテクチャがモダンとされています。

図1

呼び方は、ヘキサゴナルアーキテクチャやオニオンアーキテクチャClean Architecture (クリーンアーキテクチャ) などさまざまです。

最も外側はインフラストラクチャーであり、独自のシステムの構築では内側 2 つの箱のコード化にフォーカスします。

ユーザインターフェースの分離

最初のトレンドは UI の分離です。iOSAndroid、ブラウザなどの UI はユーザのニーズに合わせた変更頻度の高い領域です。

したがって、特にブラウザで使用する UI は JavaScript フレームワークの進化もあり、現在では静的コンテンツ (Markups: html、cssJavaScript、その他画像等) としてストレージに配置することが多くなりました。

このレイヤーは伝統的にはプレゼンテーション層と呼ばれ、ビジネスドメインのロジックを含まないとされてきましたが、現在ではそうとも言い切れないと考えています。とはいえ、この記事ではこれについてさらに言及することはやめておきます。

モノリスアプリケーション

アプリケーションには、これまで JSP や Thymeleaf といったテンプレートを使っていたプレゼンテーション層のない、主に API を処理する領域のみが残されます。

この時点のアプリケーションをモノリスアプリケーションと呼びたいと思います。

ところで、スクラッチから作成されたアプリケーションでない場合、実際の多くはモダンなアーキテクチャに描かれているようにはなっていません。

一番外側にあるべきデータベースが、中央に存在していることが非常に多くあります。これをそのまま、後で述べるコンテナに適した分散アプリケーションに進化させると、悪名高い「デススターアーキテクチャ」に向かうことになります (May the Force be with you)。

最初にデータベースとの密結合を解消して、外側に移動してください。

ドメイン駆動設計 (Domain-driven Design)

2003 年に出版された「Domain-Driven Design (日本語版: エリック・エヴァンスのドメイン駆動設計)」は 20 年近く経つ今でも輝きを保っています。ただし、当時のコンピューティング環境から進化した結果、そのまま適用するのが適切ではない部分もあるので、そこは注意深く確認する必要があります。

最初の図の中央には、「Use Case」と「Entity」と書かれた箱があります。ここがドメイン駆動設計に書かれているドメインロジックをコード化する領域です。

コンテナを利用するアプリケーションは、とにかくここに集中してください。

横断的関心事の処理

Designing Distributed Systems (日本語版: 分散システムデザインパターン)」のサイドカーパターンはコンテナアプリケーションにとって重要なデザインパターンです。

サイドカーのプロキシとして利用されることの多い Envoy proxy などでドメインロジック以外の処理を分離することができます。

認証 (Authentication)

iOSAndroid、ブラウザから使用される API の認証は OpenID Connect 標準を利用することが増えています。

例えば、Amazon Cognito Userpool のような認証システムから発行された ID Token を使った認証は Envoy を利用して図のようなシーケンスで処理することができます。

図2

サイドカーに Envoy proxy を利用する、この認証の詳細は別の記事でフォローしたいと思います (Envoy を使用して ID Token (OIDC) を検証する)。

認可 (Authorization)

上の認証に追加して、認可も処理することができます。認可には、Open Policy Agent を利用すると便利です。図のようなシーケンスで処理できます。

図3

サイドカーに Envoy proxy と OPA を利用する、この認可の詳細は別の記事でフォローしたいと思います (Envoy と Open Policy Agent を使用した認可)。

Gateway: Publish/Subscribe

Enterprise Integration Patterns」には、エンタープライズの統合ソリューションに活用できる 65 のパターンが示されています。

IoT のような大量のイベントを処理するためにイベント駆動への関心も高まっています。

イベント駆動では、MQTT や Kafka のような Publish-Subscribe Channel のパターンを使用します。

Envoy proxy には実験的な Kafka サポートがあります。Dapr という同じくサイドカーパターンを利用する分散アプリケーションランタイムには、安定版としてのサポートがあります。

これらを使用して、サブスクライブしたイベントをアプリケーションコンテナに REST や gRPC 呼び出しに変換するといったことが可能となり、アプリケーションのコードはそれが HTTP でルーティングされたものなのか、Kafka 等のイベントがルーティングされたものなのかを気にしなくていいことになります。

Dapr の詳細についてはドキュメントを確認してください。

横断的関心事のまとめ

Envoy proxy では他に可観測性、サービス間認証 (mTLS)、暗号化、A/B テストやカナリアのためのルーティング等を処理することができます。さらに機能拡張も続けられていて、分散アプリケーションパターンを利用することで、横断的関心事をコード化の対象外とすることができます。

分散アプリケーション

長い道のりを経て、アプリケーションのコードはドメインロジックを実行するのに必要最低限のものになりました。Kubernetes 関連のドキュメントを見ると、ドメインロジックとデータベース操作を異なるコンテナに分割した例まで登場します。ここまで行くと、ドメインロジックを実行するアプリケーションからはデータベースを操作するライブラリすらも含まないイメージとなります。

さて、アプリケーションをコンテナ化するとどのような利点が得られるのでしょうか。

  1. 冗長化が容易で、可用性や信頼性を高めることができる

適切に分割されたコンテナ化された分散アプリケーションであれば、さらに次のような利点が得られます。

  1. 高速な起動
  2. スケールアウト・インが容易で、リクエストが一時的に増大した場合でも並列度を高めることができる
  3. 機能追加、変更、削除がカナリアFeature Toggles、サーキットブレーカー等により容易となり保守コストの低減に寄与できる

課題は、適切な境界線をどう引くかです。ですが、ここまでの過程で最大の敵であるデータベースをドメインロジックから外に追いやっていれば、それほど心配する必要はないはずです。

デプロイ単位やスケールアウト・インの実行を必要とする理由が異なるブロックを見つけることがポイントだと考えています。

参考

サンプルコード

図書

オンライン記事