ビジネスロジックを外部依存から隔離し、依存関係を一方向に制限する設計手法。
ルール: 内側の層は外側の層を知らない。外→内への依存のみ。
| 対象 | |
|---|---|
| ✅ | 中〜大規模で長期間保守するアプリ |
| ✅ | ビジネスロジックが複雑(ドメインルールが多い) |
| ✅ | 外部依存(DB、API)が変わる可能性がある |
| ❌ | 小規模スクリプト・CLI(レイヤー分け過多) |
| ❌ | プロトタイプ(動くもの最優先) |
# === Domain層(外部依存ゼロ)===
# domain/entities.py
from dataclasses import dataclass
@dataclass
class Reservation:
id: str; guest_name: str; party_size: int; date: str
def is_valid(self) -> bool:
return self.party_size > 0 and len(self.guest_name) > 0
# domain/ports.py(インターフェース定義)
from typing import Protocol
class ReservationRepository(Protocol):
def save(self, reservation: Reservation) -> str: ...
def find_by_date(self, date: str) -> list[Reservation]: ...
class NotificationSender(Protocol):
def send_confirmation(self, email: str, reservation_id: str) -> None: ...
# === Use Case層 ===
# usecases/create_reservation.py
class CreateReservationUseCase:
def __init__(self, repo: ReservationRepository,
notifier: NotificationSender):
self.repo = repo; self.notifier = notifier
def execute(self, guest_name: str, party_size: int, date: str) -> str:
reservation = Reservation(id="", guest_name=guest_name,
party_size=party_size, date=date)
if not reservation.is_valid():
raise ValueError("Invalid reservation")
existing = self.repo.find_by_date(date)
if len(existing) >= 10:
raise ValueError("No availability")
reservation_id = self.repo.save(reservation)
self.notifier.send_confirmation(guest_name, reservation_id)
return reservation_id
# === Adapter層(外部依存はここだけ)===
# adapters/sqlite_repository.py
import sqlite3
from domain.entities import Reservation
from domain.ports import ReservationRepository
class SqliteReservationRepository:
def __init__(self, db_path: str):
self.conn = sqlite3.connect(db_path)
def save(self, reservation: Reservation) -> str:
# SQLで保存
...
def find_by_date(self, date: str) -> list[Reservation]:
# SQLで検索
...
// === Domain層 ===
// domain/entities.ts
export interface Reservation {
id: string; guestName: string; partySize: number; date: string;
}
export function isValidReservation(r: Reservation): boolean {
return r.partySize > 0 && r.guestName.length > 0;
}
// domain/ports.ts
export interface ReservationRepository {
save(reservation: Reservation): Promise;
findByDate(date: string): Promise;
}
export interface NotificationSender {
sendConfirmation(email: string, reservationId: string): Promise;
}
// === Use Case層 ===
// usecases/createReservation.ts
export class CreateReservationUseCase {
constructor(private repo: ReservationRepository,
private notifier: NotificationSender) {}
async execute(guestName: string, partySize: number,
date: string): Promise {
const reservation: Reservation = { id: "", guestName, partySize, date };
if (!isValidReservation(reservation))
throw new Error("Invalid reservation");
const existing = await this.repo.findByDate(date);
if (existing.length >= 10) throw new Error("No availability");
const id = await this.repo.save(reservation);
await this.notifier.sendConfirmation(guestName, id);
return id;
}
}
## 開発手法: クリーンアーキテクチャ
- コードを4層に分ける: Domain → UseCase → Adapter → Framework
- 依存関係は内側にのみ向ける。Domain層は外部ライブラリに依存しない
- 外部依存(DB、API)はPort(Protocol/Interface)で抽象化、Adapter層で実装
- ディレクトリ構成: domain/ usecases/ adapters/ frameworks/
- テストはDomain・UseCase層を中心に(外部依存なしで高速)