Ayende @ Rahien

It's a girl

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:

image_thumb

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:

image

And on the second one, machines requiring fixes, we have:

image

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

Petar Repac
08/06/2010 10:14 AM by
Petar Repac

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 ?

Ayende Rahien
08/06/2010 10:20 AM by
Ayende Rahien

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

Petar Repac
08/06/2010 10:39 AM by
Petar Repac

Yes, I fully agree with you arguments. Just wanted to confirm my understanding.

Thank you very much for this post. It is enlightening.

Stuart
08/06/2010 11:05 AM by
Stuart

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..

Tom
08/06/2010 11:11 AM by
Tom

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.... :(

Ayende Rahien
08/06/2010 11:14 AM by
Ayende Rahien

Tom,

I wouldn't, I find ActiveRecord too limiting in real world scenarios.

Thomas Krause
08/06/2010 11:41 AM by
Thomas Krause

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?

Ayende Rahien
08/06/2010 11:45 AM by
Ayende Rahien

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.

Stuart
08/06/2010 11:52 AM by
Stuart

Depending on the use cases you may not need to modify all the screens ;-)

Ayende Rahien
08/06/2010 11:54 AM by
Ayende Rahien

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.

tobi
08/06/2010 01:44 PM by
tobi

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.

alberto
08/06/2010 03:20 PM by
alberto

What would be the problem with ActiveRecord? I don't get it.

Dmitry
08/06/2010 03:24 PM by
Dmitry

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.

Ayende Rahien
08/06/2010 03:49 PM by
Ayende Rahien

Alberto,

I'll discuss that in a future post

alberto
08/06/2010 04:35 PM by
alberto

Great, I can't wait to read it. Although it seems I will have to be patient, two weeks of post waiting already. :D

Steve Py
08/07/2010 06:28 AM by
Steve Py

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.

Christer Nyberg
08/09/2010 07:42 AM by
Christer Nyberg

The hardest part is coming up with expressive and accurate names for your DTOs without using stupid suffixes like '2' and 'Data'.

Ed James
08/09/2010 04:22 PM by
Ed James

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.

David W Martines
08/29/2010 02:45 PM by
David W Martines

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?

Ayende Rahien
08/29/2010 03:19 PM by
Ayende Rahien

David,

If your app requires this, it is an excellent approach, certainly!

Comments have been closed on this topic.