アーキテクチャ レッスン2

クリーンアーキテクチャとヘキサゴナル

ドメインを中心に据え外部依存を切り離すクリーンアーキテクチャとヘキサゴナルアーキテクチャの考え方を学ぶ

なぜ「外部依存を切り離す」のか

ビジネスロジックのクラスが特定の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

クリーンアーキテクチャにおける依存の方向のルールとして正しいものはどれか?