38

I am working on a small application trying to grasp the principles of domain-driven design. If successful, this might be a pilot for a larger project. I'm trying to follow the book "Implementing Domain-Driven Design" (by Vaughn Vernon) and trying to implement a similar, simple discussion forum. I've also checked out the IDDD samples on github. I have some difficulties adopting the Identity and Access to my case. Let me give some background information:

  • I (hopefully) understand the reasoning behind separating the users and permissions logic: it is a supporting domain, and it's a different bounded context.

  • In the core domain, there are no users, just Authors, Moderators, etc. These are created by reaching out to the Identity and Access context using a service and then translating the received User objects to and Moderator.

  • Domain operations are called with a related role as a parameter: e.g.:

    ModeratePost( ..., moderator);

  • The method of the domain object checks if the given Moderator instance is not null (the Moderator instance will be null if the user asked from the Identity and Access context does not have the Moderator role).

  • In one case, it does an additional check before altering a Post:

    if (forum.IsModeratedby(moderator))

My questions are:

  • In the latter case aren't the security concerns blended again into the core domain? Previously the books states "with who can post a subject, or under what conditions that is permitted. A Forum just needs to know that an Author is doing that right now".

  • The role based implementation in the book is fairly straightforward: when a Moderator is the core domain tries to convert the current userId into a Moderator instance or into an Author when it needs that. The service will respond with the appropriate instance or a null if the user does not have the required role. However, I can't see how could I adapt this to a more complex security model; our current project I'm piloting for has a rather complex model with groups, ACLs, etc.

Even with rules that are not much very complex, like: "A post should be edited only by its Owner or an Editor", this approach seems to break down, or at least I don't see the correct way to implement it.

By asking the Identity and Access context for an OwnerOrEditor instance doesn't feel right, and I would end up with more and more security-related classes in the core domain. In addition, I would need to pass not just the userId, but the identifier of the protected resource (the id of the post, forum, etc.) to the security context, which probably should not care about these things (is it correct?)

By pulling the permissions to the core domain and checking them in the methods of the domain objects or in the services, I'd end up at square one: mixing security concerns with the domain.

I've read somewhere (and I tend to agree with it) that these permission related things should not be a part of the core domain, unless security and permissions are the core domain itself. Does a simple rule like the one given above justify making security a part of the core domain?

5
  • Maybe you can find what you need here : stackoverflow.com/a/23485141/329660 Also, just because the Access Control context knows about a resource ID doesn't mean it has domain knowledge about what kind of entity that resource is or what it does. Commented Mar 14, 2018 at 15:53
  • Thanks, I've seen that post earlier, my problem is exactly what the edit says at the end: I would like to move access control out of my core domain but I feel I've hit a wall with my implementation. However, your suggestion about the resource ID makes sense: as I don't use the concept of User or Role in the core domain but concrete roles, maybe I could use the concept of Resource in the security BC and map them to the related concrete domain concept. Worth a try, thanks! Commented Mar 14, 2018 at 16:05
  • Don't the code samples in the link at least answer to "I can't see how could I adapt this to a more complex security model"? Commented Mar 14, 2018 at 16:30
  • My problem is not with the implementation of the security model itself, I can't see how should I map these more complicated rules into the domain. How should the User -> Author mapping change if it is not a simple role based model on the security side? Passing resource IDs to the other context might work, like HasPermissionToEdit(userId, resourceId) but I doesn't feel right to contaminate the domain logic with these calls. Probably I should check these in the application service methods, before invoking the domain logic? Commented Mar 14, 2018 at 16:49
  • Of course it should be in application services... I thought it was clear from parts of the code like UserService @AccessControlList[inf3rno] in the answer I linked to. Commented Mar 15, 2018 at 8:10

3 Answers 3

7

It's sometimes difficult to distinguish between real access control rules and domain invariants borderlining on access control.

Especially, rules that depend on data only available far into the course of a particular piece of domain logic might not easily be extractible out of the domain. Usually, Access Control is called before or after a domain operation is performed but not during.

