So for DDD folks there, Aggregate Roots are supposed to contain business logics only and exposed what is needed only.
In DDD Red Book by Vaugh Vernon, he used LevelDB and Hibernate as examples. LevelDB is key-value storage and Hibernate I think uses reflection.
However, if I don't want to use any of those, how am I going to save Aggregates?
3 of the easiest solutions I can think of are listed in the title:
- Exposed public getters
- Reflection
- Inject repository to aggregate and have a method called save (Memento pattern?)
Let's imagine Payments:
- CardPayment with cardNumber, expiration, cardHolderName
- CashPayment with cashAmount
- PaypalPayment with paypalAccountId
Each of those has their own unique properties but adheres to an abstract class/interface (won't go deeper for simplicity).
In my whole life, there are cases like this that can't be avoided especially when doing Repository where you really need to know what are you going to save.
Going with public getters, you might need to do an instanceOf checks in repository so you can cast and access the unique properties.
Going with reflection, it may not be a problem but feels like a hack…
Injecting repositoryObj to a save
method seems to be the next best option, at least the Aggregate knows what properties to save but this violates DDD I think. It knows about persistence too much and save
is not part of ubiquitous language.
I can be pragmatic and eat a cake but I want to know how it is done the pure OOP and/or DDD way.
EDIT:
Found an article from Vaughn Vernon on how to model Aggregates with Entity Framework. The article can be applied to anything else too, it's not really specific to EF. I'll just link to it to prevent longer O.P: https://kalele.io/blog-posts/modeling-aggregates-with-ddd-and-entity-framework/
Best Answer
Instead of updating the O.P, I will just post this as an answer so I can mark it as one.
I found the book Patterns, Principles, and Practices of Domain-Driven Design by Nick Tune, Scott Millett.
To quote from Chapter 21: Repositories:
A simple method to enable the persistence of domain objects is to ensure that all properties have public getters and setters. The problem is that this leaves your domain objects open to being left in an invalid state because clients can bypass your state-altering methods. You can avoid this through code reviews and governance. However, exposing your properties does make it easy to persist your domain model because your repository has easy access to the domain object’s state.
If you don’t want to expose your domain model’s properties and want them to be totally encapsulated, you can utilize the memento pattern. The Gang of Four pattern lets you restore an object to a previous state. The previous state in this instance can be stored in a database. The way in which it works is that an aggregate produces a snapshot of itself that can be persisted. The aggregate would know how to hydrate itself from the same snapshot. It’s important to understand that the snapshot is not the data model; it’s merely the state of the aggregates, which again is free from any persistence framework. The repository would still have to map the snapshot to the data model.
Another way to persist your domain model is to use an event stream. Event streams are similar to the memento pattern, but instead of taking a snapshot in time of your aggregate, your repository persists all the events that have occurred to the aggregate in the form of domain events. Listen for events from the domain, and map them to a data model. Again, you need a factory method to reconstruct and replay these events to rebuild the aggregate.
And in the end