Implementing Associations With References and Repositories in Domain-Driven Design (DDD)
Domain-Driven Design places great emphasis on modeling the domain and representing the model in code and the ubiquitous language. A model is an abstraction of reality which preserves aspects interesting for solving a particular problem. Consider the stereotypical order model consisting of a sales order, line items and a customer. The problem is trivial - storing and managing order data and the aspects that are interesting to this problem are confined to a narrow perspective of the entirety of what an order and a customer is. The model of a sales order may contain an order number, a date, a shipping address and finally line item details such as price and quantity. Expressing every aspect of an order is of course prohibitive and defeats the utility of the model.
A primary method for implementing associations is with object references. For example, a customer class may reference an address class which allows calling code to traverse the relationship obtain a customer’s address. Another method for expressing relationships is with a repository. Repositories can express relationships by providing access to associated entities by means of a database search. Eric Evans states:
Whether to provide a traversal or depend on a search becomes a design decision, trading off the decoupling of the search against the cohesiveness of the association. Should the Customer object hold a collection of all the Orders placed? Or should the Orders be found in the database, with a search on the Customer ID field? The right combination of search and association makes the design comprehensible.
Making the decision between employing a reference or a repository is an art of compromise. Furthermore, neither can serve as a substitute for the other in all cases and thus the developer is faced with managing two very different implementation approaches. The important thing to remember is that both methods share a common goal of reflecting the model. A simple way to make the determination is by asking whether a reference is required to support behavior. Often times a reference is used to implement a relationship in order to fulfill a display requirement alone. This can lead one onto a precarious path resulting in the entity becoming increasingly convoluted and intractable. Instead, a repository can be used to implement the association keeping the entity lean. Additionally, the read-model pattern presents a suitable alternative for fulfilling display requirements. The benefits of event sourcing in the context of DDD further promote the notion of keeping entities behavior centric.
Both references and repositories are pertinent implementations of associations in the model. References are inherently cohesive and are thus best suited for associations which are required for behavior or maintenance of integrity. Repositories are best suited for associations which can or must be decoupled from the entity. For example, a customer may be associated with a collection of orders. This collection may become arbitrarily large thus demanding filtering capabilities. Moreover, in a typical scenario, the only behavior in the customer class dependent on the collection of orders is the creating of new orders. The creation of new orders can be just as easily accommodated with a factory and a repository. Repositories can also be utilized instead of lazily loaded references because lazy loading can be problematic.