How do you persist your Aggregate using Entity Framework? Using a Repository to get/set your Aggregate Root? I see a lot of examples that bend over backward to make their Entities expose behaviors while try to encapsulate data. It doesn’t need to be difficult. And if you’re not using an ORM, what’s a way that you can capture the changes made by the Aggregate Root so that you can persist them writing SQL?
Check out my YouTube channel where I post all kinds of content that accompanies my posts including this video showing everything that is in this post.
The most common examples I see are creating an Aggregate using Entity Framework entities. In the very simplest of use-cases, I think it can work. However, I’ve watched/read enough content that makes me shake my head about the hoops people will jump through to make their data persistence model also be their domain model. This is often because they are trying to hide the actual data or conform to how EF needs the entity to behave.
They don’t need to be the same object.
Your domain model is about exposing behaviors and encapsulating data.
If you’re not exposing data from an aggregate, meaning there’s no way for callers/consumers to get data from it (think CQRS), then you can encapsulate your data model inside your aggregate root. Your data model at that point is your Entity Framework entities. Your aggregate root is your aggregate.
Separate Aggregate & Data
This is very simple but allows you to use EF exactly how it’s intended and create an Aggregate Root that simply exposes behaviors and encapsulate your data model (EF).
First, let’s start with our Entity Framework Entities, which are basically now just our data model. They contain no behavior. Simply a data model used for persistence.
Next, all of our behavior goes into a separate class that takes the ShoppingCart as a parameter in the constructor. We’ll manipulate this data model in all of our behavior methods. We don’t ever expose the data model to any outside caller/consumer.
Finally, we use a repository to Get and Save a ShopingCartDomain.
There are a lot of opinions about Repositories. My opinion is they should be for constructing and saving your behavior only aggregate. This generally means you’ll only ever have 2 methods: Get() and Save()
The repository will get the data using Entity Framework, then construct a new instance of our Aggregate and pass in the data model.
Aggregates (Root) without an ORM
If you’re not using an ORM like Entity Framework, but still want to create a domain with behavior then the ultimate problem is change tracking. With an ORM, it’s doing the change tracking of knowing which properties on your entities changed that it needs to persist to your database.
This is what needs to be implemented (change tracking) if you’re not using an ORM.
To do so, I like to implement this using events to represent state changes.
In our behavior methods, we’re recording and keeping track of them in the _events member. We’re also taking a parameter that represents the data model of the current state that our repository will build us.
When we want to save our state changes, our repository will iterate through the events, then have the appropriate SQL statements for them.
Expose Behavior and Encapsulate Data
If you’re doing all sorts of odd things to make your ORM be a data model that also contains behaviors, just separate the two. If you’re not using an ORM, it doesn’t mean you can’t create aggregates. Use events as a method of change tracking and implement the SQL statements for each event that represents a state change.