Vaughn Vernon's assert (forum.IsModeratedBy(moderator)) example probably should have been outside the Domain, but it is not always feasible.

I would need to pass not just the userId, but the identifier of the protected resource (the id of the post, forum, etc.) to the security context, which probably should not care about these things (is it correct?)

If there's a Security BC and you want it to handle that logic, it doesn't have to know what a Forum is in detail but :

  • It could just have knowledge of concepts like "moderated by" and grant or deny access rights accordingly.
  • You could have adapter logic that subscribes to Core Domain events and translates them to simple (resource, authorizedUsers) key value pairs for the Security BC to store and use.
5
  • As both answers are useful and more or less are pointing to the same direction I have upvoted them both. I accepted this one, as @guillaume31 has more or less answered my question about Vernon's implementation and I'll continue with my implementation based on his hint about using resources in the Security BC. Commented Mar 15, 2018 at 12:07
  • I have to say I think this in the complete opposite of my answer.
    – Ewan
    Commented Mar 15, 2018 at 12:14
  • 1
    Maybe I'm too confused by now, but my interpretation was (for both answers): 1. Keep security concerns out of the domain and use the security BC as a service 2. Call the service before invoking any domain objects 3. The service will do the mapping from users/acls to moderators, authors, etc. moderator = securityService.GetModerator(userId, forumId) 4. Domain logic will be implemented in these objects like in moderator.EditPost() 5. Methods like EditPost will know nothing about security concepts, there will be no additional checks there Commented Mar 15, 2018 at 12:58
  • I'm still looking for answers/direction on this myself, but I've found that any authorization logic which relies on the current state of the object (such as if it's currently assigned to a moderator) is in fact business logic which belongs in your domain and further that if it is not in your domain you risk ending up in an invalid state if the model can be updated concurrently. For instance if you validate ownership using a policy and then go to update that object - in many domains that ownership is subject to change and the action may no longer be valid.
    – Jordan
    Commented Nov 29, 2019 at 19:43
  • Unless you have a very complex collaborative context you can probably afford to implement an optimistic concurrency model here using versioning, but if your checks aren't done within or at least against a particular aggregate instance then your checks may fail to be consistent with the actual state of the object by the time you persist your changes.
    – Jordan
    Commented Nov 29, 2019 at 19:45
16

I have run into the same questioning and am posting the answer I have come up with because it might give some additional vision on the topic, even though the discussion took place a while ago (still second link on the topic in Google).

I think there are two different kinds of access controls at play:

  • Role validation in terms of authentication scheme
  • Action validation in business terms

Knowing if a particular user is a Moderator belongs to the Identity & Access bounded context. That's because it has to do with the rights the user has - its category.

But

Knowing if a moderator can edit a post belongs to the business domain, and should be out of the Identity & Access bounded context. A good way to see that is that the condition is expressed without referring to the targeted user: it is a business rule that a moderator can or cannot edit another moderator's post for example. It should not pollute the Identity & Access BC.

It becomes especially clear when you want to apply this reasoning to scopes in an OAuth setting. Now in addition to auth rules, you also have to authorize actions based on whether specific scopes are included or not in the token request. Where do you put that?

I'd say you put that in the business BC. That's because the scopes semantics are known to the business domain. Having a specific role and knowing what it allows to do are two different matters. First part is in the I&A BC, second part is in the business domain.

And it feels right: if suddenly you want to forbid topic edition by a moderator, you should have to modify the forum BC, not the I&A BC - nothing has changed to roles definition really, it's just their semantics in a specific BC that changed.

That's why I think it is the best solution to have the check if (forum.IsModeratedby(moderator)) in the business BC. I guess I disagree with the accepted answer on that matter.

If many rules like that exist for a specific business BC, it seems to me that the best way is to bundle them in a subdomain in the business BC. Business rules like that can benefit from being embodied as an object to call (like a Strategy pattern).

10
  • How would you recommend providing the user permissions/scopes from your application or API layer to your domain? A parameter for the domain method, perhaps?
    – rhyek
    Commented Jul 21, 2021 at 5:40
  • 1
    In my app, I put together a concept of passport issued by the I&A BC. The passport of the requesting user can be retrieved through an exposed I&A service and holds what I call visas. Visas are looked for and intepreted in the business BC depending on their semantics. I didn't implement scopes actually, but I would add them to the passport so that the business BC can get them when it needs to check for their presence.
    – Qortex
    Commented Jul 21, 2021 at 8:05
  • 1
    Are your visas self-contained objects the higher levels provide to the domain layer? I recently implemented something similar. Application layer generates an AuthorizationDomainObject (via an AuthorizationService). it implements an interface defined in the Domain Layer. this object contains relevant info (permissions, etc) that business logic can parse when needed.
    – rhyek
    Commented Jul 22, 2021 at 13:43
  • 1
    Yes, I understand it's indeed a similar approach. Pretty handy & extensible in my experience.
    – Qortex
    Commented Jul 26, 2021 at 19:20
  • Nice answer! About "A good way to see that is that the condition is expressed without referring to the targeted user", does that mean a method Post::isUpdatableByUser(User $user) would be wrong?
    – Kwadz
    Commented Apr 27, 2022 at 2:46
15

Authentication and Authorisation is a bad example for DDD.

Neither of these things are part of a Domain unless your company creates security products.

The Business or domain requirement is, or should be, "I require role based authentication"

You then check the role before calling a domain function.

Where you have complex requirements such as 'I can edit my own posts but not others' ensure that your domain separates the edit function out into EditOwnPost() and EditOthersPost() so that you have a simple function to role mapping

You can also separate the functionality into Domain Objects, such as Poster.EditPost() and Moderator.EditPost() this is a more OOP approach, although your choice may depend on whether your method is in a Domain Service or a Domain Object.

However you choose to separate the code the Role mapping will occur outside of the Domain. so for example if you have a webapi controller:

PostController : ApiController
{
    [Authorize(Roles = "User")]
    public void EditOwnPost(string postId, string newContent)
    {
        this.postDomainService.EditOwnPost(postId, string newContent);
    }

    [Authorize(Roles = "Moderator")]
    public void EditOtherPost(string postId, string newContent)
    {
        this.postDomainService.EditOtherPost(postId, string newContent);
    }
}

As you can see although the role mapping is done on the hosting layer, the complex logic of what constitutes editing your own or an others post is part of the domain.

The domain recognises the difference of the actions, but the security requirement is simply that "functionality can be limited by roles".

This is perhaps clearer with the domain objects separation, but essentially you are checking the method which constructs the object instead of the method which calls the service method. Your requirement, if you still want to voice it as part of the domain would become 'only moderators can construct the moderator object'

35
  • 6
    Checking role "statically" is a little bit simplistic. What if a moderator is not allowed to edit the post of another moderator? Shouldn't this check be part of the domain? Commented Dec 22, 2018 at 11:41
  • 4
    @RédaHousniAlaoui I'm wondering about this as well. I can't think of a way to handle this other than either including some mention of Users/Moderators in the domain, or performing some kind of logic within that ApiController to get the post's author's role. Neither of these seems right, and this is a common enough sort of thing in my experience that some clear guidance would be extremely helpful.
    – Jimmy
    Commented Mar 28, 2019 at 14:22
  • 4
    @Erwan, the use case I am talking about is dynamic. Basing the sentence "Authentication and Authorisation is a bad example for DDD" on hello world examples is dishonest. DDD is here to avoid accidental complexity and allow to manage domain complexity. Dynamic permissions is not an accidental complexity nor something that does not happen in real life. Commented Mar 28, 2019 at 15:16
  • 3
    IMHO, the issue with your solution is that it does not satisfy the customer. The customer often want to be able to change those relations dynamically. Moreover, that's also what happens when a vendor provides the same enterprise software to different companies. If the software is poorly tweak-able, the vendor will eventually die. Commented Mar 28, 2019 at 15:27
  • 3
    I have no other way than ask questions to understand the design you are defending. If you don't want to answer questions, maybe you are on the wrong site :D Commented Mar 28, 2019 at 16:26

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