views:

1000

answers:

4

Suppose you have the canonical Customer domain object. You have three different screens on which Customer is displayed: External Admin, Internal Admin, and Update Account.

Suppose further that each screen displays only a subset of all of the data contained in the Customer object.

The problem is: when the UI passes data back from each screen (e.g. through a DTO), it contains only that subset of a full Customer domain object. So when you send that DTO to the Customer Factory to re-create the Customer object, you have only part of the Customer.

Then you send this Customer to your Customer Repository to save it, and a bunch of data will get wiped out because it isn't there. Tragedy ensues.

So the question is: how would you deal with this problem?

Some of my ideas:

  • include an argument to the Repository indicating which part of the Customer to update, and ignore others

  • when you load the Customer, keep it in static memory, or in the session, or wherever, and then when you receive one of the DTOs from the UI, update only the parts relevant to the DTO

IMO, both of these are kludges. Are there any other better ideas?

@chadmyers: Here is the problem.

Entity has properties A, B, C, and D.

DTO #1 contains properties for B and C.

DTO #2 contains properties for C and D.

UI asks for DTO #1, you load entity from the repository, convert it into DTO #1, filling in only B and C, and give it to the UI.

Now UI updates B and sends the DTO back. You recreate the entity and it has only B and C filled in because that is all that is contained in the DTO.

Now you want to save the entity, which has only B and C filled in, with A and D null/blank. The repository has no way of knowing if it should update A and D in persistence as blanks, or whether it should ignore them.

+3  A: 

Is this a web app? Load the customer object from the repo, update it from the DTO, save it back. That doesn't seem like a kludge to me. :)

UPDATE: As per your updates (the A, B, C, D example)

So what I was thinking is that when you load the entity, it has A, B, C, and D filled in. If DTO#1 only updates B & C, that's OK. A and D are unaffected (which is the desired situation).

What the repository does with the B & C updates is up to him. If you're using Hibernate/NHibernate, for example, it will just figure it out and issue an update.

Just because DTO #1 only has B & C doesn't mean you have to also null out A & D. Just leave them alone.

chadmyers
Doesn't matter if it is a web app or not. And the problem is there isn't "the" DTO, but different ones, and none of them completely describe the Customer, just a part of it.
moffdub
It does matter, because if it's stateful, you might use a different approach than the partial DTO thing and that's what I was going to explore. And it doesn't matter whether the DTO completely describes it, it doesn't need to.
chadmyers
You're right, one option is to re-design the DTOs. I failed to mention the constraint that the design of the DTOs are probably out of my hands.Are you suggesting that, instead of a seperate DTO class, that the Customer object implement a DTO interface?
moffdub
No, I didn't say redesign the DTO (for a non-web app, you might use a completely diff approach, though). I'm saying: Your DTO's don't need to map 1:1 with your entities, in fact, they shouldn't. But you shouldn't try to make a whole entity from the DTO either
chadmyers
I'm not sure what the problem is any more. Just load the entity from the repo, apply any changes from the DTO that need applied, save the entity again. Is that not working for you, or are loading entities too expensive or something?
chadmyers
Please see the question for an example. It didn't fit in 300 characters.
moffdub
@moffdub Ok, I think I'm getting closer to understanding. I've updated my answer. I think you may be missing the part about "it doesn't have to null out the other fields"
chadmyers
It sounds like you're talking about a DTO that is just an interface that the entity implements. Either that, or you're suggesting keeping the loaded entity in a cache of sorts so you can only update B and C when the updated DTO comes back. Am I in the ballpark?
moffdub
+4  A: 

I would use factory to load a complete customer object from repository upon receipt of DTO. After that you can update only those fields that were specified in DTO.

That also allows you to apply some optimistic concurrency on your customer by checking last-updated timestamp, for example.

Damir Zekić
Interesting approach. You are, though, trading some performance because you have two round-trips to the Repository instead of one.
moffdub
Beware of optimizing above the repository level because you're allowing DB optimization thinking to leak through the repository abstraction. You can apply 2nd level caching or other such techniques later if performance becomes an issue
chadmyers
Beware of optimizing your code before you even get your code working. Besides, if you need every nanosecond of throughput, why are you using objects?
Justice
You're right, Justice. I've been known to be a pre-optimizer. I'm trying to quit.
moffdub
+1  A: 

I missed the point of this question at first because it is predicated on a few things that I don't think make sense from a design perspective.

  1. Hydrating an entity from repository and then converting it to a DTO is a waste of effort. I assume that your DAL passes a DTO to your repository which then converts it to a full entity object. So converting it back to a DTO seems wasteful.

  2. Having multiple DTOs makes sense if you have a search results page that shows a high volume of records and only displays part of your entity data. In that case it's efficient to pass that page just the data it needs. It does not make sense to pass a DTO that contains partial data to a CRUD page. Just give it a full DTO or even a full entity object. If it doesn't use all of the data, fine, no harm done.

So that main problem is that I don't think you should pass data to these pages using partial DTOs. If you used a full DTO, I would do the following 3 steps whenever the save action is performed:

  1. Pull the full DTO from repository or db
  2. Update the DTO with any changes made through the form
  3. Save the full DTO back to the repository or db

This method requires an extra db hit but that's really not a significant issue on a CRUD form.

+1  A: 

If we have an understanding that a Repository handles (almost exclusively) very rich domain Entity, then you numerous DTO's could simply map back.

i.e.

dtoUser.MapFrom<In,Out>(Entity)
or
dtoAdmin.MapFrom<In,Out>(Entity)

you would do the reverse to get the dto information back to the Entity and so on. So your repository only saves rich Entity's NOT numerous DTO's

entity.Foo = dtoUser.Foo
or
entity.Bar = dtoAdmin.Bar

entityRepsotiry.Save(entity) <-- do not pass DTO.

The whole point of DTO's is to keep things simple for the presentation or say for WCF dataTransfer, it has nothing to do with the Repository or the Entity for that matter.

Furthermore, you should never construct an Entity from DTO's... the only two ways to ever acquire an Entity is through a Factory(new) or a Repository(existing) respectively.

You mention storing the Entity somewhere, why would you do this? That is the job of your repository. It will decide where to get the Entity(db,cache,e.t.c), no need to store it somewhere else.

Hope that helps assign responsibility in your domain, it is always a challenge and there are gray area's here and there but in general, these are the typical uses of Repository, DTO e.t.c.

5x1llz