Architecture – Accessing Repositories from Domain

Architecturedomain-driven-designdomain-modelentityrepository

Say we have a task logging system, when a task is logged, the user specifies a category and the task defaults to a status of 'Outstanding'. Assume in this instance that Category and Status have to be implemented as entities. Normally I would do this:

Application Layer:

public class TaskService
{
    //...

    public void Add(Guid categoryId, string description)
    {
        var category = _categoryRepository.GetById(categoryId);
        var status = _statusRepository.GetById(Constants.Status.OutstandingId);
        var task = Task.Create(category, status, description);
        _taskRepository.Save(task);
    }
}

Entity:

public class Task
{
    //...

    public static void Create(Category category, Status status, string description)
    {
        return new Task
        {
            Category = category,
            Status = status,
            Description = descrtiption
        };
    }
}

I do it like this because I am consistently told that entities should not access the repositories, but it would make much more sense to me if I did this:

Entity:

public class Task
{
    //...

    public static void Create(Category category, string description)
    {
        return new Task
        {
            Category = category,
            Status = _statusRepository.GetById(Constants.Status.OutstandingId),
            Description = descrtiption
        };
    }
}

The status repository is dependecy injected anyway, so there is no real dependency, and this feels more to me thike it is the domain that is making thedecision that a task defaults to outstanding. The previous version feels like it is the application layeer making that decision. Any why are repository contracts often in the domain if this should not be a posibility?

Here is a more extreme example, here the domain decides urgency:

Entity:

public class Task
{
    //...

    public static void Create(Category category, string description)
    {
        var task = new Task
        {
            Category = category,
            Status = _statusRepository.GetById(Constants.Status.OutstandingId),
            Description = descrtiption
        };

        if(someCondition)
        {
            if(someValue > anotherValue)
            {
                task.Urgency = _urgencyRepository.GetById
                    (Constants.Urgency.UrgentId);
            }
            else
            {
                task.Urgency = _urgencyRepository.GetById
                    (Constants.Urgency.SemiUrgentId);
            }
        }
        else
        {
            task.Urgency = _urgencyRepository.GetById
                (Constants.Urgency.NotId);
        }

        return task;
    }
}

There is no way you would want to pass in all possible versions of Urgency, and no way you would want to calculate this business logic in the application layer, so surely this would be the most appropriate way?

So is this a valid reason to access repositories from the domain?

EDIT: This could also be the case on non static methods:

public class Task
{
    //...

    public void Update(Category category, string description)
    {
        Category = category,
        Status = _statusRepository.GetById(Constants.Status.OutstandingId),
        Description = descrtiption

        if(someCondition)
        {
            if(someValue > anotherValue)
            {
                Urgency = _urgencyRepository.GetById
                    (Constants.Urgency.UrgentId);
            }
            else
            {
                Urgency = _urgencyRepository.GetById
                    (Constants.Urgency.SemiUrgentId);
            }
        }
        else
        {
            Urgency = _urgencyRepository.GetById
                (Constants.Urgency.NotId);
        }

        return task;
    }
}

Best Answer

You are intermixing

entities should not access the repositories

(which is a good suggestion)

and

the domain layer should not access the repositories

(which might be bad suggestion as long as your repositories are part of the domain layer, not the application layer). Actually, your examples show no case where an entity accesses a repository, since you are using static methods which don't belong to any entity.

If you don't want to put that creation logic into a static method of the entity class, you can introduce separate factory classes (as part of the domain layer!) and put the creation logic there.

EDIT: to your Update example: given that _urgencyRepository and statusRepository are members of the class Task, defined as some kind of interface, you now need to inject them into any Task entity before you can use Update now (for example in the Task constructor). Or you define them as static members, but beware, that could easily cause multi threading problems, or just problems when you need different repositories for different Task entities at the same time.

This design makes it a little bit harder to create Task entities in isolation, thus harder to write unit tests for Task entities, harder to write automatic tests depending on Task entities, and you produce a little bit more memory overhead, since every Task entity now needs to hold that two references to the reposities. Of course, that may be tolerable in your case. On the other hand, creating a separate utility class TaskUpdater which keeps the references to the right repositories may be often or at least sometimes a better solution.

The important part is: TaskUpdater will be still part of the domain layer! Just because you put that update or creation code in a separate class does not mean you have to switch to another layer.