skip to content

When designing a factory for an aggregate, how do you decide between putting a static/companion factory method directly on the aggregate root versus writing a separate, standalone Factory class?

level: middleimportance: must knowfreq 45%

answer

  1. factory method: no external deps needed
  2. standalone factory: repository/ID-generator/polymorphic creation
  3. keep infrastructure out of the aggregate root
  4. role, not fixed shape

basics

~20 s

Use a method on the object itself when creation only needs data already going into that object; use a separate factory class when creation needs outside help, like other aggregates, ID generators, or several related objects built together.

solid answer

~40 s

A factory method on the aggregate root (e.g., a static `Order.create(...)` or companion-object function) is the default choice: it's simple, keeps creation logic next to the type it builds, and works well when construction only depends on the inputs being passed in. A standalone factory class earns its place when creation needs collaborators the aggregate itself shouldn't depend on - a repository to check uniqueness, an ID generator service, another aggregate's data, or when one factory needs to build several different concrete types depending on context. The standalone factory can take those dependencies in its own constructor without polluting the aggregate's domain model with infrastructure concerns.

go deeper

for a junior

Should know both forms exist and that one lives on the class, the other is separate; doesn't need to justify the choice with dependency reasoning.

for a middle

Should identify at least one concrete trigger (external lookup, ID generation) for choosing a standalone factory over a method.

for a senior

Should discuss the coupling trade-off explicitly and describe a failure mode from putting infrastructure dependencies into the aggregate's own factory method.

for a principal

Should reason about how this choice affects reuse across multiple call sites/contexts (interactive vs batch) and design around swappable factory implementations.

## A role, not a fixed shape In DDD, 'factory' is a role, not a fixed shape - the pattern deliberately leaves open whether that role is played by a method living on the aggregate root itself or by a separate object dedicated to construction. The core question that decides between them is: **what does building this aggregate depend on?** If everything the factory needs to produce a valid instance is already present in the parameters being passed in - a customer id, a list of items, an amount - then a static or companion factory method directly on the aggregate class is the natural fit: - it keeps related code together (the type and the one legitimate way to create it live in the same file) - it's discoverable (an IDE autocomplete on the class immediately shows `Order.create(...)`) - it avoids introducing an extra class purely for the sake of following a pattern ## Three triggers for a standalone factory A standalone factory class becomes the better tool once construction needs something the aggregate has no business depending on. Three common triggers: 1. **External collaborators.** First, checking that an email or username is unique requires querying a repository, and an aggregate class holding a repository reference blurs the line between domain model and infrastructure, so a separate `UserFactory` that takes a `UserRepository` in its own constructor keeps that dependency out of the aggregate itself. 2. **Identity generation strategies.** Second, strategies that involve calling out to a sequence, a UUID service, or a distributed ID generator are naturally factory-level concerns rather than something the aggregate type should know how to do. 3. **Polymorphic or composite creation.** Third, if 'create a Shipment' might produce a StandardShipment or an ExpressShipment depending on business rules, or if creating one aggregate also requires creating and wiring several related aggregates or domain services together in one transaction, a standalone factory can hold that branching or orchestration logic without cluttering any single aggregate class with knowledge of its siblings. ## The trade-off The trade-off is discoverability and indirection versus dependency cleanliness. A standalone factory is one more class a new developer has to find and learn to use instead of just calling a method on the type they already know about; if overused for aggregates that don't actually need external collaborators, it adds ceremony without benefit. Conversely, cramming infrastructure dependencies (repositories, external services) into an aggregate root's static factory method to avoid creating a separate class is a common way domain models silently pick up infrastructure coupling - the aggregate becomes hard to unit test in isolation and hard to reuse across contexts (e.g., a batch import path and an HTTP endpoint both needing different transactional behavior around the same creation logic). ## A concrete failure mode A concrete failure mode: a team adds a uniqueness check for usernames directly inside an aggregate-root static factory method, injecting a repository as a parameter into what looks like a pure static function. This works initially, but later the team wants to reuse the same 'build a valid User' logic inside a Kafka consumer that processes user-import events in bulk, without the per-item repository round-trip - now the aggregate's own construction API is entangled with a specific persistence strategy, and refactoring it means touching the aggregate class itself rather than swapping out a factory implementation. Extracting that logic into a standalone `UserFactory` interface, with - one implementation for the interactive path (repository-backed uniqueness check) - a different implementation for the bulk path (a pre-loaded in-memory set) lets both call sites share a common `create(...)` contract while diverging in how the invariant is actually checked - something a same-class static method can't offer, because there's exactly one class to change. ## Where it shows up A concrete, real-world-flavored example: an online marketplace's `Listing` aggregate can be created with a simple static factory (`Listing.create(sellerId, title, price)`) because everything needed is in the parameters and no external lookups are required. But its `Order` aggregate is built by a standalone `OrderFactory` that takes an `InventoryReservationService` and an `IdGenerator` in its constructor, because placing an order needs to reserve stock across possibly multiple `Listing` aggregates and mint an order number from a shared sequence - concerns the `Order` type itself should stay ignorant of. The rule of thumb many teams converge on: | Construction | The shape | |---|---| | needs only the parameters already being passed in | default to a factory method on the aggregate | | the moment it needs a dependency injected | reach for a standalone factory | | the moment it needs to choose between concrete types | reach for a standalone factory | | the moment it needs to coordinate more than one aggregate | reach for a standalone factory |

  • What's the concrete risk of putting a repository dependency into a static factory method on the aggregate root itself?
    It couples the domain type to a specific persistence/infrastructure concern, making the aggregate harder to unit test without mocking that dependency and harder to reuse in contexts with different data-access needs, like a bulk import job. It also blurs the boundary between domain logic and infrastructure that DDD tries to keep separate.
  • If an aggregate's creation logic needs to decide between two different concrete subclasses depending on input, which approach fits better?
    A standalone factory, because a static method on one concrete aggregate class is an awkward place to hold branching logic that produces a sibling type; a dedicated factory class (or interface with implementations) can own that decision cleanly and can be swapped or extended without touching either aggregate class.

A factory method on the aggregate is like a barista making your drink from ingredients you hand over at the counter; a standalone factory is like a catering company that has to call the supplier, check stock, and coordinate several dishes before anything gets built - too much coordination to happen behind the counter.

saying these in an interview costs you the question

  • Says the two approaches are interchangeable with no criteria for choosing
  • Doesn't mention infrastructure/dependency coupling as a driver
  • Thinks 'standalone factory' means the GoF Abstract Factory pattern specifically
  • Can't name a concrete trigger (uniqueness check, ID generation, polymorphic creation) for reaching for a standalone factory

context