I received an interesting question via email recently that I figured would be better answered via a blog post than a private response.
Greg,
<clip of intro and flattery>
So you are getting messages directly out of your repositories and sending command messages directly to them for persistence.
There’s one thing that hit me when thinking about your approach: You do not seem to have a UI with a master-detail connected with this “model”, as it sounds very transactional. Further sending a bunch of messages just as dta for a single view does not make sense. Do you also have to deal with such UI stuff and how do you support it? I guess with another synced model?
This is a great question as it really jumps into how things work architecturally. This becomes rather obvious in the talk I have been doing as there are diagrams but has not been talked about in detail on this blog.
Command and Query Separation
The first thing that should be discussed here is that repositories are not responsible for handling "read" messages. Repositories have a single reading operation and that is to load a single instance of an aggregate root based upon it's unique identifier.
CustomerRepository::Get(Guid Id)
This is because our domain is only responsible for handling *Command* (write) operations. All other *Query * (reading) operations are delegated off to another "thin" bit of code. But this "thin" bit of code is important and deserves to be talked about more because it gets into the second question discussed here.
The UI Mapping
In most systems the creation of DTOs for viewing of data by the UI is handled by some code that interacts with repositories then maps domain objects to the DTOs. This process is often quite inefficient as the fetching paths are different for reading in this way than when one would want to execute a transaction on a domain object. In DDDD this is not done but instead there are separate "providers" (repositories is a bad term since they only handle reads) in another layer that handle the mapping of DTOs for the UI. It is important to note that this is a very thin layer that basically reads from a data model and maps directly to the DTOs (there are no intermediate domain objects).
It is important that we pause for a moment and look at what these "DTOs" are. In an ideal world these DTOs map directly to how the UI wants to use the data. They will often times have no correlation to how the domain model is laid out (it is quite possible that 3-4 aggregate roots make up a single DTO for the UI). I say that this is in an "ideal" world because there is also a cost associated with the creation of screen specific DTOs. What if I have 5 clients that show similar but slightly different information? Do I create 5 slightly different methods? Probably not because the reason I want them to map up is to minimize the number of round trips I am getting. The law of diminishing returns applies here...
For things like "searches" that are lists (the master generally in the master-detail). The DTOs also match up the screen (summary information and an id). It is important to note that we are not searching the same model (if we are storing commands we don't have a current snapshot). The model that we are searching will tend to be an eventually consistent snapshot denormalized version of the model but does not *have* to be eventually consistent (we could in a system of small scale require the write all the way through to the denormaliztion before returning to the caller (2pc) thus creating full consistency). It is important to note that the unique identifier of each item is included.
Knowing this let's take a look at a master-detail screen. I come to a screen where I search customers by their first name ...
I type "greg".
My UI sends up a request to my thin read layer let's call it a PerformCustomerSearchByFirstNameQuery with the first name of "Greg"
My thin read layer returns a set of PerformCustomerSearchByFirstNameQueryResult to my UI (note that there are probably other things going on here like paging).
Now when I want to drill in. I would in the UI take the id that I wanted and ask for the full DTO for that object note that if the object has 7 tabs, I am probably getting all 7 tabs of data at once). Generally the rule is to prefer big chunky messages sent less often here but occasionally lazy loading from a client model can have value (just remember to measure!)
While in the detail view my UI would build commands representing the changes the user was making within the detail (bound to the unique aggregate root id of the item I am editing). When complete the user would press save and these commands would be dispatched to my "write only" domain model.
This pattern is called event sourcing and is not new although I think it has been discovered hundreds of times :). Martin Fowler has a write up on it here.
Begging the question...
Sometimes the complexity that we are trying to encapsulate in our domain is "reading" code. How do we encapsulate this logic and avoid duplicating it between the domain and the "reading" layer. An example of this might be a business rule and a search that both use a complex derived "score" for a customer. How can we prevent duplicating all of the logic in the two models ...
next time on this blog...
Posted
Tue, Jan 13 2009 12:49 PM
by
Greg