Dec 24, 2014
Table of contents:
A commonly recognised term in object-orientated programming is Factory. A Factory is an object that has the sole responsibility to create other objects.
Factories are definitely not unique to Domain Driven Design, but they do hold an important role within a Domain Driven Design project.
In today’s article we are going to look at Factories, why are they important and how to use them within the context of a Domain Driven Design application.
A Factory is an object that has the single responsibility of creating other objects.
For example, we might have the following simple Factory class in PHP:
class Factory
{
public static function build($type)
{
if ($type == "iPhone") {
return new iPhone();
}
if ($type == "Nexus") {
return new Nexus();
}
}
}
$mobile = Factory::build("iPhone");
The Factory
class has a single static
method called build
that has the responsibility to accept a type of mobile phone and return the correct new object instance. This Factory
class should have no other responsibilities.
Factories can also be methods on an existing object. For example, we might have the following Order
object that has the responsibility for creating a new Invoice
:
class Order
{
// other methods
public function invoice()
{
return new Invoice(/** details */);
}
// other methods
}
In either case, when something has the responsibility for creating another object, it’s called a Factory.
Factories are often used to create Aggregates. As we looked at in last week’s tutorial on Aggregates (What are Aggregates in Domain Driven Design?), Aggregates provide encapsulation and a consistent boundary around a group of objects.
Aggregates are important because they enforce the internal consistency of the object they are responsible for. A Factory can be useful when creating a new Aggregate because it will encapsulate the knowledge required to create an Aggregate in a consistent state and with all invariants enforced.
There are a number of important reasons as to why you would want to use a Factory as part of your application. As applications grow in complexity, Factories become more common.
Factories provide a consistent interface for creating complex objects within an application. As an application grows, the rules around object creation can get lost. Factories provide a standard way to instantiate objects so the creation of new objects is not lost in ambiguity.
When the creation of an object becomes complicated, or it reveals too much of the internal structure, Factories provide encapsulation.
A Factory encapsulates the knowledge needed to create a complex object or Aggregate. The Factory needs to know a lot about the internal structure and dependencies of the object, but the Factory will shield this complexity from the outside world by providing an interface that reflects the goals of the client and an abstract view of the created object.
The consumer of an object should not be required to understand the internals of an object and so a Factory provides this encapsulation for complex objects.
The real power behind object-oriented programming is the encapsulation of an object’s internal logic. A client should be able to use an object without ever being concerned about the internal implementation of that object.
Objects should have a single responsibility, and so complexity of an object should be distilled until nothing remains that does not relate to its meaning.
However, it’s easy to overload an object with complexity when it becomes responsible for it’s own creation. Certain objects will require a complex process of instantiation that can muddy the water of the object’s single responsibility.
When this situation occurs, it’s time to abstract that creation process to a Factory. There is no inherent value in having an object take responsibility for it’s own creation.
Factories can also provide an important layer of abstraction that protects the client from being dependent on a particular concrete class.
For example, we might have a payment gateway Factory that should provide a class for accepting payments from customers to any one of many payment gateways.
The Factory should protect the client by providing a class that implements a particular interface. It is up to the Factory to instantiate and return a particular class based upon a given type. This shields the dependency inside of the Factory.
In object-oriented programming the use of Factories is extremely common across many different languages. The use of Factories long predates Domain Driven Design. Domain Driven Design is largely built on good object-oriented programming patterns.
Domain Driven Design is all about modelling the domain of an application. Factories have nothing to do with the domain of an organisation, but they do form an important part of the domain of an application.
It is the domain of the application that is responsible for creating and working with Domain Objects. The Ubiquitous Language of a Domain Driven Design project will not directly refer to the process of creating objects. However it is our responsibility to devise the appropriate technical solution for working with the Domain Objects of an applications.
This means that despite the fact that the Ubiquitous Language of a project does not directly refer to Factories, they still form an important part of the Domain layer of the application.
The interface of a Factory is important as it should reflect the goals of the client and an abstract view of the created object.
Each operation of a Factory should accept everything that is required to create the new object in a single interaction. This means that the Factory should require that you pass in everything needed to create the object or Aggregate so that it’s internal invariants are satisfied.
It’s also important to decide how problems should be handled if the invariants of the created object are not satisfied. Typically this would be handled by throwing an Exception (When should you use an Exception?), but sometimes it makes sense to simply return null
.
And finally you should use abstract types, rather than concrete classes. This means instead of relying on any particular concrete implementation of a class, instead you should rely on a generic interface of that type of object.
Factories provide an encapsulation around the instantiation of a particular object. This means the Factory will know about the internals of the object, but it will act as an abstraction from the outside world.
Therefore, the internals of an object will often leak out into the Factory. This is an inevitable situation when the complexity of object creation increases.
Often the complexity of creating a new object will mean that the Factory itself will become responsible for protecting the invariants of the object. As I mentioned earlier, the object should have a single responsibility and anything that does not enhance that meaning should be distilled.
Due to the coupled nature of the Factory and the object, it is usually fine to allow the Factory to protect the invariants of the object.
Two of the most important types of objects are Entities and Value Objects (What is the difference between Entities and Value Objects?).
Entities have identity within the application and so exist as part of a lifecycle. Value Objects on the other hand are immutable and are easily destroyed and replaced.
When using a Factory to create Value Objects, the Factory should create the Value Object as a complete whole.
Entities on the other hand often have a minimum set of requirements, and then further optional properties that can be set during the lifecycle of the object. An Entity Factory should be able to create an Entity with the minimum requirements for that Entity to be valid.
An important part of an Entity is its identity. When using a Factory to create an Entity you can either pass in the identity as an argument to the method, or alternatively allow the Factory to create the new identity as part of the object creation process.
Objects are usually moved around during the processing of an application. It is very rare that all objects will live entirely in memory. Instead objects are usually persisted to a database or transferred across a network.
Factories can be used to reconstitute objects as well as create new objects, however there are a couple of important differences.
Firstly, an Entity Factory should not assign a new id to an existing Entity. This means you will need to maintain two different Factories for creating or reconstituting Entities.
Secondly, the Factory needs a way to deal with broken invariants when an object already exists. This could be due to any number of reasons, but the internal consistency of the object is extremely important and so this problem should be handled with the appropriate response. Typically this would be an Exception
because it is signally that something has seriously went wrong during the processing of that object.
Factories are a very important tool to have in your developer tool belt. At first it can be difficult to fully appreciate when and where you should use Factories in your code. But over time and through experience you will find that using Factories can really clarify and distill your code.
Factories are by no means exclusive to Domain Driven Design or PHP. You will likely encounter Factories in one form or another in just about every object oriented programming language.
So if you are struggling to deal with the complexity of instantiating certain objects, try thinking about using a Factory to distil that complexity and bring order to your code.