Data access is contextual, a generic approach will fail
In my previous post, I discussed the following scenario, a smart client application for managing Machines, Parts & Maintenance History:
From the client perspective, Machine,Parts & Maintenance History where all part of the same entity, and they wanted to be able to be able return that to the user in a single call. They run into several problems in doing that, mostly, from what I could see, because they tried to do something that I strongly discourage, sending entities on the wire so they could be displayed on the smart client application. The client uses CSLA, so the actual business logic is sitting in Factory objects (I discussed my objection to that practice here), but the entities are still doing triple duty here. They need to be saved to the database, serve as DTOs on the wire and also handle being presented in the UI.
When I objected to that practice, I was asked: “Are we supposed to have three duplicates of everything in the application? One for the database, one for the wire and one for the UI? That is insane!”
The answer to this question is:
This is the symbol for Mu, which is the answer you give when the question is meaningless.
The problem with the question as posed is that it contains an invalid assumption. The assumption is that the three separate models in the application are identical to one another. They aren’t, not by a long shot.
Let me see if I can explain it better with an example. Given the Machine –>> Part –>> Maintenance History model, and an application to manage those as our backdrop. We will take two sample scenarios to discuss, the first would be to record replacing a part in a machine, and the second would be looking at a list of machines that need fixing.
For the first scenario, we need to show the user the Maintenance screen for the machine, which looks like this:
And on the second one, machines requiring fixes, we have:
Now, both of those screens are driven by data in the Machine –>> Part –>> Maintenance History model. But they have very different data requirements.
In the first case, we need the Machine.Name, Machine.NextMaintenanceDate, and a list of Part.SKU, Part.Name, Part.Broken, Part.Notes.
In the second case, we need Machine.Owner, Machine.Car, Machine.Age, Machine.Preferred.
The data set that they require is completely different, not only that, but sending the full object graph to each of those is a waste. If you noticed, no one actually even needed Maintenance History for any of those screens. It is likely that Maintenance History will be only needed rarely, but by saying that there is only one model that needs to serve all those different scenarios, we are tying ourselves into knots, because we need to serve Maintenance History every time that we are asked for a machine, even though it is not needed.
The way I would design the system, I would have:
Entities: Machine, Part, Maintenance History
This is the full data model that we use. Note that I am not discussing issues like CQRS here, because the application doesn’t warrant it. A single model for everything would work well here.
DTO: MachineHeaderDTO, MachineAndPartsDTO, PartAndMaintenanceHistoryDTO, etc.
We have specific DTOs for each scenario, only giving us the data that we need, so we can spare a lot of effort in the server side in deciding how to fill the data.
View Models
The view models would be built on top of the DTOs, usually providing some additional services like INotifyPropertyChanged, calculated properties, client side validation, etc.
I am in favor of each screen having its own View Models, because that lead to a self contained application, but I wouldn’t have a problem with common view models shared among the entire application.
Sample application
And because I know you’ll want to see some code, the Alexandria sample application demonstrate those concepts quite well: http://github.com/ayende/alexandria
Comments
You end up with many DTOs, then ?
If you display Machines on 3 slightly different pages would you end up with 3 MachineDTOs ? Would you than end up with 3 different APIs to retrieve this 3 MachineDTOs ?
Is so, you are than creating your API to serve specific view requirements, iow your API is not generic ?
Or you would have one API set for UI layer and another for unknow 3rd party application clients for integration ?
Petar,
DTOs are stupid classes that you can write in a heartbeat.
Simple, single use, API is easy to work with.
Yes, you end up with a large body of simple, easy to understand, easy to work with code.
That is a GOOD thing
Yes, I fully agree with you arguments. Just wanted to confirm my understanding.
Thank you very much for this post. It is enlightening.
Sounds also like the client is trying to use one CSLA class per db entity .. the intention with CSLA afaik is to use one (or more) CSLA class(es) per use case..
What do you suggset when your forced to use an ActiveRecord DAL, you have to bring back everthing which is a pain. Would love to use something like nhibernate ORM or SubSonic, but DBA and team leader very much against it.... :(
Tom,
I wouldn't, I find ActiveRecord too limiting in real world scenarios.
Okay, that makes sense, but you still have a lot of duplication on the property level, don't you?
Imagine you want to add a new property to the machine entity (e.g. manufacturer name). You may have 3 screens that need to display this new property. So to add only 1 property you have to add the poperty to the machine entity, to the database, 3 DTO classes and possibly another 3 view models. And you need some kind of mapping between Entity, DTO and View Model, so the property data is transferred properly between the layers.
Only looking at the database and entities it's probably handled by an ORM, but what do you use to fill DTOs and View Models?
Thomas,
And?
For mapping between entities & dtos, you can use a tool like AutoMapper.
But more to the point, so you got 6 properties to write.
Time to do that? 3 minutes?
Remember that you have to also modify all three screens, which is going to take a lot longer, so this is not even noise.
What you gain is drastically simpler architecture and easier to work with system.
Depending on the use cases you may not need to modify all the screens ;-)
Stuart,
In fact, that is the main point, you usually don't need to do so.
For that reason I advocate working on separate view models for each screen, and dto per scenario.
Considering the way we usually work, the work is already done per screen, so adding a new prop is a no op, essentially.
I went through the same thoughts 1,5 years ago. I was using MVC with the ViewData dictionary but once I found out that it literally takes a minute to write the view model class I never looked back. It just doesn't cost that much to do it right.
What would be the problem with ActiveRecord? I don't get it.
Writing a specific DTO class is the easiest part.
Copying the data between objects requires AutoMapper or a similar tool that uses reflection which might be not acceptable with some clients. Doing mappings by hand would require a lot of stupid code. You would also have to duplicate numerous data validation rules and attributes so you can validate object data at the UI level.
I agree that from the application maintenance point of view, separate models make it easier.
Alberto,
I'll discuss that in a future post
Great, I can't wait to read it. Although it seems I will have to be patient, two weeks of post waiting already. :D
Using DTOs is, and always has been a good idea for expressing information between persistence and UI. Sure there are technologies that pop up that say it's no longer necessary, but the fact is that DTOs are simple, easy to maintain, and suited to their single purpose.
Sure, you'll get technical staff that grumble at the thought of having two or three different DTO classes to express "mostly" the same information. But I'd rather deal with them grumbling about spending a few extra minutes customizing a DTO than telling customers they can't do something a bit different on one screen because the data isn't arranged that way, or adding complexity to suit a specific change, or risking introducing an ugly bug. (Plus dealing with the performance cost of sending more data than I need to for the sake of consistency.)
It's like a tug of war between DNRY and SRP, IMO SRP should always win. A DTO should be responsible for servicing it's view, and only it's view. If it can service 2 views without any conditional changes or complexity, great. But as soon as that changes, the DTO gets subclassed. Tidy.
The hardest part is coming up with expressive and accurate names for your DTOs without using stupid suffixes like '2' and 'Data'.
The CSLA framework does not restrict from the design you seem to prefer.
I frequently use CSLA with DTOs. CSLA objects are my equivalent of the View Model containing the authorisation, validation rules and change notification. The CSLA and DTO objects contain only the information they need to complete the scenario for which they are designed.
Ayende - totally agree with the concept here. I use Agatha to support this type of architecture. One difference is the way I do queries (read-only requests). Instead of loading domain model entities and building DTOs from them, I just maintain a view in the db and have a specific readonly model that I query against (using NH of course). This makes the queries super simple, at the cost of having to maintain two sets of mappings. Thoughts?
David,
If your app requires this, it is an excellent approach, certainly!
Comment preview