实体可以访问存储库吗?
我刚刚开始使用 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 对象时提供它?
谢谢, 磷
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
这并不是对 DDD 的严重违反,这是对……嗯……这简直太可怕了(我是开玩笑地说的):)。
首先,您的实体变得依赖于存储库……这并不理想。
理想情况下,您希望让存储库创建 Person,然后为其分配在当前域上下文中有效所需的一切。
因此,当您需要一个人时,您将转到 personRepository.GetPersonWithDefaultEmployer() 并返回一个已填充默认雇主的人。 personRepository 将依赖于雇主Repository,并在返回之前使用它来填充人员。
it's not a horrible violation of DDD it's a horrible violation of... well... it's just plain horrible (i say this tongue in cheek) :).
First off, your entity becomes dependent on having a repository... that's not ideal.
Ideally you'd want to have your repository create the Person and then assign it everything it needs to be effective in the current domain context.
So when you need a Person, you'll go personRepository.GetPersonWithDefaultEmployer() and get back a person which has default employer populated. The personRepository will have a dependency on an employerRepository and use that to populate the person before returning it.
您的问题的答案是标准的:这取决于。
根据经验,永远不要这样做。 保持您的实体不引用存储库。
[戴上实用的帽子]在一些极其罕见的情况下,如果你有非常非常非常好的理由这样做,请添加一个大注释来解释你为什么这样做并这样做:添加引用或使用 Double Dispatch 传递存储库[脱帽]
另外,如果您希望遵循 DDD 原则,强烈建议您联系领域专家并进行迭代开发过程(请参阅 Eric Evans - 我从这本书中学到的东西)。
您应该与领域专家一起定义边界上下文,最重要的是聚合及其聚合根及其实体和值对象。 走 DDD 之路一开始并不容易,但回报是值得的。
关于您发布的代码的一些事项:
不建议在您的实体上使用公共设置器。 使用可以更好地表达意图的方法。
如果在没有初始化 _employer 字段的情况下创建 person 实例,Employer 属性的 getter 将返回 null。 如果随后将 Employer 属性的值设置为 null,则下次调用 getter 将返回非 null 值。 这可能是您的类的用户意想不到的。
设置此人的雇主(通过公共 setter 或公共方法)的调用者应该知道它想要设置的确切 Company 实例,即使它是默认实例。 也许调用者可以获得对存储库的引用。
根据您的具体领域,公司可能是一个价值对象。 在这种情况下,您可以使用值对象的默认值来初始化 _employer,而不是使用 null 来初始化它。 如果您只有很少的公司 (1-2) 并且它们是不可变的并且没有特定行为,则可能会出现这种情况。
The sort-of answer to your question is the standard: It depends .
As a rule of thumb, don't ever do this. Keep your entities without references to repositories.
[putting practical hat on] In some extremely rare cases where you have a very, very, very good reason for doing this, add a big comment explaining why you're doing it and do it: add the reference or use Double Dispatch to pass the repository[hat off]
Also, if you wish to follow DDD principles, it's highly recommended you have access to a domain expert and an iterative process of development ( see Eric Evans - what i've learned since the book ).
With your domain expert you should define bounding contexts and most importantly the aggregates and their aggregate roots and their entities and value objects. Going down the DDD road is not easy at first but the rewards are worth it.
A few things regarding the code you posted:
It's not recommended to have public setters on your entities. Use methods instead that express the intent better.
If a person instance is created without initializing the _employer field, the getter for the Employer property will return null. If you then set the value of the Employer property to null, the next call to the getter will return a non-null value. This is probably unexpected by the users of your class.
The caller setting the Employer of the Person (either by public setter or public method) should know the exact Company instance it wants to set, even if it's the default one. Maybe the caller can have the reference to the repository.
Depending on your concrete domain, the Company might be a value object. In that case instead of initializing the _employer with null you could initialize it with the default value for the value object. This could be the case if you only have very few companies (1-2) and they are immutable and don't have specific behavior.
我认为说一个实体不应该意识到存储库很容易,但很难将其付诸实践。 特别是当聚合在其中包含大量 vo 集合时,我们必须重构它并将诸如 add 之类的操作委托给某些实际上充当存储库的域服务,以避免将整个集合加载到内存中的开销。
但我认为让实体知道存储库是不合理的。 如果必须,那么使用域服务来代替。我们还应该考虑存储库是否违反单一职责原则——它应该被认为是聚合根的集合,不是普通的工厂。
I think it is easy to say an entity shouldn't be aware of repositories, but hard to put it into practice. Especially when an aggregate gets a big collection of vo's inside it, we have to refactor it and delegate operations like add to some domain service that actually acts as a repository to avoid the overhead of loading entire collection into memory.
But I don't think it's reasonable to let entities know of repositories. If we have to , then use domain services instead.We should also consider if a repository violates the Single Responsibility principle -- it should have been thought of as a Collection of aggregate roots, not a normal factory.
这真的是建议做的 ddd 吗?
如果您没有内存存储库,而是存储库的关系数据库,并且您想要获取 1000 个人及其雇主,该怎么办? 您要通过雇主存储库调用进行 1000 次查询吗?
我会使用 NHibernate 或任何 ORM 来帮助实现 personRepository。 对于 NHibernate,我将使用接近此查询的 Hibernate 查询:“from Person join fetch Employer”,它将在每个“Person”实例中加载一个“Employer”实例,仅使用一个 SQL 查询。
这是否违反了DDD?
Is it really what is recommanded doing ddd ?
And what if you don't have an in-memory repository, but a relationnal database for your repository, and you want to get 1000 persons with their employer ? Are you going to make 1000 queries through employerRepository call...?
I would use NHibernate or any ORM to help implement personRepository. With NHibernate, I will use Hibernate Query close to this one : "from Person join fetch Employer" which will load an "Employer" instance in each "Person" instance, with only one SQL query.
Is it a violation to DDD ?
首先,我认为实体本身和如何组装实体实际上是两个职责。 因此,理想情况下最好将它们分配到不同的类中。 但这也取决于。
First of all, I think the entity itself and how to assembly the entity are 2 duties in fact. So ideally it is better to distribute them into different class. But that's depends on too.