实体可以访问存储库吗?

use*_*945 25 domain-driven-design ddd-repositories repository-pattern

我刚刚开始使用DDD,所以这可能是个愚蠢的问题......

是否可以让实体访问存储库(通过某个IRepository接口)在运行时获取值?例如,我想对属性强制执行"默认"选择:

class Person {
    private Company _employer;

    public Company Employer {
        get { return _employer; }
        set { 
            if(value != null) {
                _employer = value;
            } else {
                _employer = employerRepository.GetDefaultEmployer();
            }
        }
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)

我的问题是,做这样的事情是对DDD原则的可怕违反.如果不是,我的下一个问题是提供存储库使用的最佳方式是什么?是否应该在创建Person对象时提供?

谢谢,P

lom*_*axx 23

这不是对DDD的可怕违反,这是一个可怕的违反......好吧......它只是简单可怕(我说这个舌头在脸颊):).

首先,您的实体变得依赖于拥有存储库......这并不理想.理想情况下,您希望让您的存储库创建Person,然后为其分配在当前域上下文中有效的所有内容.

所以当你需要一个Person时,你会去personRepository.GetPersonWithDefaultEmployer()并找回一个拥有默认雇主的人.personRepository将依赖employeeRepository,并在返回之前使用它来填充此人.

PersonReposotory : IPersonRepository
{
    private readonly IEmployerRepository employerRepository;

    //use constructor injection to populate the EmployerRepository
    public PersonRepository(IEmployerRepository employerRepository)
    {
        this.employerRepository = employerRepository;
    }

    public person GetPersonWithDefaultEmployer(int personId)
    {
        Person person = GetPerson(personId);
        person.Employer = employerRepository.GetDefaultEmployer(personId);
        return person;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我很高兴得知实体知道存储库是一种不好的代码味道!就是说,我喜欢您上面创建新的Person对象的解决方案(实际上已经做了类似的事情),但是我如何处理在Person创建后更改雇主的情况?一种解决方案是将逻辑移到具有选择默认雇主的逻辑的单独的域服务类中。但是该服务仍需要访问Employer存储库-也许更可取? (2认同)

Iul*_*scu 5

你问的答案是标准的依赖.

根据经验,不要这样做.保持您的实体不引用存储库.[ 戴上实用的帽子 ]在一些罕见的,非常罕见的情况下,你有一个非常非常好的理由这样做,而不是添加一个大的评论解释你为什么这样做并做 - 添加参考或使用Double Dispatch传递存储库[ 帽子关闭 ]

此外,如果您希望遵循DDD原则,强烈建议您访问领域专家和迭代开发过程(请参阅Eric Evans - 自本书以来我学到了什么 ).

使用域专家,您应该定义边界上下文,最重要的是聚合及其聚合根及其实体和值对象.一开始沿着DDD路走下去并不容易,但是rewords是滔滔不绝的.

关于您发布的代码的一些事项:

  1. 不建议在您的实体上安装公共设置器.使用方法代替更好地表达意图.

  2. 如果在未初始化_employer字段的情况下创建了person实例,则Employer属性的getter将返回null.如果然后将Employer属性的值设置为null,则对getter的下一次调用将返回非null值.这可能是您班级用户的意外.

  3. 设置人员雇主的呼叫者(通过公共设置者或公共方法)应该知道它要设置的确切公司实例,即使它是默认设置.也许调用者可以拥有对存储库的引用.

  4. 根据您的具体域,公司可能是一个价值对象.在这种情况下,不是使用null初始化_employer,而是可以使用值对象的默认值对其进行初始化.如果你只有很少的公司(1-2)并且它们是不可变的并且没有特定的行为,情况可能就是这种情况.


Noz*_*ama 5

我认为很容易说一个实体不应该知道存储库,但很难将其付诸实践。尤其是当一个聚合在其中包含大量 vo 集合时,我们必须重构它并委托操作,例如添加到一些实际充当存储库的域服务,以避免将整个集合加载到内存中的开销。
但我认为让实体知道存储库是不合理的。如果一定要,然后使用 域名服务instead.We也应该考虑如果存储库违反了单一职责原则-它应该有被认为是一个总根,而不是一个正常的工厂