Java Omnibus
OOAD & Architecture / Domain-Driven Design / Repositories and Factories
DDD Repositories Factories Java
Tactical DDD

Repositories and factories matter because creation and persistence are important boundaries, and the domain should not absorb them casually.

Repositories give the model a persistence-facing boundary. Factories give the model a controlled creation boundary. In DDD, both help keep domain behavior expressive while moving technical concerns to better places.

Why This Topic Matters

Why it matters in real Java systems

When aggregates need to be loaded and saved consistently, and when creation rules are more meaningful than plain constructors, repositories and factories help keep those responsibilities explicit rather than hidden throughout the codebase.

Core Explanation

The main design idea

A repository abstracts access to aggregates as part of the domain model's collaboration. A factory helps create complex domain objects or aggregates in a way that preserves invariants and intent. Neither pattern is unique to DDD, but both become especially important in rich domain models.

The main design question is whether the boundary clarifies the model or merely adds another layer of indirection.

Concept Breakdown

Key ideas to keep in mind

Concept

Repository

A boundary for retrieving and storing aggregates in domain terms.

Concept

Factory

A controlled creation mechanism when construction itself has business meaning or complexity.

Concept

Main payoff

Creation and persistence stop leaking everywhere into use cases and UI code.

Boundary reminder

Boundary reminder

Repository question: does this boundary speak in aggregate language, or is it just a thin database helper? Factory question: is creation complex enough to deserve a named concept, or would a clear constructor be simpler?
Java Example

Java example in the Order Management domain

Java example: repository plus factory
package org.javaomnibus.ecommerce.domain;

public interface OrderRepository {
    void save(Order order);
    Order requireById(OrderId orderId);
}

public final class OrderFactory {
    public Order create(OrderId orderId, CustomerId customerId) {
        return new Order(orderId, customerId);
    }
}
Code Walkthrough

Step by step

  1. The repository speaks in terms of Order rather than SQL or HTTP details.
  2. The factory gives creation a named boundary if creation deserves one.
  3. This keeps the application layer from scattering persistence and creation concerns everywhere.
  4. DDD uses these patterns to protect model clarity, not just to add layers.
Real-World Usage

Where this helps in practice

  • Aggregate persistence boundaries in domain-heavy applications
  • Creation workflows with invariants or multiple required collaborators
  • Keeping use cases focused on intent rather than plumbing
Trade-Offs

Trade-offs and when to be careful

  • Repositories and factories help when they clarify the model, but they become noise if they are purely ceremonial.
  • Factories are especially easy to overuse when constructors would be clearer.
Common Mistakes

Frequent mistakes to watch for

  • Creating repositories that expose persistence internals rather than domain concepts
  • Using factories for trivial object construction
  • Letting repositories become generic data utility layers for unrelated models
  • Confusing repository boundaries with direct ORM entity access everywhere
Related Pages

Related concepts


What To Read Next