9

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?

  • 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. – guillaume31 Mar 14 '18 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! – LittlePilgrim Mar 14 '18 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"? – guillaume31 Mar 14 '18 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? – LittlePilgrim Mar 14 '18 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. – guillaume31 Mar 15 '18 at 8:10
3

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.
  • 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. – LittlePilgrim Mar 15 '18 at 12:07
  • I have to say I think this in the complete opposite of my answer. – Ewan Mar 15 '18 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 – LittlePilgrim Mar 15 '18 at 12:58
5

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'

  • This answer is spot on. Authentication and Authorization are simply outside the concern for most DDD systems. I would only add here, that it may be preferable to, instead of just functionally mapping roles to different methods, create new Entities that are responsible for privileged actions (Visitor vs RegisteredUser vs AdminUser for a web-based example). Conceptually, I find this approach easier to understand. I also have found that it provides a cleaner separation of actions/validations. – king-side-slide Mar 14 '18 at 16:33
  • I agree with you that Authentication and Authorization are outside the core domain. However, they must be implemented somehow, as the are critical to business. I might use a prepurchased/open source component, but my problem is exactly this: how can I communicate with this supporting/generic subdomain without the concepts bleeding into the core domain? – LittlePilgrim Mar 14 '18 at 16:40
  • After the edits (and digesting the pieces) I think I'm starting to see your point. My question was more about where the role mapping/permission checking should happen. Author, Moderator, etc. are in the ubiquitous language, while User and Role are not: I need to do the mapping. If I'm not mistaken, you suggest doing this outside the domain objects and services, even before calling the service methods. On the other hand, wouldn't this (moving this code out of the application services) result in code duplication if there are multiple clients (e.g. a REST API and a web client?). – LittlePilgrim Mar 14 '18 at 19:08
  • 2
    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? – Réda Housni Alaoui Dec 22 '18 at 11:41
  • 1
    @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 Mar 28 at 14:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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