Continuing on from these debates:
DDD - the rule that Entities can't access Repositories directly
Is it ok for entities to access repositories?
There are still some situations where it feels better for the Domain to access the repository. Take this example, which assumes I need a TaskStatus table in the database which contains a description for reporting purposes:
public class TaskStatus
{
public long Id {get;set;}
public string Description {get;set;}
}
public class Task
{
public long Id {get;set;}
public string Description {get;set;}
public TaskStatus Status {get;set;}
public void CompleteTask()
{
ITaskStatusReposity repository = ObjectFactory.GetInstace<ITaskStatusReposity>(); //Or whatever DI you do.
Status = repository.LoadById(Constants.CompletedTaskStatusId);
}
}
I know I could have CompletedTaskStatus and OpenTaskStatus objects but there are situations where this would be unnecessary and could lead to a class explosion.
And anyway, why are repository interfaces stored in the Domain, if not for this sort of thing?
Is it ok for Entities to access Repositories?
No. Please don't do this.
Firstly the idea of the Domain Model is to separate out the business logic from the application. Isolate it from your db, repositories and your application. This allows you to keep the business logic separate and to allow it to be tested and changed separate from your application.
The Domain should be completely unaware of data persistence, and should assume that it happens automagically.
ddd-the-repository-pattern.aspx
Secondly there's other more practical reasons not to inject your repositories into your enties.
Your entites should be unit testable, by injecting your repositories into your entity you've created a dependency on your repository.
Using a GetInstance() method breaks the Law of Demeter, you are creating a tight coupling of ITaskStatusRepository to your entity. Meaning that when creating a new Task and writing unit tests it is not obvious opon construction that a Task requires a ITaskStatusRepository. This makes it more difficult to unit test your business logic.
From a DDD standpoint, a repository is not only concerned with interfacing with the DB, it could be retrieving from an in memory store. Or a List.
Your repository does not have to be a 1 to 1 relationship with tables. If you need your task repository to perform inner joins with other tables to perform complex queries and it returns a list of Task items, then you expose a method from your repository that performs that query. (I think this is a common misconception of the repository pattern).
Your entities should not be concerned with performing actions on the db.
Refer to the image posted here:
DDD: how the layers should be organized?
public class TaskStatus
{
public long Id { get; set; }
public string Description { get; set; }
public TaskStatus() {
Description = "Incomplete";
}
}
public class Task
{
public long Id {get;set;}
public string Description {get;set;}
public TaskStatus Status {get;set;}
public void CompleteTask()
{
Status.Description = "Complete";
}
}
Inside the application layer, your repository is responsible for persistence (or not). A repository is a List<> of aggregate roots. And it works at the aggregate root level.
An example of using your Tasks inside a TaskService. A service acts upon Entities from your Application Layer.
public class TaskService
{
private readonly ITaskRepository _taskRepository;
public TaskService(ItaskRepository taskRepository){
_taskRepository = taskRepository;
}
public List<Task> CompleteAllTasks()
{
List<Tasks> getTasks = _taskRepository.GetTasks();
getTasks.ForEach(CompleteTask);
return _taskRepository.Save(getTasks);
}
}