🏭
概念 #DDD #ファクトリ #オブジェクト生成 #ドメインモデル 📚 実践ドメイン駆動設計

ファクトリ(Factory)

複雑な集約・エンティティの生成ロジックをカプセル化し、クライアントに詳細を隠す

解決する課題

集約の生成にビジネスルールが絡む場合、コンストラクタがドメインの詳細を過剰に知ってしまう。また呼び出し側が複数の依存オブジェクトを組み立てて渡す必要があり、生成の複雑さがクライアントに漏れる

概念

ファクトリは、複雑なオブジェクトの生成ロジックをカプセル化する。クライアントはどのように作るかを知らずに「完全な状態のオブジェクト」を受け取れる。

DDD では3種類のファクトリが使われる:

種類使いどころ
ファクトリメソッド(集約ルート内)別の集約が特定の子集約を生成する場合
スタティックファクトリコンストラクタをシンプルに保ちたい場合
専用ファクトリクラス生成に複数の依存が必要な場合

スタティックファクトリメソッド

class Order:
    def __init__(self, order_id: OrderId, customer_id: CustomerId):
        # プライベートな初期化
        self._id = order_id
        self._customer_id = customer_id
        self._items = []
        self._status = OrderStatus.DRAFT

    @classmethod
    def create(cls, customer_id: CustomerId) -> 'Order':
        """新しい注文を生成するファクトリメソッド"""
        return cls(
            order_id=OrderId.generate(),
            customer_id=customer_id,
        )

    @classmethod
    def reconstruct(cls, order_id: OrderId, customer_id: CustomerId,
                    items: list, status: OrderStatus) -> 'Order':
        """永続化されたデータから集約を復元するファクトリメソッド"""
        order = cls(order_id, customer_id)
        order._items = items
        order._status = status
        return order

別集約のファクトリメソッド

class Forum:
    def start_discussion(self, author_id: UserId, subject: str) -> 'Discussion':
        """Forum がDiscussion の生成責任を持つ"""
        if not self._is_open:
            raise DomainException("クローズされたフォーラムにはスレッドを立てられません")
        return Discussion(
            discussion_id=DiscussionId.generate(),
            forum_id=self._id,
            author_id=author_id,
            subject=subject,
        )

ファクトリが必要なサイン

  • コンストラクタが5つ以上の引数を取る
  • 生成時にバリデーション・ビジネスルールがある
  • 生成のロジックを複数箇所で再利用する
  • 「生成する」行為自体がドメインイベントを発行する

ファクトリ不要のケース

単純なエンティティは __init__ で十分。ファクトリは複雑さが正当化する場合のみ導入する。

関連概念

出典: 実践ドメイン駆動設計(Vaughn Vernon)第11章