クリーンアーキテクチャが進化耐性をもたらす理由
依存方向の原則がドメインを安定させ、DB・フレームワーク変更を局所化する。Expand-Contract・Dual Write との接続と、ユースケーステストが Fitness Function になる仕組みを整理する
依存方向の原則
クリーンアーキテクチャの本質は一つの規則だ。
「依存関係は常に内側(ドメイン)に向かう。外側(インフラ)が内側を知る。内側は外側を知らない」
外側(インフラ / フレームワーク)
┌─────────────────────────────────────┐
│ Controller / Gateway / Repository │
│ ↓(依存する方向) │
│ ┌─────────────────────┐ │
│ │ Use Cases │ │
│ │ ↓ │ │
│ │ ┌─────────────┐ │ │
│ │ │ Domain │ │ │
│ │ │ (Entity) │ │ │
│ │ └─────────────┘ │ │
│ └─────────────────────┘ │
│ 内側(ドメイン)が変化の中心 │
└─────────────────────────────────────┘
← 変化の影響は外側に留まる
インフラの詳細(どの DB を使うか、どのフレームワークか)は外側に閉じる。ドメインはそれらを知らない。
なぜドメインが安定するとアーキテクチャが進化できるか
DB を変更しても Domain は無傷
// Domain 層: DB を知らない(ドメインの言語で定義)
interface UserRepository {
findById(id: UserId): Promise<User | null>;
save(user: User): Promise<void>;
}
// Infrastructure 層: ドメインを知り、実装を提供する
class PostgresUserRepository implements UserRepository {
async findById(id: UserId) { /* Postgres の詳細 */ }
async save(user: User) { /* Postgres の詳細 */ }
}
// PostgreSQL → DynamoDB に変える場合:
class DynamoUserRepository implements UserRepository {
async findById(id: UserId) { /* DynamoDB の詳細 */ }
async save(user: User) { /* DynamoDB の詳細 */ }
}
// DI の設定(外側)を変えるだけ。ドメインは一行も変わらない。
フレームワーク変更が局所化される
Express → Hono → Elysia へ変えても、Controller 層だけを書き換えればよい。Use Case / Domain は変わらない。
// Use Case(フレームワークを知らない)
class CreateOrderUseCase {
async execute(command: CreateOrderCommand): Promise<Order> {
const order = Order.create(command);
await this.orderRepo.save(order);
return order;
}
}
// Express の Controller(外側)
app.post('/orders', async (req, res) => {
const order = await createOrderUseCase.execute(req.body);
res.status(201).json(order);
});
// Hono の Controller(外側)← ここだけ変わる
app.post('/orders', async (c) => {
const order = await createOrderUseCase.execute(await c.req.json());
return c.json(order, 201);
});
Expand-Contract・Dual Write との接続
Expand-Contract パターンと Dual Write はインフラ層(外側)で完結する。ドメインを汚さない。
Domain 層(変わらない)
├─ User エンティティ
└─ UserRepository インタフェース(findById / save のみ)
Infrastructure 層(ここで Expand-Contract を実施)
└─ PostgresUserRepository
├─ Phase 1 (Expand): email と email_new の両方に書く
├─ Phase 2 (Migrate): バックフィル
└─ Phase 3 (Contract): email_new だけ読み書き、email カラム削除
ドメイン層の UserRepository インタフェースは save(user: User) のままで変わらない。DB 移行の詳細は Repository 実装の中だけで変わる。
Fitness Function = ユースケーステストがドメインを守る
クリーンアーキテクチャではユースケーステストが書きやすい。インフラなし(In-Memory Repository)でドメインの振る舞いを高速に検証できる。
これは進化的アーキテクチャの Fitness Function として機能する。
// ユースケーステスト = ドメインを守る Fitness Function
describe('CreateOrderUseCase', () => {
it('在庫がない場合は注文できない', async () => {
const inventory = new InMemoryInventory({ itemId: 'A', stock: 0 });
const useCase = new CreateOrderUseCase(
new InMemoryOrderRepository(),
inventory,
);
await expect(
useCase.execute({ items: [{ itemId: 'A', qty: 1 }] })
).rejects.toThrow(OutOfStockError);
// DB なし・ネットワークなしで高速に実行できる
// 変更のたびに CI でこのテストが走り、ドメインルールを守る
});
});
レイヤードアーキテクチャとの進化耐性の比較
| 観点 | レイヤードアーキテクチャ | クリーンアーキテクチャ |
|---|---|---|
| 依存の方向 | 上から下(Domain が Infra を知ることがある) | 常に内側(Domain は Infra を知らない) |
| DB 変更の影響範囲 | Domain まで波及しやすい | Repository 実装だけで完結 |
| フレームワーク変更の影響 | Service 層まで影響することがある | Controller 層だけ |
| テストの書きやすさ | DB モックが必要になりやすい | In-Memory で高速テスト可 |
| 進化的アーキテクチャとの親和性 | 中 | 高い(外側が変わってもドメインは不変) |
「Clean Architecture だから変化に強い」ではない
注意点がある。クリーンアーキテクチャはあくまで依存方向の原則だ。ドメインロジックが肥大化したり、インタフェースが多すぎて変更しにくくなったりすると逆効果になる。
進化耐性を高めるのは「依存方向の原則」+「Fitness Function(ユースケーステスト)」+「段階的変更」の組み合わせだ。
関連概念
- → 進化的アーキテクチャ概要
- → ヘキサゴナルアーキテクチャ(ポート&アダプターによる具体的実装)
- → Expand-Contract パターン(インフラ層で完結するDB変更)
- → Unix 哲学とソフトウェア設計
出典・参考文献
- Robert C. Martin, Clean Architecture: A Craftsman’s Guide to Software Structure and Design (2017)
- Neal Ford et al., Building Evolutionary Architectures (2022) Chapter 4
- 1. 🧬進化的アーキテクチャとは:定義・誕生背景・3原則
- 2. 📐適応度関数(Fitness Functions):アーキテクチャ特性を自動検証する
- 3. 🔗適切な結合(Appropriate Coupling):Connascence と分散モノリスの罠
- 4. 🚀段階的変更(Incremental Change):Feature Toggle・Blue-Green・Canary・Dark Launch
- 5. 🔄Expand-Contract パターン:ゼロダウンタイムでスキーマ変更する
- 6. ✍️Dual Write:マイクロサービス分割時のデータ整合性パターンと落とし穴
- 7. 📤Outbox Pattern:トランザクション境界を越えた安全なイベント発行
- 8. 📡CDC(Change Data Capture):アプリを汚さずに変更を伝播する
- 9. 🐚Unix 哲学を現代アーキテクチャへ翻訳する
- 10. 🧅クリーンアーキテクチャが進化耐性をもたらす理由
- 11. 🏢Conway's Law:組織構造がアーキテクチャを決める
出典: Clean Architecture(Robert C. Martin)/ 進化的アーキテクチャ(O'Reilly)