DDD - Modelling the Domain - Factories
In Object-Oriented programming there are many design patterns to create objects that were mainly popularised by Gamma et al (1995). It should be stressed out that “favouring object composition over class inheritance” (Gamma et al, 1995) is central to Domain-Driven Design which raises the importance of the creational patterns, hereafter referred to as Factories (Evans, 2003). In short, Factories are objects that have a single responsibility i.e. to encapsulate the logic of creating other objects (Evans, 2003).
Often the construction of domain objects is too complex and this is especially true for aggregates (Evans, 2003). For example, consider a Computer aggregate with one or more CPUs, RAM chips, hard disks etc. Should the Computer know how to create its own CPUs and RAM? Calling the constructors of CPU and RAM inside the Computer's constructor is a sign that an independent Factory needs to be employed to shift the responsibility of assembling complicated objects (Evans, 2003).
According to Evans (2003), there are 2 main ways of employing factories when designing the domain model: as a Factory Method and as a standalone Abstract Factory object (Gamma et al, 1995). An aggregate could provide a Factory Method to instantiate an object that is not part of the aggregate after it is created (Evans, 2003, Vernon 2013). As a case in point, consider a ShoppingCart aggregate that contains a list of products. To create a PurchaseOrder we need the contents of the cart. Instead of fetching and processing the contained products directly, we could employ the method ShoppingCart.create_order_for(customer) that instantiates and returns a PurchaseOrder object for a particular customer and enforces any invariants (Vernon, 2013). Furthermore, factory methods on aggregates could be used to hide implementation details from the client e.g. the method PurchaseOrder.add(product, quantity) could instantiate an OrderItem enforcing any aggregate integrity rules (Evans, 2003). A standalone Abstract Factory object is generally used to provide "an interface for creating families of related or dependent objects without specifying their concrete classes" (Gamma et al, 1995). In this case, the Factory object should create and return a reference to the whole aggregate (e.g. a Computer) including any depending objects (e.g. CPU, RAM) and enforcing any business logic (Evans, 2003). Moreover, a Factory object could be used in reconstituting domain objects from the data store (Evans, 2003). At the same time, Factories should not be overused, especially when an object's construction is not complex.
References
- Evans, E. (2003) Domain-Driven Design: Tacking Complexity In the Heart of Software. Boston: Addison-Wesley.
- Gamma, E., Helm, R., Johnson, R. & Vlissides, J. (1995) Design Patterns: Elements of Reusable Object-Oriented Software. New York: Addison-Wesley.
- Vernon, V. (2013) Implementing Domain-Driven Design. Boston: Addison-Wesley.
This post is an excerpt of my MSc Thesis titled "Applying Domain-Driven Design in Python - designing a self-hosted Read-it-Later service" (January 2014).