LESSON 25分

ストーリー

高橋アーキテクト
レイヤードアーキテクチャの限界は理解できたかな?
あなた
はい。ビジネスロジックがデータベースに依存してしまうのが問題ですよね
高橋アーキテクト
そう。ではここで、アーキテクチャの最も重要な2つの原則を学ぼう。“関心の分離”と”依存性の方向”だ。この2つを理解すれば、なぜヘキサゴナルアーキテクチャやクリーンアーキテクチャが生まれたのかが自然にわかる

関心の分離(Separation of Concerns)

関心の分離とは、システムの各部分が1つの責任だけを持つように分割することです。SOLID原則の「単一責任の原則」をシステム全体に拡張した考え方です。

3つの関心領域

┌──────────────────────────────────────────────┐
│                                              │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐   │
│  │ ドメイン  │  │アプリケー│  │インフラ   │   │
│  │          │  │ション    │  │ストラクチャ│   │
│  │ ビジネス  │  │          │  │          │   │
│  │ ルール   │  │ ユースケース│  │ DB, API  │   │
│  │ 最も安定  │  │ 仲介役   │  │ 最も不安定 │   │
│  └──────────┘  └──────────┘  └──────────┘   │
│                                              │
└──────────────────────────────────────────────┘
関心領域役割変更頻度
ドメインビジネスルールとエンティティ低い(ビジネスが変わらない限り不変)
アプリケーションユースケースの実行フロー中程度(機能追加時に変更)
インフラストラクチャDB、API、UI、フレームワーク高い(技術の入れ替え、DB変更など)
// ドメイン: 注文のビジネスルール(最も安定)
class Order {
  cancel(): void {
    if (this.status !== OrderStatus.PENDING) {
      throw new Error('確定済みの注文はキャンセルできません');
    }
    this.status = OrderStatus.CANCELLED;
  }
}

// アプリケーション: ユースケースの流れ(中程度の安定性)
class CancelOrderUseCase {
  async execute(orderId: string): Promise<void> {
    const order = await this.orderRepo.findById(orderId);
    order.cancel();
    await this.orderRepo.save(order);
    await this.notifier.notify(order.userId, 'キャンセル完了');
  }
}

// インフラ: 具体的な技術実装(最も不安定)
class PostgresOrderRepository {
  async findById(id: string): Promise<Order> {
    const row = await prisma.order.findUnique({ where: { id } });
    return Order.fromPersistence(row);
  }
}

依存性の方向

安定依存の原則

安定しているものに依存し、不安定なものには依存しない。

レイヤードアーキテクチャの依存方向(問題あり):
  ドメイン → インフラ
  (安定) → (不安定)  ← ビジネスロジックがDBに依存

理想的な依存方向:
  インフラ → ドメイン
  (不安定) → (安定)  ← DBがビジネスロジックに合わせる

依存性逆転の原則(DIP)をアーキテクチャに適用

Month1で学んだDIPを、クラスレベルではなくアーキテクチャレベルで適用します。

// Step 1: ドメイン層にインターフェース(Port)を定義
// domain/ports/out/OrderRepository.ts
interface OrderRepository {
  findById(id: string): Promise<Order>;
  save(order: Order): Promise<void>;
}

// Step 2: ドメイン層はインターフェースにのみ依存
// application/CancelOrderUseCase.ts
class CancelOrderUseCase {
  constructor(private orderRepo: OrderRepository) {} // インターフェースに依存
  async execute(orderId: string): Promise<void> {
    const order = await this.orderRepo.findById(orderId);
    order.cancel();
    await this.orderRepo.save(order);
  }
}

// Step 3: インフラ層がインターフェースを実装
// adapters/out/PostgresOrderRepository.ts
class PostgresOrderRepository implements OrderRepository {
  async findById(id: string): Promise<Order> {
    // Prismaを使った具体的な実装
    const row = await prisma.order.findUnique({ where: { id } });
    return Order.fromPersistence(row);
  }

  async save(order: Order): Promise<void> {
    await prisma.order.update({
      where: { id: order.id },
      data: order.toPersistence(),
    });
  }
}

依存の方向が変わった

Before(レイヤード):
  CancelOrderUseCase → PostgresOrderRepository → Prisma → DB
  「ビジネスロジックがDBに依存」

After(依存性逆転):
  CancelOrderUseCase → OrderRepository(interface)

  PostgresOrderRepository ────┘(インフラがインターフェースに依存)
  「DBがビジネスロジックに合わせる」

境界を守るとは

依存性の方向を正しく制御することで、各関心領域の「境界」が守られます。

┌──────────────────────────────────────────┐
│  外側(インフラストラクチャ)               │
│  ┌──────────────────────────────────┐    │
│  │  中間(アプリケーション)            │    │
│  │  ┌──────────────────────────┐    │    │
│  │  │  内側(ドメイン)           │    │    │
│  │  │                          │    │    │
│  │  │  ビジネスルール            │    │    │
│  │  │  エンティティ              │    │    │
│  │  │  値オブジェクト            │    │    │
│  │  └──────────────────────────┘    │    │
│  │  ユースケース                     │    │
│  └──────────────────────────────────┘    │
│  DB, API, UI, Framework                  │
└──────────────────────────────────────────┘

矢印の方向: 外側 → 内側(常に内側に向かう)

ルール: 依存は常に外側から内側に向かう。内側は外側を知らない。


まとめ

ポイント内容
関心の分離ドメイン、アプリケーション、インフラの3領域に分割
安定依存の原則安定しているもの(ドメイン)に依存する
依存性逆転インターフェースを使って依存の方向を逆転させる
境界を守る依存は常に外側から内側に向かう

チェックリスト

  • 3つの関心領域とその変更頻度を説明できる
  • 依存性の方向が逆転する仕組みを理解した
  • インターフェースが境界を守る役割を果たすことを理解した
  • 「内側は外側を知らない」ルールを把握した

次のステップへ

次は「アーキテクチャパターンの全体像」を学びます。ヘキサゴナル、クリーン、オニオンなど、さまざまなアーキテクチャパターンがどのような関係にあるのかを俯瞰しましょう。


推定読了時間: 25分