【问题标题】:Domain Entities Accessing Repositories访问存储库的域实体
【发布时间】:2011-09-28 00:42:51
【问题描述】:

继续这些辩论:

DDD - the rule that Entities can't access Repositories directly

Is it ok for entities to access repositories?

在某些情况下,域访问存储库感觉更好。以这个例子为例,假设我需要数据库中的 TaskStatus 表,其中包含用于报告目的的描述:

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);
    }
}

我知道我可以拥有 CompletedTaskStatus 和 OpenTaskStatus 对象,但在某些情况下这是不必要的,并且可能导致类爆炸。

无论如何,如果不是为了这类事情,为什么存储库接口存储在域中?

【问题讨论】:

    标签: architecture domain-driven-design repository-pattern ddd-repositories


    【解决方案1】:

    实体可以访问存储库吗?

    没有。请不要这样做。

    首先,域模型的思想是将业务逻辑从应用程序中分离出来。将其与您的数据库、存储库和您的应用程序隔离开来。这使您可以将业务逻辑分开,并允许将其与您的应用程序分开进行测试和更改。

    域应该完全不知道数据持久性,并且应该假设它是自动发生的。

    ddd-the-repository-pattern.aspx

    其次,不将存储库注入实体还有其他更实际的原因。

    1. 您的实体应该是可单元测试的,通过将您的存储库注入到您的实体中,您已经创建了对存储库的依赖项。

    2. 使用 GetInstance() 方法违反了得墨忒耳法则,您正在创建 ITaskStatusRepository 与您的实体的紧密耦合。这意味着在创建新任务和编写单元测试时,任务需要 ITaskStatusRepository 的构造并不明显。这使得对业务逻辑进行单元测试变得更加困难。

    3. 从 DDD 的角度来看,存储库不仅关注与 DB 的接口,还可以从内存存储中检索。或列表。

    4. 您的存储库不必与表是一对一的关系。如果您需要您的任务存储库执行与其他表的内部连接以执行复杂查询并且它返回一个任务项列表,那么您从存储库中公开一个执行该查询的方法。 (我认为这是对存储库模式的常见误解)。

    5. 您的实体不应关心对数据库执行操作。

    请参阅此处发布的图片:

    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";
        }
    }
    

    在应用层内部,您的存储库负责(或不负责)持久性。存储库是聚合根的 List。它适用于聚合根级别。

    在 TaskService 中使用您的任务的示例。服务作用于应用层中的实体。

    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);
        }    
    }
    

    【讨论】:

    • 1 + 2:通过访问 ITaskStatusRepository,域不知道持久性的性质,尽管我接受它知道持久性的存在。当然它仍然是可单元测试的,因为可以模拟存储库吗?我仍然不确定您是如何建议我实现上述目标的?
    • 3:我不确定我的存储库除了访问数据库还做了什么? 4:好的,所以 TaskStatus 可以是一个枚举,你可以有类似 'public void Complete(){ Status = TaskStatus.Complete; }' 和 StatusDescription 应该是 Task 的属性?
    • 除非我弄错了,否则我不会真正接受“CompleteTask”是属于存储库的东西。我的例子是一个非常琐碎的例子,唯一的业务逻辑是当一个任务完成时,它的状态变为“完成”。这个逻辑可能要复杂得多,当然不应该在存储库中完成?
    • ddd-the-repository-pattern 。存储库可能负责也可能不负责持久性。如此有效,您的 Complete Task 操作根本不应该注入存储库。它应该直接更新任务状态。我会更新答案。
    • 使用您的新编辑后,我无法再从任务中访问任务状态的描述了吗?
    猜你喜欢
    • 2010-10-24
    • 2016-11-30
    • 1970-01-01
    • 2017-05-18
    • 1970-01-01
    • 2018-08-03
    • 2018-02-15
    • 2017-01-12
    相关资源
    最近更新 更多