Steve Maine continues his very interesting series about a DDD style here, here, here and here. Steve Eichert does the same here and here. As I said in my previous post, my current favorite style is a bit different to this which is why I want to describe some differences again since it will help me challenge the solution I'm using now.
- The life cycle of my entities is a bit different to how I understand Steve's to be. My entities start their lives in a Factory
class (when needed), that is, a DDD Factory, which means that the main purpose is to encapsulate the creation knowledge. What is
created is a transient entity instance, with nothing being said about persistence as yet.
Then, in my case, the way to signal the instance for persistence is to call AddX(instance) on the right repository. The repository then registers the instance with the Unit of Work (UoW). The instance will now be persisted to the database at the next commit call to the right UoW.
The alternative way of signaling for persistence is to hook the instance to a parent instance, such as adding a new OrderLine to an Order. I use persistence by reachability and therefore the newly created OrderLine will be persisted when the UoW which the Order belongs to is committed the next time.
- My Factory classes are only creating new instances which haven't been saved before. It is up to my repositories to reconstitute old instances from the database, at least when it comes to the Domain Model. Most of the real work is done by the infrastructure.
- I see Lazy Load as being something technical, and it is not important for the Domain Model itself. On the other hand I see
Repositories as being part of the Domain Model. What I mean is that Lazy Load isn't about capturing knowledge about the domain,
rather it's just a performance thing. I mean, it's for creating the illusion that the instances are around when you need them,
but without the cost when you don't need them. Sure, this illusion creates some nasty problems, but let's not go there now. That's
why I think it's OK to leave Lazy Load to the infrastructure, even if it bypasses the Repositories.
Another way to see this is in regards to Eager Load. Assume it's time to think in sets, such as when you want to fetch a list of Orders and all the OrderLines for each Order, but you don't want to Lazy Load the OrderLines in this particular case. Instead now you want to Eager Load the OrderLines. Hopefully the infrastructure will only make at most two roundtrips to the database to fulfill that request, instead of making one call to the database for each Order to fetch the OrderLines of that Order.
Let's say instead you go for an Eager Load strategy that goes via the "GetById" calls to the repositories in the Domain Model. This could be when the Orders are fetched and then we call, for instance, GetOrderLinesByOrderId() for each and every Order on an OrderRepository. In this particular case that solution would be extremely costly. Sure, I might have stretched it a bit, but you get the picture.
- In my previous post about DDD style I talked about having a UoW spanning several repositories/aggregates. I do find having
such a "global" UoW a bit troublesome.
One idea is to let each repository have its own UoW. This would create a less coupled Domain Model (and then my Repositories would probably also be closer to how I think Eric describes them). All navigation between aggregates would have to go via repository methods, but that's fine, I think. The saving could be governed by an über-UoW which talks to each "real" UoW. Is this too fragmented? What's your take?
There wouldn't be transactional semantics for the über-UoW, but I think it "might" be OK with no realtime validity between aggregates, mightn't it? Hmmm… This feels almost SO-ish, one aggregate per SO-service, hmm... OK, this is something I need to look into later on, but for now I'll continue with my current style...