views:

759

answers:

2

I apologize in advance if folks think this has been beaten to death. I've just spent the last few hours searching and reading many excellent posts here in SO but I'm still confused.

The source of my confusion is DTO vs. DDD and repositories. I want my POCO domain objects to have the smarts, and I want to get them from repositories. But it seems like I have to violate some encapsulation rules to make that work, and it seems like it can turn DTO's onto their heads.

Here's a simple example: In our catalog application, a Part could be a package that includes a number of other parts. So, it makes sense for the Part POCO to have a 'GetChildren()' method which returns IEnumerable< Part >. It might even do other stuff with the list on its way out.

But how is that list resolved? Seems like the repository is the answer:

interface IPartRepository : IRepository<Part>
{
    // Part LoadByID(int id); comes from IRepository<Part>
    IEnumerable<Part> GetChildren(Part part);
}

And

class Part
{
    ...
    public IEnumerable<Part> GetChildren()
    {
        // Might manipulate this list on the way out!
        return partRepository.GetChildren(this);
    }
}

So now the consumers of my catalog, in addition to (correctly) loading parts from the repository, can also bypass some Part-encapsulated logic by calling GetChildren(part) directly. Isn't that bad?

I read that repositories should serve up POCO's but that DTO's are good for transferring data 'between layers.' A lot of part properties are computed - prices, for example, are calculated based on complex pricing rules. A price won't even be in a DTO coming from a repository - so it seems like passing pricing data back to a web service requires the DTO to consume the Part, not the other way around.

This is already getting too long. Where is my head unscrewed?

+1  A: 

One approach that solves this problem is to move the logic into the child parts themselves - that is, change the semantics of the class so that Part objects are responsible for transforming themselves when they are associated with a parent.

For example, if the price of a Part is dependent on its parent Part, the price can be determined at the following times (at a minimum):

  • Upon construction, if a parent Part (and all other necessary data) is available.

  • In an AttachToParent(Part parentPart) method or in response to an event, i.e., OnAttachedToParent(Part parentPart).

  • When it is needed by client code (i.e., on the first access of its Price property).


Edit: my original answer (below) really wasn't in the spirit of DDD. It involved making domain objects simple containers, a design considered by many to be an anti-pattern (see Anemic Domain Model).

An additional layer between the Part and IPartRepository (I'll call it IPartService) solves this problem: move GetChildren(part) into IPartService and remove it from Part, then make client code call IPartService to get Part objects and their children rather than hitting the repository directly. The Part class still has a ChildParts (or Children) property - it just doesn't know how to populate it itself.

Obviously, this imposes additional costs - you may end up writing or generating a lot of pass-through code for repository calls if you don't need extra business logic in most cases.

Jeff Sternal
Interesting. But I'm confused by 'move GetChildren(part) into IPartService and remove it from part' and then 'The Part class still has a Childparts property.' What if part needs to massage its children for some reason?
n8wrl
If the massaging needs to happen immediately when you retrieve it from the repository, I would put that logic in the `IPartService.GetChildren()`. If you need to be able to modify the child parts at arbitrary times, you could make another service method, such as `IPartService.UpdateChildPartPrices(Part part).` (Or both - you might call `UpdateChildPartPrices` from `GetChildren`.)
Jeff Sternal
Thanks for taking the time, Jeff!
n8wrl
I hope you get more answers, it's a vexing, interesting question. I've updated my answer to account for the controversial nature of original suggestion (and acknowledge its liabilities).
Jeff Sternal
A: 

The missing part of the equation here is the behaviour of the Parts object, and how you want to work with the aggregate. Do you need to work on individual children of each Part to the nth recursion, or do you only ever work with a "root" Part (ie those with no parents) and it's children as a whole?

Having a Part aggregate root that contains a list of fairly generically typed Parts as children seems like it wouldn't express your domain model particularly well, but you could do it and recursively lazy load each child collection. However, I'd still be very careful where infinite recursion is possible.

As to your second concern, DTOs aren't for transferring data between layers as much as they are for transferring data into and out of your application layer.

They are very useful if you're using a service oriented architecture (you mention web services, but it can be any SOA). Your services will query your repositories, do any extra work, and then map your domain objects into flat DTOs to send back to the requesting clients. DTOs should be simple, contain no logic and application function specific with the intention of being serialized.

Use your domain objects inside your application, and DTOs outside.

Mike Gardiner