Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I'd like to start Domain-Driven-Design, but there are several problems I'd like to solve before starting :)

Let's imagine I have a Groups and Users and when user wants to join a group, I'm calling groupsService.AddUserToGroup(group, user) method. In DDD I should do group.JoinUser(user), which looks pretty good.

The problem appears if I there are some validation rules for adding a user, or some external tasks need to be started when user is added to the group. Having these tasks will lead to the entity having external dependencies.

An example could be - a restriction that user can only participate in 3 groups max. This will require DB-calls from inside group.JoinUser method to validate this.

But the fact that an Entity depends on some external services/classes doesn't seem so good and "natural" to me.

What's the proper way to deal with this in DDD?

share|improve this question

2 Answers 2

Let's imagine I have a Groups and Users and when user wants to join a group, I'm calling groupsService.AddUserToGroup(group, user) method. In DDD I should do group.JoinUser(user), which looks pretty good.

But DDD also encourages you to use (stateless) services to perform tasks, if the task at hand is too complex or would not fit into an entity model. It's ok to have services in the domain layer. But the services in the domain layer should only include business logic. External tasks and application logic (like sending an email) on the other hand, should use the domain service in the application layer, in which you could have a separate (application-)service wrapping it for example.

The problem appears if I there are some validation rules for adding a user ...

The validation rules do belong to the domain model! They should be encapsulated inside the domain objects (entities etc.).

... or some external tasks need to be started when user is added to the group. Having these tasks will lead to the entity having external dependencies.

While I don't know what kind of external task you're talking about, I assume it's something like sending an email etc. But this is not really part of your domain model. It should live in the application layer and be hanlded there imho. You can have a service in your application layer which operates on domain services and entities to perform those tasks.

But the fact that an Entity depends on some external services/classes doesn't seem so good and "natural" to me.

It is unnatural and shouldn't be happening. The entity should not know about stuff which is not its responsibility. Services should be used to orchestrate entity interactions.

What's the proper way to deal with this in DDD?

In your case, the relationship should probably be bidirectional. Whether the user joins the group or the group takes the user depends on your domain. Does the user join the group? Or is the user added to a group? How does it work in your domain?

Anyway, you have a bidirectional relationship and can thus determine the amount of groups the user already belongs to within the user-aggregate. Whether you pass the user to the group or the group to the user is technically trivial once you have determined the responsible class.

The validation should then be performed by the entity. The whole thing is called from a service of the application layer which also can do technical stuff, like sending emails etc.

However, if the validation logic is really complex, a domain service might be a better solution. In that case, encapsulate the business rules in there and then call it from your application layer.

share|improve this answer
    
But if we move so much logic outside of entity, what should be kept inside? –  Idsa Aug 13 '11 at 18:55
    
The direct responsibilities of the entity! If you can say "the user can join a group" for example, then it is a responsibility of the user entity. Sometimes you have to make tradeoff decisions for technical reasons. I am not a big fan of bidirectional relationships either, but sometimes it fits the model best. So listen carefully when talking about the domain. "An entity does..." "The entity can..." When you hear such sentences, then those operations do most likely belong to the entity. –  Falcon Aug 13 '11 at 19:04
    
Further, you know you need a service when two or more otherwise unrelated objects need to participate in a task to accomplish something. –  Falcon Aug 13 '11 at 19:09
    
Thanks for your answer, Falcon! Btw, I always tried to use stateless services, so I'm one step closer to DDD :) Let's say that in a domain this UserJoinsToGroup operation belongs to Group. The problem is, that to validate that operation I need to know in how much groups that User already participates (to deny an operation if it's already > 3). To know that I need to query the database. How can I do that from Group entity? I've some more examples, when I need to touch the DB in the operations that should naturally belong to entity (I'll post them if needed :)) –  Shaddix Aug 14 '11 at 5:17
1  
Well, if I think about it: What about a GroupMembership entity? It can be constructed by a factory and this factory may access respositories. That'd be good DDD and encapsulates the creation of memberships. The factory may access Repositories, creates a Membership and than adds it to the user and the group respectively. This new entity could also encapsulate privileges. Maybe that's a good idea. –  Falcon Aug 14 '11 at 8:07

The way I would approach the problem of validation is this way: Create a Domain Service called MembershipService:

class MembershipService : IMembershipService
{
   public MembershipService(IGroupRepository groupRepository)
   { 
     _groupRepository = groupRepository;
   }
   public int NumberOfGroupsAssignedTo(UserId userId)
   {
        return _groupsRepository.NumberOfGroupsAssignedTo(userId);
   }
}

The Group entity needs to be injected with IMemberShipService. This can be done at the class level or the method level. Lets assume we do it at the method level.

class Group{

   public void JoinUser(User user, IMembershipService membershipService)
   {
       if(membershipService.NumberOfGroupsAssignedTo(user.UserId) >= 3)
         throw new BusinessException("User assigned to more than 3 groups. Cannot proceed");

       // do some more stuff
   }
}

The Application service: GroupService can be injected with IMemberShipService using Constructor injection, which it can then pass to JoinUser method of the Group class.

share|improve this answer
1  
You might want to consider formatting the source code in your post for readability –  Benjamin Rogge yesterday

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.