Domain driven design 实体可以访问存储库吗?

Domain driven design 实体可以访问存储库吗?,domain-driven-design,repository-pattern,ddd-repositories,Domain Driven Design,Repository Pattern,Ddd Repositories,我刚刚开始使用DDD,所以这可能是一个愚蠢的问题 实体访问存储库(通过某些IRepository接口)以在运行时获取值是否可以?例如,我希望对属性强制执行“默认”选择: class Person { private Company _employer; public Company Employer { get { return _employer; } set { if(value != null) {

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

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

class Person {
    private Company _employer;

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

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

谢谢,
P

这不是对DDD的可怕违反,而是对。。。好。。。这简直太可怕了(我开玩笑地说):)

首先,您的实体依赖于存储库。。。这并不理想。 理想情况下,您希望让存储库创建人员,然后为其分配在当前域上下文中有效所需的一切

因此,当您需要一个人时,您将转到personRepository.GetPersonWithDefaultEmployer(),并找到一个填充了默认雇主的人。personRepository将依赖于EmployeerRepository,并在返回它之前使用它填充person

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

这真的是什么建议做ddd

如果你没有内存中的存储库,而是存储库的关系数据库,你想和他们的雇主联系1000人,那该怎么办?您是否打算通过EmployeerRepository电话进行1000次查询

我会使用NHibernate或任何ORM来帮助实现personRepository。对于NHibernate,我将使用与此类似的Hibernate查询:“from Person join fetch Employer”,它将在每个“Person”实例中加载一个“Employer”实例,只使用一个SQL查询


这是否违反了DDD?

对您的问题的回答是标准的:视情况而定

根据经验,永远不要这样做。保留实体而不引用存储库

[戴上实用帽子]在一些非常罕见的情况下,如果你有非常、非常、非常好的理由这样做,请添加一条大评论,解释你为什么要这样做,然后这样做:添加引用或用于传递存储库[帽子关上]

此外,如果您希望遵循DDD原则,强烈建议您访问领域专家和迭代开发过程(请参阅)

使用领域专家,您应该定义边界上下文,最重要的是定义聚合及其聚合根及其实体和值对象。开始走DDD之路并不容易,但回报是值得的

关于您发布的代码,有几点:

  • 不建议在实体上使用公共设置器。使用更好地表达意图的方法

  • 如果在未初始化_employer字段的情况下创建person实例,则employer属性的getter将返回null。如果随后将Employer属性的值设置为null,那么对getter的下一次调用将返回非null值。这可能是您的类的用户没有预料到的

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

  • 根据您的具体领域,该公司可能是一个价值对象。在这种情况下,您可以使用value对象的默认值初始化它,而不是使用null初始化_雇主。如果你只有很少的公司(1-2家),而且它们是不可变的,没有特定的行为,那么可能会出现这种情况


  • 首先,我认为实体本身以及如何组装实体实际上是两项职责。因此,理想情况下,最好将它们分配到不同的类中。但这也取决于你

    我认为说实体不应该知道存储库很容易,但很难付诸实践。特别是当一个聚合中有一个很大的vo集合时,我们必须对其进行重构,并将诸如添加之类的操作委托给一些实际充当存储库的域服务,以避免将整个集合加载到内存中的开销
    但我认为让实体知道存储库是不合理的。如果我们必须使用,则使用<强>域服务< /强>。我们也应该考虑一个库是否违反<强>单职责<强>原则——它应该被认为是聚合根的“强”集合<强>。不是一个正常的工厂

    我很高兴知道,让实体知道存储库是一种糟糕的代码味道!这就是说,我喜欢你上面创建新Person对象的解决方案(事实上已经做了类似的事情),但是我如何处理在创建了Person之后雇主发生变化的情况呢?一种解决方案是将逻辑移出一个单独的域服务类,该类具有选择默认雇主的逻辑。但该服务仍然需要访问雇主存储库-也许这更可取?为什么不能使用personRepository保存人员并将封装逻辑放在其中?我不确定是否遵循您的指示-这不是保存问题,而是更新内存中的person对象。不允许雇主为空的规则似乎是一条不应该出现在存储库中的业务规则。我的应用程序不是一个web应用程序,而是一个始终在内存中具有完整对象图的应用程序。我不会一直从数据库加载对象,只是在应用程序启动时才加载。无论如何,您不希望依赖Person实体中的EmployeerRepository。创建人员时,由存储库的作业或单独的服务调用存储库并返回人员,以确保人员