Last Updated: March 25, 2026 at 15:30
Factories in Domain-Driven Design
Factories encapsulate the complexity of creating domain objects, ensuring that entities, value objects, and aggregates are constructed in a valid state without overloading constructors with logic or long parameter lists. By separating creation responsibilities from the objects themselves, factories maintain clear boundaries—constructors handle simple assignment while factories manage validation, calculation, and coordination with external services. When invariants must be enforced from the moment an object comes into existence, factories serve as gatekeepers that reject invalid creation requests rather than leaving partially constructed objects behind. Mastering factory patterns allows developers to keep domain models focused on behavior rather than creation mechanics, resulting in code that is both expressive and maintainable

Introduction
Throughout this series, we have explored the tactical building blocks of Domain-Driven Design. We have examined entities, defined by identity that persists through change. We have examined value objects, immutable and defined by their attributes. And we have examined aggregates, which cluster these objects together to protect consistency boundaries.
But there is a question that arises naturally when working with these patterns: how are complex objects created in the first place?
Consider a customer placing an order in an e-commerce system. An order requires items, each with a product, quantity, and price. It has a customer, a shipping address, a status, and a total amount. The order number must follow business rules. The total must be calculated. The initial status must be set. The customer must be validated. If any item is out of stock, the entire order should be rejected.
If all this logic were placed inside the order constructor, the constructor would become overloaded with responsibility. It would need to know about inventory services, pricing calculations, and identifier generation. The constructor would no longer be a simple mechanism for creating an object; it would become entangled with infrastructure and cross-aggregate coordination.
Factories solve this problem. They encapsulate the complexity of object creation, ensuring that domain objects are constructed in a valid state while keeping the creation logic separate from the objects themselves.
This article explores factories in Domain-Driven Design. We will examine what factories are, why they are needed, how they differ from other patterns, and how to implement them effectively.
Part One: The Problem of Complex Object Creation
Why Creation Matters
In any domain model, objects must be created in a valid state. An order cannot exist without items. A bank account cannot exist without an initial balance. A meeting cannot exist without a time and a room. These invariants must hold from the moment the object comes into existence.
When object creation is simple—a few fields that can be set directly—a standard constructor suffices. The constructor takes the required parameters, validates them, and assigns them to fields. The object emerges in a valid state.
But many domain objects are not simple to create.
When Constructors Become Overwhelmed
Constructors can become problematic in several situations.
First, when an object has many required attributes, the constructor parameter list becomes long. A constructor with ten or twelve parameters is difficult to use and difficult to read. Callers must remember the order of parameters and ensure they pass the correct values.
Second, when creation involves logic. If an object must validate relationships between parameters, perform calculations, or enforce complex invariants, the constructor becomes bloated with business logic. This logic may properly belong in the constructor, but when it grows large, it obscures the constructor's primary purpose.
Third, when creation requires external data. An order may need to look up product prices or validate customer status. A constructor cannot easily access services or repositories without violating the principle that domain objects should remain independent of infrastructure.
Fourth, when multiple creation scenarios exist. An order might be created by a customer placing an order online. It might be created by a sales representative in the call center. It might be recreated from historical data for reporting. Each scenario may require different steps and different validation.
The Result: Constructor Overload
When constructors are stretched beyond their natural limits, several problems emerge.
The domain object becomes harder to test. Setting up valid instances requires understanding all the constructor parameters and any hidden dependencies.
The domain object becomes harder to understand. A constructor with a long parameter list does not communicate the meaning of each parameter. Callers cannot easily see which parameters are required versus optional.
The domain object becomes harder to maintain. Changes to creation logic require modifying the constructor, which may affect all callers.
Factories address these problems by providing a dedicated place for creation logic.
Part Two: What Is a Factory?
Definition
A factory is a pattern that encapsulates the logic of creating complex domain objects. It takes responsibility for constructing an object in a valid state, handling any necessary validation, calculation, or coordination that creation requires.
Factories can take different forms. They can be standalone classes with methods that return domain objects. They can be static methods on the domain object itself. They can be services that coordinate creation across multiple aggregates. The common thread is that factories separate creation logic from the objects being created.
Factory vs. Constructor
A constructor is the simplest form of creation mechanism. It creates a new instance of a class. It validates inputs and assigns fields. It is appropriate when creation is straightforward.
A factory becomes appropriate when creation is complex. The factory may perform multiple steps. It may coordinate with other services. It may enforce complex invariants that span multiple parameters. It may support multiple creation scenarios through different factory methods.
The distinction is not absolute. Many domain objects have both a simple constructor for internal use and factory methods for external use. The guiding principle is to keep creation logic at the appropriate level of complexity.
Factory vs. Repository
Factories and repositories serve different purposes and are often confused.
A factory creates new objects. It takes specifications and produces a new instance that did not previously exist.
A repository retrieves existing objects. It takes an identifier and returns an object that already exists in persistent storage.
The lifecycle is clear: factories create, repositories retrieve. An object is created by a factory, stored via a repository, and later retrieved by that repository.
However, it is worth noting that repositories often use internal factories during reconstruction. When an aggregate is loaded from persistent storage, the repository must rebuild the object from its stored state. This reconstruction is itself a form of object creation—just with different rules. During reconstruction, invariants that apply to new objects may not apply, because the object already existed and was validated when originally created. This is why factories typically handle creation, while repositories handle reconstruction, though a repository may delegate to a factory-like mechanism for the actual instantiation.
Repositories are covered in detail in a later tutorial.
Part Three: Types of Factories
Factory Methods
The simplest form of factory is a factory method—a static method on the domain object itself that encapsulates creation logic.
For example, an Order class might have a static method forOnlinePlacement that takes the necessary parameters and returns a new order instance. The method handles validation, calculation, and any other creation concerns.
Factory methods, sometimes called named constructors, are appropriate when creation logic is self-contained and does not require external dependencies. They keep the creation logic close to the domain object while keeping the constructor simple.
Factory Classes
When creation logic becomes more complex or requires dependencies, a separate factory class becomes appropriate.
A factory class contains methods for creating domain objects. It may hold references to services, repositories, or other dependencies needed during creation. It can be instantiated and configured as needed.
Factory classes are particularly useful when multiple related objects need to be created. For example, an OrderFactory might create orders, order items, and related value objects, ensuring consistency across all created objects.
Factory Services
When creation involves coordination across multiple aggregates or requires access to infrastructure, a factory service may be appropriate.
A factory service sits at the application layer or domain service layer. It coordinates the creation of aggregates, handles any necessary cross-aggregate validation, and ensures that all creation steps occur in the correct order.
Factory services are distinct from domain services that handle ongoing behavior. Their sole responsibility is creation.
Choosing Between Factory Types
The choice between a factory method, a factory class, and a factory service depends on complexity and dependencies.
Start with a factory method. If the creation logic is simple and self-contained, a static method on the domain object keeps things close and easy to find.
If the creation logic grows or requires dependencies, move to a factory class. The factory class can accept dependencies through its constructor, making testing easier.
If creation involves coordination across aggregates or requires access to infrastructure services, consider a factory service. This moves creation to a higher layer while keeping the domain layer free of infrastructure concerns.
Reconstruction vs. Creation
A subtle but important distinction exists between creating a new object and reconstructing an existing object.
When a new order is created for the first time, business rules apply: a new order number must be generated, the initial status must be set, validation must occur.
When an order is reconstructed from a database, these rules do not apply. The order already has an identity, a status, and a history. The reconstruction process simply recreates the object from stored data.
This distinction is important. Factories typically handle creation. Repositories handle reconstruction, often using a simple constructor or a dedicated reconstruction factory. Using the same mechanism for both can lead to bugs where creation logic is incorrectly applied to reconstructed objects.
Part Four: Maintaining Invariants During Creation
The Role of Invariants
Invariants are rules that must always hold true for an aggregate to be valid. These invariants apply from the moment the aggregate is created.
When a new order is created, the invariants of the order aggregate must be satisfied. The order must have at least one item. The total must equal the sum of item subtotals. The status must be appropriate for a new order.
The factory is responsible for ensuring that the created object satisfies all invariants. If an invariant cannot be satisfied given the inputs, the factory must reject the creation request.
Validation at the Boundary
Validation should occur at the factory boundary. The factory receives the inputs, validates them, and only proceeds if all validation passes.
This validation may include:
- Checking that required fields are present
- Ensuring that values fall within acceptable ranges
- Validating that relationships between fields are consistent
- Enforcing business rules that apply at creation time
If validation fails, the factory should not create a partially constructed object. It should return an error or throw an exception, leaving no inconsistent object behind.
Deferring to the Aggregate
While the factory handles the mechanics of creation, the aggregate itself should still enforce its own invariants. The factory should call the aggregate's constructor or creation methods, allowing the aggregate to perform its own validation.
This approach ensures that the aggregate retains control over its own consistency. The factory acts as a convenience layer, not as a bypass.
Part Five: Avoiding Constructor Overload
The Problem of Long Parameter Lists
Constructors with many parameters are difficult to work with. Callers must remember the order of parameters. Optional parameters lead to overloaded constructors. The meaning of each parameter is not self-evident.
Factories provide a solution by allowing named methods that communicate intent.
Builder Pattern as Factory
One approach to constructor overload is the builder pattern. A builder collects creation parameters and then builds the final object in one operation.
Builders are a form of factory. They separate the collection of parameters from the actual creation. They allow callers to set parameters in any order using named methods. They make optional parameters explicit.
Builders are particularly useful when an object has many optional attributes or when creation requires a fluent interface.
Invariants must be checked in the build step, not deferred until later use. The factory—whether implemented as a builder or not—must ensure that the final object is fully valid before returning it.
Multiple Factory Methods
Another approach is to provide multiple factory methods, each representing a different creation scenario.
An order might have a factory method for online orders and another for phone orders. Each method takes exactly the parameters needed for that scenario, with clear names that indicate the scenario.
This approach makes the creation process self-documenting. Callers can see exactly which factory method to use for their specific scenario.
Part Six: Practical Modeling Examples
Example One: Order Aggregate and Factory Methods
Consider an order aggregate. Creating an order requires a customer, a shipping address, a list of items, and a payment method. The order number must be generated according to business rules. The total must be calculated. The initial status must be set.
A factory class might look like this conceptually:
The OrderFactory takes a service for generating order numbers. Its createOrder method accepts the customer, items, addresses, and payment method. It generates the order number, calculates the total, validates that at least one item exists, and returns a new order instance.
The factory encapsulates all the logic that would otherwise clutter the order constructor. The order constructor itself can be simple, accepting the core attributes and trusting that the caller has already validated them.
For internal creation within the aggregate, such as adding items after creation, the aggregate root provides its own methods. For example, order.addItem(product, quantity) creates a new order item entity internally. This method enforces invariants—such as not adding items after the order is shipped—and maintains the aggregate's consistency. Using order.addItem() rather than new OrderItem() ensures that the root remains in control.
Example Two: Bank Account Factory
Consider a bank account aggregate. Creating an account requires a customer, an initial deposit, and an account type. Different account types have different minimum balance requirements.
A factory might validate that the initial deposit meets the minimum requirement for the chosen account type. If the deposit is insufficient, the factory rejects the creation. If the deposit is sufficient, it creates the account and records the initial transaction.
This validation could be placed in the account constructor, but doing so would require the constructor to know about account type rules. By placing it in a factory, the constructor remains simple while the creation rules are centralized.
Example Three: Meeting Factory with Cross-Aggregate Coordination
Consider a meeting aggregate in a scheduling system. Creating a meeting requires a title, a time range, a room, and a list of attendees.
A factory might coordinate with a room availability service to ensure the room is available at the requested time. If the room is not available, the factory can either reject the creation or suggest alternative times.
This coordination with an external service is a strong indicator that a factory is appropriate. The meeting aggregate should not have direct knowledge of the room availability service. The factory handles the coordination, then passes the validated parameters to the meeting constructor.
Example Four: Named Constructors as a Lightweight Alternative
For simpler cases, a named static constructor may be sufficient. For instance, an Address value object might have static methods like fromUnitedStates or fromInternational that apply different validation rules. These named constructors are easy to discover, communicate intent clearly, and require no additional classes.
When creation logic becomes more complex—requiring dependencies, multiple steps, or coordination—a factory class becomes the better choice. The progression from named constructor to factory class to factory service follows the natural growth of complexity.
Part Seven: Factories and Aggregates
Creating Internal Objects
When an aggregate contains internal entities, the aggregate root is typically responsible for creating them. The root may delegate to internal factories, but the creation of internal objects should happen through the root's methods.
For example, an order might have a method addItem that creates an order item entity. The order root creates the item, adds it to its collection, and updates the total. External code does not directly create order items.
This pattern ensures that the aggregate root remains in control of its internal consistency. The root can enforce invariants—such as not adding items after the order is shipped—every time an item is created.
Factories and Aggregate Roots
Factories typically create aggregate roots. The root serves as the entry point to the aggregate, so creating the root creates the aggregate as a whole.
When an aggregate contains complex internal structures, the factory may create the entire structure. The factory constructs the root and all internal objects, ensuring that the entire aggregate is valid at the moment of creation.
Factories and Value Objects
Value objects are often created by factories as well. A Money value object might have a factory method that ensures the currency code is valid. An Address value object might have a factory that normalizes street names and postal codes.
Because value objects are immutable, factories provide a convenient way to enforce validation at creation time. Once created, a value object never changes, so validation only needs to occur once.
Part Eight: Factories vs. Domain Services
Overlapping Responsibilities
Factories and domain services both contain domain logic. The distinction can sometimes blur.
A domain service handles operations that do not naturally belong to any entity or value object. It coordinates across multiple aggregates. It performs calculations that involve multiple domain objects.
A factory creates domain objects. It handles the transition from raw inputs to a valid domain object.
Domain services will be covered in detail in a later tutorial.
When to Use Which
If the primary responsibility is creating a new object, a factory is appropriate.
If the primary responsibility is coordinating behavior across existing objects, a domain service is appropriate.
If creation requires coordination across aggregates, a factory service—which is a domain service focused specifically on creation—may be appropriate.
The key is clarity of purpose. A factory should not be used for general coordination. A domain service should not be used solely for creation unless creation involves coordination.
Part Nine: Practical Challenges with Persistence Frameworks
The Tension with ORMs
One of the most common practical challenges developers face when implementing factories is the tension with persistence frameworks such as Hibernate, Entity Framework Core, or other object-relational mappers.
Many ORMs require a no-argument constructor to create objects when loading them from the database. This requirement conflicts directly with the principle of keeping constructors simple and validated. If the domain object has only a validated constructor that requires parameters, the ORM cannot instantiate it.
Several approaches address this tension.
One approach is to give the ORM a private no-argument constructor. The constructor is only accessible to the ORM, while public constructors enforce validation. This approach preserves the domain invariants while satisfying the ORM's requirements.
Another approach is to use a dedicated reconstruction factory. The repository uses a factory-like mechanism specifically for rebuilding objects from persistence. This mechanism bypasses the creation validations that apply to new objects while still ensuring the object is correctly reconstructed.
A third approach is to separate the persistence model from the domain model. The repository maps between a persistence-friendly representation and the domain object, allowing the domain object to retain full control over its constructors.
The choice depends on the specific ORM and the project's constraints. What matters is that the domain model does not compromise its invariants to accommodate persistence concerns.
Part Ten: Implementation Guidelines
Keep Constructors Simple
Constructors should be simple. They should accept validated parameters and assign them to fields. They should not contain complex logic, access services, or perform validation beyond basic null checks.
By keeping constructors simple, the domain object remains easy to understand and test. Complex creation logic moves to factories where it can be managed properly.
Make Factories Self-Documenting
Factory methods should have names that clearly indicate what they create and under what circumstances. A method named createOnlineOrder communicates more than a generic createOrder method with a boolean parameter.
Factory classes should have clear responsibilities. An OrderFactory should create orders. If it also creates invoices or shipments, those responsibilities likely belong elsewhere.
Return Fully Valid Objects
A factory must never return an object in an invalid state. If creation cannot complete successfully, the factory should not return a partially constructed object. It should return an error or throw an exception.
This principle ensures that any object obtained from a factory is safe to use. The factory serves as a gatekeeper, preventing invalid objects from entering the system.
Consider Testing
Factories should be designed for testability. They should accept dependencies through constructors or method parameters, allowing tests to provide mock implementations when needed.
Factory methods should be easy to call in tests. If creating a valid order requires ten parameters, tests will become burdened. Consider providing factory methods that accept minimal required parameters and use sensible defaults for the rest.
Part Eleven: Common Misconceptions
Every Object Needs a Factory
Not every object needs a factory. Value objects with a few attributes can be created directly with constructors. Entities with simple creation requirements can use constructors.
Factories are a tool for managing complexity. When creation is simple, a factory adds unnecessary indirection.
Factories Replace Constructors
Factories do not replace constructors. They complement them. Constructors handle the basic mechanics of instance creation. Factories handle the logic of determining what to pass to those constructors.
Even when a factory exists, the domain object should have a constructor. The factory calls the constructor after performing any necessary validation or coordination.
Factories Should Not Contain Domain Logic
Factories contain domain logic. The logic of what constitutes a valid creation request is domain logic. The rules for generating identifiers are domain logic. The validation that ensures invariants at creation is domain logic.
Factories are part of the domain layer. They are not infrastructure. They should be placed alongside the domain objects they create.
Conclusion
Factories fill an essential gap in the tactical toolkit of Domain-Driven Design. They provide a dedicated place for complex creation logic, keeping domain objects focused on their core responsibilities while ensuring that objects are always created in a valid state.
When constructors become overloaded with parameters, factories offer a cleaner alternative. When creation requires coordination across aggregates, factories provide a natural boundary. When invariants must be enforced at creation, factories serve as the gatekeeper.
The factory pattern complements the other tactical building blocks. It works with entities, value objects, and aggregates to ensure that objects are not only well-designed in their behavior but also well-designed in their origins.
By using factories appropriately, developers can achieve several important goals. Domain objects remain simple and focused. Creation logic is centralized and maintainable. Invariants are enforced from the moment objects come into existence. And the domain model becomes more expressive, with creation scenarios clearly named and documented.
About N Sharma
Lead Architect at StackAndSystemN Sharma is a technologist with over 28 years of experience in software engineering, system architecture, and technology consulting. He holds a Bachelor’s degree in Engineering, a DBF, and an MBA. His work focuses on research-driven technology education—explaining software architecture, system design, and development practices through structured tutorials designed to help engineers build reliable, scalable systems.
Disclaimer
This article is for educational purposes only. Assistance from AI-powered generative tools was taken to format and improve language flow. While we strive for accuracy, this content may contain errors or omissions and should be independently verified.
