ソフトウェアの構造を「ビジネスの概念」に合わせて設計し、業務ルールを正確にコードで表現する。
| 対象 | |
|---|---|
| ✅ | ビジネスルールが複雑(予約、在庫、契約、ワークフロー等) |
| ✅ | ルールが頻繁に変わる(業務知識の変化にコードを追従させたい) |
| ✅ | ユーザーとの会話から要件を整理したい |
| ❌ | 単純なCRUD(作る・読む・更新・消すだけのアプリ) |
| ❌ | データの変換・加工が中心のツール |
from dataclasses import dataclass
from enum import Enum
# --- 値オブジェクト(属性で識別・不変)---
@dataclass(frozen=True)
class GuestCount:
value: int
def __post_init__(self):
if self.value < 1 or self.value > 20:
raise ValueError(f"人数は1〜20人: {self.value}")
class ReservationStatus(Enum):
PENDING = "pending"
CONFIRMED = "confirmed"
CANCELLED = "cancelled"
# --- エンティティ(IDで識別)---
@dataclass
class Reservation:
id: str
guest_name: str
party_size: GuestCount
date: str
status: ReservationStatus = ReservationStatus.PENDING
def confirm(self) -> None:
"""予約を確定(ドメインルール: PENDINGのみ確定可能)"""
if self.status != ReservationStatus.PENDING:
raise ValueError(f"確定できるのはPENDINGのみ: {self.status}")
self.status = ReservationStatus.CONFIRMED
def cancel(self) -> None:
"""予約をキャンセル(ドメインルール: CONFIRMEDのみ可能)"""
if self.status != ReservationStatus.CONFIRMED:
raise ValueError(f"キャンセルできるのはCONFIRMEDのみ: {self.status}")
self.status = ReservationStatus.CANCELLED
# --- ドメインサービス(単一エンティティに属さないルール)---
class ReservationDomainService:
@staticmethod
def check_double_booking(new_res: Reservation, existing: list[Reservation]) -> bool:
same = [r for r in existing if r.date == new_res.date
and r.guest_name == new_res.guest_name
and r.status != ReservationStatus.CANCELLED]
return len(same) > 0
// --- 値オブジェクト ---
export class GuestCount {
readonly value: number;
constructor(value: number) {
if (value < 1 || value > 20) throw new Error(`人数は1〜20人: ${value}`);
this.value = value;
}
}
export enum ReservationStatus {
Pending = "pending",
Confirmed = "confirmed",
Cancelled = "cancelled",
}
// --- エンティティ ---
export class Reservation {
readonly id: string;
readonly guestName: string;
readonly partySize: GuestCount;
readonly date: string;
private _status: ReservationStatus;
constructor(props: {
id: string; guestName: string;
partySize: GuestCount; date: string;
status?: ReservationStatus;
}) {
this.id = props.id; this.guestName = props.guestName;
this.partySize = props.partySize; this.date = props.date;
this._status = props.status ?? ReservationStatus.Pending;
}
get status(): ReservationStatus { return this._status; }
confirm(): void {
if (this._status !== ReservationStatus.Pending)
throw new Error("確定できるのはPendingのみ");
this._status = ReservationStatus.Confirmed;
}
cancel(): void {
if (this._status !== ReservationStatus.Confirmed)
throw new Error("キャンセルできるのはConfirmedのみ");
this._status = ReservationStatus.Cancelled;
}
}
## 開発手法: DDD(ドメイン駆動設計)
- ビジネスルールをドメイン層(domain/)に集約する
- 値オブジェクト: 不変(frozen=True / readonly)、業務上の制約をコンストラクタで検証
- エンティティ: IDで識別、状態遷移のルールをメソッドとして定義
- ドメインサービス: 単一エンティティに属さないルール(重複チェック等)
- コード内の名前は業務用語(ユビキタス言語)に合わせる