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