3

I am a beginner to DDD and have a question on how to implement to authorization invariants for a particular domain use case.

I have two subdomains: Membership and Identity. Identity handles authentication and managing user and roles.

The subdomain in question is Membership. Members can have a number of statuses. When activating a member, there are three invariants:

  1. Chapter Administrators can only activate a member in their chapter
  2. Chapter Administrators can only activate inactive members.
  3. System Administrators can activate any member from any status.

Users have roles. The roles for this situation are System Administrator and Chapter Administrator(for a single chapter).

So I have an application service. User Id is stored in .Net Identity, but I feel like keeping the application service unaware of .Net Identity is a good idea?

public void ActivateMember(UserId userId, MemberId memberId)
{
     //This handles invariants 1 & 3
     memberAccess.DoesUserHaveAccessToMember(userId, memberId);

     //But how to I handle 2?

     //here is the call into the domain
     commands.Handle(new ActivateMember(memberId);
}

How to handle 2? The command handler is a domain service that just loads the member, calls its activate member and persists it back. Should authentication services from the Identity domain be pushed that far down? I could implement 2 in the above class, but then I have to load the member twice from the repository. Is that bad?

Constantin Galbenu
  • 16,951
  • 3
  • 38
  • 54
Luke Bailey
  • 129
  • 1
  • 4
  • Possible duplicate of [Access Control in Domain Driven Design](https://stackoverflow.com/questions/23464697/access-control-in-domain-driven-design) – guillaume31 May 30 '18 at 11:44

1 Answers1

2

Identity handles authentication and managing user and roles

So, Identity handles authentication and authorization; having roles is just implementation details that should be hidden from other bounded contexts. This means that the Membership BC should not care how the authorization works, just that it works. So, in order to hide this, the Authorization BC should publish an interface like this: canUserActivateMember(userId,memberId).

Now, the tricky part is that in both the BCs there is the notion of members but it means something else:

  • in the Membership BC, members contain (a lot of) properties and behavior specific to this domain, like ID, Name, Status, Gender, entering/leaving a club, whatever is relevant in this domain
  • in the Authorization BC, members contain only ID, Chapter and Status, with no behavior. The Status property is synchronized by an Anti-corruption layer from the Membership BC (in a cron or whatever).

So, your ActivateMember service from Membership BC should look like this:

public void ActivateMember(UserId userId, MemberId memberId)
{
     //This handles invariants 1, 2 & 3
     if(!authorization.canUserActivateMember(userId,memberId)) {
         throw ExceptionOrSomething;
     }

     //here is the call into the domain
     commands.Handle(new ActivateMember(memberId);
}

In the Authorization BC, the method canUserActivateMember could look like this:

public boolean canUserActivateMember(UserId userId, MemberId memberId)
{
    var user = userRepository.load(userId);
    var member = memberRepository.load(memberId);

    if(user.isSystemAdministrator()){
        return true;
    }

    if(user.isChapterAdministrator() && member.hasChapter(user.getChapter)){
        return true;
    }

    if(user.isChapterAdministrator() && member.isInactive()) {
        return  true;
    }

    return false;//the default  
}

So, you have two Member classes, one in each BC, but with different properties and behavior.

Constantin Galbenu
  • 16,951
  • 3
  • 38
  • 54