ストーリー
クリーンアーキテクチャの4層
┌───────────────────────────────────────────┐
│ Layer 4: Frameworks & Drivers │
│ Express, Prisma, SendGrid, AWS SDK │
│ ┌─────────────────────────────────────┐ │
│ │ Layer 3: Interface Adapters │ │
│ │ Controller, Presenter, Gateway │ │
│ │ ┌───────────────────────────────┐ │ │
│ │ │ Layer 2: Application │ │ │
│ │ │ Business Rules (Use Cases) │ │ │
│ │ │ ┌─────────────────────────┐ │ │ │
│ │ │ │ Layer 1: Enterprise │ │ │ │
│ │ │ │ Business Rules │ │ │ │
│ │ │ │ (Entities) │ │ │ │
│ │ │ └─────────────────────────┘ │ │ │
│ │ └───────────────────────────────┘ │ │
│ └─────────────────────────────────────┘ │
└───────────────────────────────────────────┘
依存性ルール: 矢印は常に内側に向かう
Layer 4 → Layer 3 → Layer 2 → Layer 1
各層の役割
Layer 1: Enterprise Business Rules(エンティティ層)
企業全体のビジネスルールを表現します。最も安定した層です。
// entities/Order.ts
class Order {
private _status: OrderStatus;
constructor(
readonly id: OrderId,
readonly customerId: string,
private _items: OrderItem[],
status: OrderStatus
) {
this._status = status;
}
get totalAmount(): Money {
return this._items.reduce(
(sum, item) => sum.add(item.subtotal),
Money.zero('JPY')
);
}
canBeCancelled(): boolean {
return this._status === OrderStatus.PENDING
|| this._status === OrderStatus.CONFIRMED;
}
cancel(): void {
if (!this.canBeCancelled()) {
throw new DomainError('この注文はキャンセルできません');
}
this._status = OrderStatus.CANCELLED;
}
}
Layer 2: Application Business Rules(ユースケース層)
アプリケーション固有のビジネスルール(ユースケース)を実装します。
// use-cases/CancelOrderUseCase.ts
class CancelOrderUseCase {
constructor(
private orderRepo: OrderRepository,
private paymentGateway: PaymentGateway,
private notifier: NotificationSender
) {}
async execute(input: CancelOrderInput): Promise<CancelOrderOutput> {
const order = await this.orderRepo.findById(
OrderId.fromString(input.orderId)
);
if (!order) throw new NotFoundError('注文が見つかりません');
order.cancel(); // Layer 1のビジネスルールを呼び出す
await this.orderRepo.save(order);
await this.paymentGateway.refund(input.orderId, order.totalAmount);
await this.notifier.sendOrderConfirmation(order);
return { orderId: order.id.value, status: 'CANCELLED' };
}
}
Layer 3: Interface Adapters(インターフェースアダプター層)
データの変換を行います。外部のフォーマットと内部のフォーマットを橋渡しします。
// adapters/controllers/OrderController.ts
class OrderController {
constructor(private cancelOrder: CancelOrderUseCase) {}
async cancel(req: Request, res: Response): Promise<void> {
// HTTPリクエスト → Use Case入力 への変換
const input: CancelOrderInput = {
orderId: req.params.id,
reason: req.body.reason,
};
const output = await this.cancelOrder.execute(input);
// Use Case出力 → HTTPレスポンス への変換
res.json({
orderId: output.orderId,
status: output.status,
message: 'Order cancelled successfully',
});
}
}
// adapters/gateways/PrismaOrderRepository.ts
class PrismaOrderRepository implements OrderRepository {
async findById(id: OrderId): Promise<Order | null> {
const row = await prisma.order.findUnique({ where: { id: id.value } });
// DBレコード → ドメインエンティティ への変換
return row ? this.toDomain(row) : null;
}
}
Layer 4: Frameworks & Drivers(フレームワーク・ドライバー層)
最も外側の層。フレームワーク、ライブラリ、DBドライバーなどの具体的な技術です。
// frameworks/express/app.ts
import express from 'express';
const app = express();
app.use(express.json());
// ルーティング設定(フレームワーク固有のコード)
app.delete('/api/orders/:id', (req, res) =>
orderController.cancel(req, res)
);
// frameworks/prisma/schema.prisma
// model Order {
// id String @id
// status String
// ...
// }
依存性ルール(The Dependency Rule)
内側の層は外側の層について何も知らない。
| Layer | 知っていること | 知らないこと |
|---|---|---|
| Layer 1 (Entity) | ビジネスルールのみ | Use Case、Controller、DB |
| Layer 2 (Use Case) | Entity、Port IF | Controller、Prisma、Express |
| Layer 3 (Adapter) | Use Case、Entity | フレームワーク固有の詳細 |
| Layer 4 (FW) | すべて | - |
// Layer 2のUse Caseが知っているのは:
// - Layer 1のEntity(Order, Money, OrderId)
// - Layer 2で定義されたPort(OrderRepository interface)
// 知らないのは:
// - Prisma(Layer 4)
// - Express(Layer 4)
// - Controller(Layer 3)
ヘキサゴナルとの対応
| ヘキサゴナル | クリーン | 役割 |
|---|---|---|
| Domain | Entity (Layer 1) | ビジネスルール |
| Port (in) | Use Case Input (Layer 2) | 入力インターフェース |
| Port (out) | Use Case Output IF (Layer 2) | 出力インターフェース |
| Adapter (in) | Controller (Layer 3) | 入力変換 |
| Adapter (out) | Gateway (Layer 3) | 出力変換 |
| - | Framework (Layer 4) | 技術詳細 |
まとめ
| ポイント | 内容 |
|---|---|
| 4層構造 | Entity → Use Case → Adapter → Framework |
| 依存性ルール | 内側は外側を知らない |
| Layer 1 | 企業全体のビジネスルール(最も安定) |
| Layer 2 | アプリケーション固有のユースケース |
| Layer 3 | データ変換(内部↔外部) |
| Layer 4 | フレームワーク・ドライバー(最も不安定) |
チェックリスト
- 4層それぞれの役割を説明できる
- 依存性ルールを理解した
- ヘキサゴナルとの対応関係を把握した
- 各層に配置すべきコードを判断できる
次のステップへ
次は「Use Caseの設計」を学びます。クリーンアーキテクチャの核となるUse Case層を、どのように設計するかを詳しく見ていきましょう。
推定読了時間: 25分