Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DDD, Repository, & Encapsulation

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?

like image 839
n8wrl Avatar asked Jul 17 '09 14:07

n8wrl


People also ask

Why is repository in domain layer?

Repository interface belongs to the domain layer since it plays the roles of defining the operations on Entity required for implementing business logic (Service).

Are repositories part of the domain layer?

Yes, repository implementations can definitely be a part of your domain model.

Can domain services use repository?

Yes, a domain service can access repositories.

What is repository in design pattern?

Repository pattern separates the data access logic and maps it to the business entities in the business logic. Communication between the data access logic and the business logic is done through interfaces. To put it simply, Repository pattern is a kind of container where data access logic is stored.


1 Answers

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.

like image 89
Jeff Sternal Avatar answered Sep 23 '22 01:09

Jeff Sternal