モジュールごとにセキュリティレベルを変えることも可能
このアプローチはモジュラーモノリスアーキテクチャの利点を最大限に活かす、非常に優れた設計パターンです。
モノリスでありながらも、ドメインごとに異なるセキュリティ要件(非機能要件)を適用したい場合、各ドメインモジュール内に、そのモジュール専用の 「アスペクトモジュール(またはアスペクトパッケージ)」 を配置するのは、まさに理想的な解決策と言えます。
モジュラーモノリスにおける実装パターン
仮に、決済を扱う「ペイメントドメイン」と、商品情報を扱う「カタログドメイン」の2つが同じモノリス内に存在するとします。
・ペイメントドメイン:高いセキュリティレベルが要求される。
(例えば多要素認証(MFA)が必須)
・カタログドメイン:基本的な認証のみで良い。
アプリケーションの構造
この場合、アプリケーションの構造は以下のようになります。
MonolithicApp/
|
├── 📦 PaymentModule/
| ├── Application/ (支払い実行サービスなど)
| ├── Domain/ (支払いのドメインモデル)
| ├── Infrastructure/
| └── 🛡️ SecurityAspects/ <-- ペイメントドメイン専用のアスペクト
| └── MfaCheckAspect.java
|
├── 📦 CatalogModule/
| ├── Application/ (商品検索サービスなど)
| ├── Domain/ (商品のドメインモデル)
| ├── Infrastructure/
| └── 🛡️ SecurityAspects/ <-- カタログドメイン専用のアスペクト
| └── BasicSessionCheckAspect.java
|
└── SharedKernel/ (共通のライブラリなど)
仕組み
アスペクト指向プログラミング(AOP)の 「ポイントカット」 という機能を利用して、
セキュリティロジックが適用される範囲を各モジュールのパッケージ内に限定します。
(下図の緑の部分)
1. ペイメントドメインのアスペクト (MfaCheckAspect.java)
ポイントカット
com.example.payment.** パッケージ内の特定のメソッド(例: Application層の全てのpublicメソッド)を対象に設定します。
アドバイス
メソッド実行前に、「MFAが完了しているか」をチェックするロジックを割り込ませます(Inject)。
2. カタログドメインのアスペクト(BasicSessionCheckAspect.java)
ポイントカット
com.example.catalog.** パッケージ内のメソッドを対象に設定します。
アドバイス
メソッド実行前に、「有効なセッションが存在するか」という基本的なチェックのみを割り込ませます。
この仕組みにより、同じモノリシックなアプリケーション内で実行されているにもかかわらず、ペイメントドメインの処理にだけMFAチェックが適用され、カタログドメインには影響を与えないという、きめ細やかな制御が可能になります。
この設計のメリット
この設計には、モジュラーモノリスの理念に沿った多くの利点があります。
1. ドメイン固有のセキュリティ
ドメインのビジネス的なリスクレベルに応じて、最適なセキュリティ強度を適用できます。
「ワンサイズ・フィッツ・オール」の問題を回避できます。
2. 高い凝集性
ペイメントドメインに関わるセキュリティコードは、ペイメントモジュール内に存在します。
関心事が一箇所にまとまっているため、モジュールの自己完結性が高まり、理解しやすくなります。
3. ビジネスロジックの純粋性
PaymentServiceのようなビジネスロジックのクラスに、セキュリティチェックのコードが混入しません。
関心が分離されることで、ロジックは本来の責務に集中でき、コードがクリーンに保たれます。
4. 管理すべきコンポーネントが1つでいい
物理コンポーネントとしては1つなので、デプロイメントコストとかの運用の複雑さやコストが抑えられます。
5. マイクロサービス化への布石
将来、ペイメントドメインをマイクロサービスとして切り出す際、
ドメイン固有のセキュリティロジック(MfaCheckAspect)も一緒に持っていけるため、
切り出し作業が格段に容易になります。
このアプローチのデメリット
一方で、この優れたパターンにもトレードオフは存在します。採用にあたっては、以下の至らない点も考慮する必要があります。
また、インフラストラクチャ層に目を向けると、物理的な境界が一つであるために新たなデメリットが浮かび上がってきます。
1. AOP自体の複雑さと学習コスト
アスペクト指向は、処理の流れをコードの見た目通りではなく、実行時に動的にビジネスロジックとデコレーションします。
これは、処理の裏側で何が起きているかが分かりにくくなります。
新しい開発者が参画した際、ビジネスロジックを読んだだけではセキュリティチェックの存在に気づけず、デバッグや仕様変更時に混乱を招く可能性があります。
2. セキュリティコード重複の可能性
各モジュールが独自のセキュリティアスペクトを持つため、複数のモジュールで共通するような基本的なセキュリティチェック(例: BasicSessionCheckAspect)を、それぞれのモジュールにコピー&ペーストしてしまう可能性があります。
これはDRY原則に反し、将来その共通チェックに修正が必要になった際に、全てのコピーを修正し忘れるというバグの原因になり得ます。
3. 脆弱なポイントカット定義
ポイントカットは、パッケージ名やメソッド名といった「文字列」で定義されることが多く、リファクタリングに弱いという性質を持ちます。
例えば、開発者がcom.example.payment.applicationパッケージの名前をcom.example.payment.usecaseに変更した場合、ポイントカットの定義を修正し忘れると、
MFAチェックが誰にも気づかれないまま、無効化されてしまうという重大なリスクがあります。
これを防ぐには、アーキテクチャのルールを強制する適応度関数が別途必要になります。
4. セキュリティガバナンスの難しさ
このアプローチは各モジュールの自律性を高めますが、裏を返せば、セキュリティ適用の徹底を各チームの規律に委ねることになります。
「新しいモジュールを作ったが、開発者がセキュリティアスペクトを適用し忘れた」というヒューマンエラーが発生する余地があります。
中央集権モデルが持つ「強制力」が弱まるため、コードレビューや静的解析ツールによるチェック体制の強化が不可欠です。
🛡️ 「最も高い基準」に合わせるコスト
モジュラーモノリスは、論理的には分割されていますが、物理的には 「単一のデプロイメントユニット」 です。
つまり、同じプロセスで動き、同じサーバーに乗り、同じデータベースインスタンスに接続します。
これは、インフラ層のセキュリティにおいては 「最も高い基準」の原則 が適用されることを意味します。システム全体のセキュリティレベルは、システム内で最も機密性の高いドメイン(この場合はペイメントドメイン)の要件に合わせる必要があります。
その結果、「過剰な防御コスト」 が、セキュリティ要求の低いカタログドメインにかかってしまうのです。
過剰な防御コストの具体例
金銭的コスト
①. データベース
ペイメントドメインの要件で「保存データの暗号化」や「詳細なアクセス監査ログ」が必須な高価なDBプランを選択する必要があります。
カタログドメインのデータも、この高価なプランの上で管理されることになります。
②. サーバー/ネットワーク
ペイメントドメインを守るために、高性能なWAFや厳格な設定のファイアウォールを導入せざるを得ないです。
カタログドメインへのトラフィックも、この高コストなインフラを経由します。
パフォーマンスコスト
厳格なファイアウォールルールやWAFによる詳細なパケットインスペクションは、
ペイメントドメインだけでなく、カタログドメインのレスポンスタイムにもレイテンシーとして影響を与える可能性があります。
カタログドメインの方は、基本的な承認のみでいいのにもかかわらず、これによってUX低下になります。
運用コスト
高セキュリティ環境の維持には、頻繁な脆弱性スキャン、厳格なパッチ管理、監査対応など、高い運用負荷がかかります。この負荷は、モノリス全体に対して発生します。
今回は、2つしかモジュールがない例だからいいですが、下図のようになっていたら、
だいぶ過剰コスト投下することになります。