且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

DDD-实体不能直接访问存储库的规则

更新时间:2023-02-13 11:10:14

这里有些混乱.存储库访问聚合根.聚合根是实体.这样做的原因是关注点分离和良好的分层.在小型项目中这没有意义,但是如果您在大型团队中,您会说:您可以通过Product Repository访问产品.Product是实体集合(包括ProductCatalog对象)的集合根.如果要更新ProductCatalog,则必须通过ProductRepository."

There's a bit of a confusion here. Repositories access aggregate roots. Aggregate roots are entities. The reason for this is separation of concerns and good layering. This doesn't make sense on small projects, but if you're on a large team you want to say, "You access a product through the Product Repository. Product is an aggregate root for a collection of entities, including the ProductCatalog object. If you want to update the ProductCatalog you must go through the ProductRepository."

通过这种方式,您可以非常清楚地将业务逻辑和更新内容分开.您没有一个孩子一个人呆着,自己编写了整个程序,将所有这些复杂的事情写到了产品目录中,当涉及到将其集成到上游项目时,您就坐在那里看着它并意识到了这一点.一切都必须抛弃.这也意味着当人们加入团队,添加新功能时,他们知道去哪里以及如何构建程序.

In this way you have very, very clear separation on the business logic and where things get updated. You don't have some kid who is off by himself and writes this entire program that does all these complicated things to the product catalog and when it comes to integrate it to the upstream project, you're sitting there looking at it and realize it all has to be ditched. It also means when people join the team, add new features, they know where to go and how to structure the program.

但是等等!像存储库模式"一样,存储库也指持久层.在一个更好的世界中,埃里克·埃文斯(Eric Evans)的存储库和存储库模式将具有单独的名称,因为它们往往会重叠很多.为了获得存储库模式,您需要使用服务总线或事件模型系统来与其他访问数据的方式进行对比.通常,当您达到这一级别时,Eric Evans的存储库定义就会绕开,您便开始谈论有限的上下文.每个有界上下文本质上都是其自己的应用程序.您可能具有完善的审批系统,可以将产品放入产品目录中.在您的原始设计中,产品是核心,但在这种有限的背景下,产品目录就是其中的一部分.您仍然可以通过服务总线访问产品信息并更新产品,但是您必须意识到,在受限上下文之外的产品目录可能意味着完全不同的东西.

But wait! Repository also refers to the persistence layer, as in the Repository Pattern. In a better world an Eric Evans' Repository and the Repository Pattern would have separate names, because they tend to overlap quite a bit. To get the repository pattern you have contrast with other ways in which data is accessed, with a service bus or an event model system. Usually when you get to this level, the Eric Evans' Repository definition goes by the way side and you start talking about a bounded context. Each bounded context is essentially its own application. You might have a sophisticated approval system for getting things into the product catalog. In your original design the product was the center piece but in this bounded context the product catalog is. You still might access product information and update product via a service bus, but you must realize that a product catalog outside the bounded context might mean something completely different.

回到您的原始问题.如果要从实体内部访问存储库,则意味着该实体实际上不是业务实体,而可能是服务层中应该存在的某种实体.这是因为实体是业务对象,因此应尽可能使自己像DSL(特定于域的语言)一样.在这一层中只有业务信息.如果您要对性能问题进行故障排除,您将知道在其他地方查找,因为此处仅应包含业务信息.如果突然之间您在这里遇到应用程序问题,那么您将很难扩展和维护应用程序,而这确实是DDD的核心:开发可维护的软件.

Back to your original question. If you're accessing a repository from within an entity it means the entity is really not a business entity but probably something that should exist in a service layer. This is because entities are business object and should concern themselves with being as much like a DSL (domain specific language) as possible. Only have business information in this layer. If you're troubleshooting a performance issue, you'll know to look elsewhere since only business information should be here. If suddenly, you have application issues here, you're making it very hard to extend and maintain an application, which is really the heart of DDD: making maintainable software.

对评论1的回复:对,很好的问题.因此,并非 all 验证在域层中进行.夏普有一个"DomainSignature"属性可以满足您的需求.它具有持久性,但是作为属性可以使域层保持干净.这样可以确保您没有名称相同的重复实体.

Response to Comment 1: Right, good question. So not all validation occurs in the domain layer. Sharp has an attribute "DomainSignature" that does what you want. It is persistence aware, but being an attribute keeps the domain layer clean. It ensures that you don't have a duplicate entity with, in your example the same name.

但是让我们谈谈更复杂的验证规则.假设您是Amazon.com.您是否曾经使用过期的信用卡订购过商品?我有,但我还没有更新卡和买东西.它接受订单,并且UI通知我所有东西都是桃红色的.大约15分钟后,我会收到一封电子邮件,说我的订单有问题,我的信用卡无效.理想情况是,在域层中进行了一些正则表达式验证.这是正确的信用卡号吗?如果是,则继续执行该命令.但是,在应用程序任务层还需要进行其他验证,在该层中,将查询外部服务以查看是否可以在信用卡上付款.如果没有,则实际上不运送任何东西,暂停订单并等待客户.这都应该在服务层中进行.

But let's talk about more complicated validation rules. Let's say you're Amazon.com. Have you ever ordered something with an expired credit card? I have, where I haven't updated the card and bought something. It accepts the order and the UI informs me that everything is peachy. About 15 minutes later, I'll get an e-mail saying there's a problem with my order, my credit card is invalid. What's happening here is that, ideally, there's some regex validation in the domain layer. Is this a correct credit card number? If yes, persist the order. However, there's additional validation at the application tasks layer, where an external service is queried to see if payment can be made on the credit card. If not, don't actually ship anything, suspend the order and wait for the customer. This should all take place in a service layer.

不要害怕在可以访问存储库的服务层上创建验证对象.只需将其放在域层之外即可.

Don't be afraid to create validation objects at the service layer that can access repositories. Just keep it out of the domain layer.