How to retrieve domain objects on indirect references? (when an entity has the Id of another entity, instead of a direct reference)

StackOverflow https://stackoverflow.com/questions/22769912

Question

Vaughn Vernon has a series of pdfs that suggests to use indirect references to link related entities together. This way, Entity A would have a list of Entity B Ids instead of references to them. This avoids performance and scalability issues and also let's you focus on consistency boundaries.

My problem comes with traversing such a type of indirect reference to be able to perform domain logic.

Say I have Group, User and Authorizations. All three are entities and also their own aggregate. They can each be modified simultaneously from one another, and therefore are not part of a consistency boundary. They relate to each other in the way that Users are in Groups and Groups are Authorized for things.

I need domain logic that retrieves me the list of things a User is Authorized to. Ideally, I feel like this logic should be on the User as User.GetAuthorizations(); But I'm willing to move this somewhere else if it feels more appropriate.

The problem is that, no matter where I put this logic I will need to:

  1. Get the Groups of a User.
  2. For each of the user's groups, get the given Authorizations.
  3. Perform the merge logic of those Authorization.
  4. Return the Authorizations as they fit the User.

How should I therefore implement User.GetAuthorizations(); in a DDD context which uses Repositories for data access?

By this I mean that I want to know how I should retrieve the indirect reference and traverse it inside my method.

Here is an example:

public class UserApplicationService
{
    public IEnumerable<Authorization> GetUserAuthorizations(string userId)
    {
        User user = _userRepo.Find(userId);
        IEnumerable<Group> groups = _groupRepo.FindMany(user.GroupIds);

        List<Tuple<Group, List<Authorization>>> groupAuths = new List<Tuple<Group, List<Authorization>>>()
        foreach(Group group in groups)
        {
            List<Authorization> auths =  _authorizationRepo.FindMany(group.AuthorizationIds).ToList();
            Tuple<Group, List<Authorization>> groupAuth = new Tuple<Group, List<Authorization>>(group, auths);
            groupAuths.Add(groupAuth);
        }

        return user.GetAuthorizations(groupAuths);
    }
}

IEnumerable<Authorization> User.GetAuthorizations(List<Tuple<Group, List<Authorization>>> groupAuths)
{
    // merge logic would be here
}

In this case, I retrieve in an Application Service the groups of the User and the Authorizations of all of those groups and I pass it to the User.GetAuthorizations method. I find this quite cumbersome, and if the relationship would be nested deeper, it would become even more cumbersome.

I wonder what other approach would there be that respect the DDD approach? How is this normally done?

Was it helpful?

Solution

Time has passed since asking this, and I've furthered my understanding to the point where I feel confident in answering this myself.

I need domain logic that retrieves me the list of things a User is Authorized to. Ideally, I feel like this logic should be on the User as User.GetAuthorizations(); But I'm willing to move this somewhere else if it feels more appropriate.

This is where lies the entire problem of my question, I want retrieval logic, and I'm mistakenly thinking it must go in my domain.

In DDD, you have a domain model that tries to encapsulate the ubiquitous language of the application, but only for purposes of data change. Those entities, value objects and aggregate roots should encapsulate transactional context.

When you want to get information, you bypass all that. Query your data directly, and get exactly the info you want in the form you need.

The problem is that, no matter where I put this logic I will need to:

Get the Groups of a User.

Perform the merge logic of those Authorization.

Return the Authorizations as they fit the User.

How should I therefore implement User.GetAuthorizations(); in a DDD context which uses Repositories for data access?For each of the user's groups, get the given Authorizations.

This was missing the point. The proper way to do this is have an application service that has a method GetAuthorizations, like in my example, but this method should never use the Repository and never use the Aggregate Roots User, Group and Authorization. Instead, it should use a second abstraction, which is a query layer for retrieval of persistence data, which returns it's own type of entities that better match the query. So GetAuthorizations could retrieve objects of type AuthorizedUser. It would implement the merge logic as part of the querying process.

OTHER TIPS

First of all - make sure that customer actually needs grouping of users for authorization rules definition. If not, your approach is an overkill and consider defining authorization rules in static fashion.

Another approach would be to make UserAuthorization entity that gets properly mutated for changes like 'user group has changed', 'authorization deleted' etc. Then it would be simple as asking authorization rules list for a user.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top