クリーンアーキテクチャとヘキサゴナル
ドメインを中心に据え外部依存を切り離すクリーンアーキテクチャとヘキサゴナルアーキテクチャの考え方を学ぶ
なぜ「外部依存を切り離す」のか
ビジネスロジックのクラスが特定のDBライブラリやWebフレームワークのAPIに直接依存していると、 そのフレームワークやDBを変更・アップデートするたびにビジネスロジックまで書き換える必要が出てきます。 また、DBに接続しないとビジネスロジックのテストすら実行できなくなってしまいます。
クリーンアーキテクチャとヘキサゴナルアーキテクチャ (ポート&アダプター)は、どちらも「ビジネスロジック(ドメイン)を中心に据え、 フレームワークやDBなどの技術的な詳細を周辺の交換可能な部品として扱う」という同じ思想を、 異なる図で表現したものです。
クリーンアーキテクチャの同心円構造
Robert C. Martinが提唱したクリーンアーキテクチャは、同心円でシステムを表現します。 円の内側に行くほど抽象度が高く、外側に行くほど具体的な技術に近づきます。
+-------------------------------------------------+
| Frameworks & Drivers (Web, DB, UI) |
| +---------------------------------------------+ |
| | Interface Adapters (Controller, Gateway) | |
| | +-------------------------------------------+
| | | Application Business Rules (Use Cases) |||
| | | +---------------------------------------+||
| | | | Enterprise Business Rules (Entities) |||
| | | +---------------------------------------+||
| | +-------------------------------------------+
| +---------------------------------------------+ |
+-------------------------------------------------+
dependency rule: outer -> inner のみ最も内側のEntities(ドメインモデル)は何にも依存せず、外側のUse Casesがそれを利用し、 さらに外側のController/Gatewayが技術的な入出力を仲介し、最外周のフレームワークやDBがそれを実行します。
ヘキサゴナルアーキテクチャ(ポート&アダプター)
Alistair Cockburnが提唱したヘキサゴナルアーキテクチャは、アプリケーションを中心に置き、 外部とのやり取りをすべて「ポート(インターフェース)」と「アダプター(実装)」の組で表現します。
HTTP Adapter --- Port ---+
|
CLI Adapter --- Port ---+--[ Application Core / Domain ]--+--- Port --- DB Adapter
| |
Test Adapter --- Port ---+ +--- Port --- Email Adapter
左側: Application を駆動する側 (driving/primary adapters)
右側: Application が駆動する側 (driven/secondary adapters)擬似コードで表すと、アプリケーション中核はポート(インターフェース)だけを知っています。
// Port(アプリケーション側が定義するインターフェース)
interface OrderRepositoryPort:
findById(id): Order
save(order): void
// Application Core はポートにのみ依存する
class PlaceOrderUseCase:
constructor(repo: OrderRepositoryPort)
execute(input):
order = Order.create(input)
repo.save(order)
// Adapter(具体的な技術を実装する側)
class PostgresOrderRepositoryAdapter implements OrderRepositoryPort:
findById(id): // PostgreSQLへのクエリ
save(order): // PostgreSQLへの書き込み
class InMemoryOrderRepositoryAdapter implements OrderRepositoryPort:
findById(id): // テスト用のインメモリ実装
save(order): // テスト用のインメモリ実装テスト時はInMemoryアダプターに差し替えるだけで、DBなしにユースケースのロジックを検証できます。
適用判断とコスト
どちらのアーキテクチャも「ビジネスロジックを技術の変化から守る」という価値がありますが、 その代償としてインターフェースの定義やアダプターの実装が増え、コード量・ファイル数は増加します。
ドメインロジックが複雑・長期運用される・DBやフレームワークの変更可能性がある
単純なCRUD中心の小規模アプリ、短命なプロトタイプ、ドメインロジックがほぼ存在しない場合
ポイント
- クリーンアーキテクチャは同心円、ヘキサゴナルはポート&アダプターで「外側から内側への依存」を表現する
- ドメイン(ビジネスロジック)は外部技術のインターフェースを知らず、外側の実装を差し替え可能にする
- ドメインの複雑さに見合わない場合は過剰設計になりうるため、プロジェクトの性質に応じて採否を判断する
確認クイズ
1 / 3クリーンアーキテクチャにおける依存の方向のルールとして正しいものはどれか?