实体可以访问存储库吗?

时间:2009-05-06 01:39:59

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

我刚刚开始使用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

5 个答案:

答案 0 :(得分:23)

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

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

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

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

答案 1 :(得分:5)

问题的答案是标准取决于

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

此外,如果您希望遵循DDD原则,强烈建议您访问域专家和迭代的开发过程(请参阅Eric Evans - what i've learned since the book)。

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

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

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

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

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

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

答案 2 :(得分:3)

我认为很容易说实体不应该知道存储库,但很难将其付诸实践。特别是当聚合内部收集大量的vo时,我们必须重构它并将 add 等操作委托给一些实际充当存储库的域服务,以避免将整个集合加载到内存中的开销。
但我认为让实体知道存储库是不合理的。如果必须,请使用域名服务。我们还应该考虑存储库是否违反单一责任原则 - 它本应被视为聚合根的集合,而不是正常的工厂

答案 3 :(得分:0)

这真的是推荐做ddd吗?

如果您没有内存存储库,但是存储库的关系数据库,并且您希望与其雇主一起获得1000人,该怎么办?你打算通过employerRepository调用来进行1000次查询......?

我会使用NHibernate或任何ORM来帮助实现personRepository。使用NHibernate,我将使用接近这个的Hibernate Query:“来自Person join fetch Employer”,它将在每个“Person”实例中加载一个“Employer”实例,只有一个SQL查询。

它是否违反DDD?

答案 4 :(得分:0)

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