Repositories, Factories, and Domain Services

Where persistence ends and the domain begins.

0/1 done

Theory

  • Repository — collection-like interface for aggregate roots. Hides persistence from the domain.
  • Factory — encapsulates complex aggregate construction.
  • Domain Service — stateless operation that doesn't belong on any single aggregate (e.g. MoneyTransfer.between(a, b)).

If a piece of logic would force an aggregate to know about something outside its boundary, it probably belongs in a domain service.

Worked example — a Protocol-based repository interface

Worked example — a repository defined as a typing.Protocol.

Python's Protocol (PEP 544) lets the domain declare a shape without importing anything from infrastructure. Any class with matching methods automatically satisfies it — duck typing, type-checked:

from typing import Protocol
from uuid import UUID
from .order import Order   # the aggregate root we defined earlier

class OrderRepository(Protocol):
    """Collection-like interface for the Order aggregate."""

    def get(self, order_id: UUID) -> Order: ...
    def add(self, order: Order) -> None: ...
    def remove(self, order: Order) -> None: ...

Three things to notice:

  • Each method body is just ... — Protocols are interfaces, not implementations.
  • The interface lives in the domain package; a SqlOrderRepository (in infrastructure) implements it without inheriting from it.
  • The repository deals only in aggregate roots — there is no OrderLineRepository because OrderLine cannot exist outside an Order.

Fill in the playground below with the same three methods.

Reading in progress · 0 of 1 activity done