Let’s take an example where we have two entities: ServiceProvider and TelephoneNumber. The system allows service providers to add telephone numbers. Each telephone number added by a service provider is represented by an instance of TelephoneNumber. So, I have an association between these two entities. For now, I’ve decided that a TelephoneNumber instance is associated to exactly one service provider. I also decided that these two entities belong to the same aggregate with ServiceProvider being the aggregate root (one argument is that the TelephoneNumber lifecycle depends on the ServiceProvider entity).
My question is about the traversal direction of the association. I know in advance that a service provider may add many millions of telephone numbers.
If the traversal direction of the association is from ServiceProvider to TelephoneNumber, how to implement in an efficient way? Storing a set of telephone numbers in the ServiceProvider entity would be inefficient (especially for memory). Lazy loading may be used but it would limit the implementation options by using frameworks that support it (e.g. Hibernate).
In a known example where we have Customer and Order entities, a unidirectional association from Order to Customer is often chosen. If the traversal direction of the association is from TelephoneNumber to ServiceProvider, how to implement the CRUD operations for TelephoneNumber? I guess we need to use a TelephoneNumberRepository? But, I often saw that repositories should be defined only for aggregate roots since we generally get the aggregate root instance and we communicate with the domain model through it.
Thanks
Since you need to access phone numbers according to some criteria, I wouldn’t be shocked to see a PhoneNumberRepository even if it’s not an Aggregate root.
On the other hand, you could have the appropriate FindPhoneNumberBy…() methods directly in ServiceProvider. What bothers me there is that you can’t load a huge number phone numbers in memory at once, which means you would have to introduce some kind of performance management (lazy loading/enumeration, etc.) in ServiceProvider, breaking the separation of concerns.
The advantage of a separate Repository is that while the interface would be in the Domain layer, its concrete implementation would reside in the Infrastructure layer, which nicely decouples the contracts of FindPhoneNumberBy…() methods from the underlying fetching tricks. All in all, I can’t really see a more legitimate candidate than a Repository to handle that kind of stuff.