-1

I ran into the situation where I have to decide whether a property belongs to my aggregate or not. My domain logic is the following. A User has a group in his organization ex: IT, SALES, MARKETING. Depending on which group this user belongs to, he will have a set of Permissions. The logic of which group has which permissions is defined in the Website Policies. A Policy is a mapping between a group and a set of permissions. ex: group IT has permissions to view post, edit post and delete posts.

I have 2 aggregates: User and Website(we are managing multiple websites)

The question is: Should the permissions be part of the user aggregate?

It is easy and somehow makes sense to have the permissions in the user aggregates so it can be requested any time. But the problem I see with this is that the user aggregate root will not be able to satisfy this invariant:"A user should have the permissions that are defined for the groups that he/she belongs to" and since the permissions are transient data and they are not being persisted I think it should not be part of the User AR.

The other option would be to have a domain service or a method in the website AR that returns the permissions for a specific user. And then use this method whenever the permissions are needed.

13
  • you shouldnt have a User object at all
    – Ewan
    Commented Jun 1, 2018 at 14:35
  • 1
    @Ewan there is a lot of data related to user. User can also perform action like adding a comment or rate a post. Why do you think it shouldn't exist? Commented Jun 1, 2018 at 14:44
  • check out my answer on softwareengineering.stackexchange.com/questions/367659 and similar questions, Authentcation and Authorisation should not be in your domain
    – Ewan
    Commented Jun 1, 2018 at 14:55
  • @ewan: That's a clearer statement than your first one. Every system I've ever worked on has some sort of user records in it. Commented Jun 1, 2018 at 14:55
  • 1
    @mohamed: People new to DDD sometimes focus too much on the structure of DDD, and not enough on solving business problems by writing code. DDD has less to do with writing code than it does with providing a common platform by which developers can collaborate with business stakeholders. DDD is especially useful if the business domain is large, which is not the case in your question. How would you accomplish this if you weren't using DDD? Commented Jun 1, 2018 at 14:57

1 Answer 1

1

Simply put, No, an Aggregate should not depend upon some piece of state for which it is not the single, authoritative, and unambiguous representation. The real issue here is that the question you are asking is orthogonal to the problem you are trying to solve.

You may be able to reorganize your current domain in a way that might satisfy your requirements by introducing concurrent state machines where each state (ITGroupMember, etc) is the direct entity responsible for executing your actions and each User is given a Membership collection or the like, but this kind of solution will undoubtedly lead to problems down the road and is, frankly, much more complex than necessary.

Generally speaking, authorization/authentication are cross-cutting concerns that don't lend themselves well to being modeled using DDD principals. Just laying out the requirements for such a system should make this clear:

In abstract terms, in order to authorize any given command in your domain, that command must be either 1) injected with or 2) wrapped by an extra piece of logic, say an AuthorizationContext, such that Permissions data can be made available to that command for validation. In practice, the most suitable solution is to wrap commands because it leads to lower coupling (the commands themselves needn't know anything about authorization).

In DDD terms, authorization could take either of the above forms. You could inject every entity with your AuthorizationContext so that each entity method has access to the data necessary for authorization (which is about what you have landed on and are asking about here), or you could treat each entity as a state machine where each state represents a particular set of permissions (in this way wrapping your method calls). Neither of these solutions are particularly elegant and both introduce extra coupling as well as move the focus of your model away from executing business logic. As such, I simply cannot recommend either of the above methods.

The most traditional approach will be to just "raise" authorization up above your domain layer and authorize commands before they execute. This may take the form of thin service layer. This is BY FAR the most common case. Nearly every web framework I've encountered does authorization/authentication via some sort of middleware service that is invoked before any domain code executes. Most use some sort of data annotations put directly onto controller methods. You could simply take a page from this playbook and implement something similar (this is essentially just a global configuration mapping commands to roles).

If this must be done according to DDD, we need to have a broader discussion. A cornerstone of DDD is refactoring towards deeper insight and making the implicit explicit. Remember, the goal of DDD is to model the behavioral requirements of a system such the result is a useful abstraction of the business domain. The key is to focus on the behavior of a system, not the data. This is because the data of a given system is rarely a good starting point for modelling the functional requirements. The single biggest problem I am seeing in your domain is the inclusion of a User entity. I would bet my first-born grandson that the only reason a User entity exists in your domain is because you have a [user] table/document/whatever in your data store.

User is usually a poor choice for a domain object because it denotes almost no behavior and probably encompasses too much knowledge. What do your Users do? Comment? Commenter. Rate? Rater. Review? Reviewer. You see where I'm going here? You need to slice your data vertically to group related data together according to behavior. Maybe a Commenter has a PastCommentCollection. Does a Rater need a PastCommentCollection? No. The boundaries of normalization are very unlikely to be the boundaries of all of your entities.

I know what your thinking. How does partitioning the User entity solve my particular problem regarding authorization? And to that, I'll just leave you with a question:

What if RaterRepository.find( userId ) would throw a RaterNotFoundException for each userId that could not be related to the appropriate permissions necessary to rate something?

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