简体   繁体   English

清洁架构中的存储库(数据库网关)

[英]Repositories (database gateway) in Clean Architecture

I have been studying Robert Martin's Clean Architecture.我一直在研究 Robert Martin 的 Clean Architecture。 I loved this book, but when I saw some tutorial and code that is supposed to be an example of clean architecture, I saw a repository interface declared in the Entities (enterprise business rules) layer, with methods referencing an Entities class (which means the repository implementation in the adapters layers depends directly on Entities layer, bypassing application layer).我喜欢这本书,但是当我看到一些应该是干净架构示例的教程和代码时,我看到了在实体(企业业务规则)层中声明的存储库接口,其方法引用了一个实体类(这意味着适配器层中的存储库实现直接依赖于实体层,绕过应用层)。 Although in this sample codes the dependency rule is followed, i have some question about it:尽管在此示例代码中遵循依赖规则,但我对此有一些疑问:

  1. Wouldn't a repository interface fit better in application layer, since persistence is more an application rule then a core rule about the entities?存储库接口不是更适合应用程序层吗,因为持久性更多的是应用程序规则而不是关于实体的核心规则?
  2. In his book, and also in his blog article (search for the "What data crosses the boundaries" subtitle), Uncle bob says that only simple data structures should be passed across the boundaries, never entities.在他的书和他的博客文章中(搜索“什么数据跨越边界”副标题),鲍勃叔叔说只有简单的数据结构应该跨越边界,而不是实体。 So are this sample codes wrong?那么这个示例代码错了吗?

From my point-of-view, which is a little more influenced by Domain-driven Design not only Clean Architecture, I see repositories as clear members of the domain layer (or business or core layer mostly referred to in clean architecture).从我的观点来看,它更多地受到领域驱动设计的影响,不仅是清洁架构,我认为存储库是领域层(或清洁架构中主要提到的业务或核心层)的明确成员。

There are mainly two reasons for that:主要有两个原因:

1. Repositories are domain services that contain logic that has a meaning to business people. 1. 存储库是包含对业务人员有意义的逻辑的领域服务。

Like finding an entity (aggregate) based on different criteria.就像根据不同的标准找到一个实体(聚合)。 For instance, a customer support employee, when on a support call, might have to search for an order either by the order number or if the customer does not have that they have to search by customer name and other criteria.例如,客户支持员工在拨打支持电话时可能必须通过订单号搜索订单,或者如果客户没有,则他们必须按客户名称和其他条件进行搜索。 The order repository would provide something like findOrderByCustomerName() which reflects the business language and needs .订单存储库将提供类似findOrderByCustomerName()的东西,它反映了业务语言和需求 So repositories can contain more than just methods to retrieve the entity and save the entity.因此存储库可以包含的不仅仅是检索实体和保存实体的方法。 In some cases they can also provide a primitive value.在某些情况下,它们还可以提供原始值。 For instance, a repository for incident tickets could give you the number of unresolved tickets for a specific product.例如,事件工单的存储库可以为您提供特定产品的未解决工单的数量。 Again, it needs to fulfill some business logic related purpose.同样,它需要完成一些与业务逻辑相关的目的。

2. The domain (business) layer itself might need to consume a repository in order to perform some business logic. 2. 域(业务)层本身可能需要使用存储库来执行一些业务逻辑。

If business logic does not fit with one single entity it is often the case that some kind of domain service is needed that contains the required logic.如果业务逻辑不适合单个实体,则通常需要某种包含所需逻辑的域服务 And this service containing business logic could need access to a repository to fulfill its tasks .而这个包含业务逻辑的服务可能需要访问存储库来完成其任务

Let's say we have some company internal meeting room reservation application.假设我们有一些公司内部会议室预订申请。 If you want to make a new appointment there might be business logic that says that it must not be possible to schedule an appointment if there is already another appointment at the same time in the same meeting room.如果您想进行新的约会,可能会有业务逻辑表明,如果同一会议室中同时已有另一个约会,则不能安排约会。 So the domain logic to ask if the requested meeting room is still free at that time might be part of the meeting room repository providing something like IsRoomFreeAt(RoomId roomId, DateTime requestedMeetingTime) .因此,询问所请求的会议室当时是否仍然空闲的域逻辑可能是会议室存储库的一部分,提供了诸如IsRoomFreeAt(RoomId roomId, DateTime requestedMeetingTime) 之类的内容。 And there could be a service in the domain layer executing all the related business logic which needs this repository's functionality.并且域层中可能有一个服务执行所有需要此存储库功能的相关业务逻辑。 Even if you don't want to apply that query pattern and rather just search for the respective meeting room and do check the free status of that room instead, it would be the same result.即使您不想应用该查询模式,而只是搜索相应的会议室并检查该会议室的空闲状态,结果也会相同。

If the one approach or the other would be more suited for the respective application use case is another question.如果一种方法或另一种方法更适合各自的应用程序用例是另一个问题。 The important part here is that the business logic to make sure no rooms are double booked requires some information from a repository, no matter how.这里的重要部分是确保没有房间被重复预订的业务逻辑需要来自存储库的一些信息,无论如何。

If the repository would be located in the application layer the dependencies would no longer be pointing inwards because now the domain (business) layer would have a dependency on the application layer and not just the other way around.如果存储库位于应用程序层,则依赖项将不再指向内部,因为现在域(业务)层将依赖于应用程序层,而不仅仅是相反。

  1. Wouldn't a repository interface fit better in application layer, since persistence is more an application rule then a core rule about the entities?存储库接口不是更适合应用程序层吗,因为持久性更多的是应用程序规则而不是关于实体的核心规则?

The repository interfaces are made to be used by interactors (aka. use cases).存储库接口供交互者使用(又名用例)。 With the repository interface an interactor says what it wants but not how it is fulfilled.使用存储库接口,交互者会说出它想要什么,但不会说出它是如何实现的。 Thus they belong to the interactors and should therefore be placed in the use cases (application) layer.因此它们属于交互者,因此应该放在用例(应用程序)层中。

I don't know the example you mentioned in your question, so I can't investigate it to see if there is some reason why the author put the repository interfaces in the entities layer and if it could have been solved in another way.我不知道您在问题中提到的示例,因此我无法对其进行调查,以查看作者将存储库接口放在实体层中是否有某种原因以及是否可以通过其他方式解决。

  1. In his book, and also in his blog article (search for the "What data crosses the boundaries" subtitle), Uncle bob says that only simple data structures should be passed across the boundaries, never entities.在他的书和他的博客文章中(搜索“什么数据跨越边界”副标题),鲍勃叔叔说只有简单的数据结构应该跨越边界,而不是实体。 So are this sample codes wrong?那么这个示例代码错了吗?

Uncle Bob says鲍勃叔叔说

Typically the data that crosses the boundaries is simple data structures.通常,跨越边界的数据是简单的数据结构。

and later然后

For example, many database frameworks return a convenient data format in response to a query.例如,许多数据库框架返回方便的数据格式以响应查询。 We might call this a RowStructure.我们可以称其为 RowStructure。 We don't want to pass that row structure inwards across a boundary.我们不想跨边界向内传递该行结构。 Let me explain why I highlighted typically .让我解释一下为什么我通常突出显示。

So what he doesn't want in his architecture is that classes, or any types, that are defined in the database layer appear in the entities or the interactors.因此,他不希望在他的架构中出现在数据库层中定义的类或任何类型出现在实体或交互器中。 Because this would be a dependency from higher level modules to details.因为这将是高级模块对细节的依赖。

Sometimes there are use cases that some developers want to implement more relaxed, but it comes at a price.有时有些用例是一些开发人员希望更轻松地实现的,但这是有代价的。

Eg when you have a Show order details and you have loaded the complete order entity in your interactor, you usually have to copy almost all data to the data structure that passed the boundary (the output port) in order to strictly follow the architecture.例如,当您有一个Show order details并且您已在交互器中加载了完整的订单实体时,您通常必须将几乎所有数据复制到通过边界(输出端口)的数据结构中,以严格遵循架构。

You could also pass the entity to the output port, but this would violate the strict architecture rules.您也可以将实体传递到输出端口,但这会违反严格的架构规则。 So if you want to follow the strict rules you must copy the data to a new data structure, if not you pass the entity to the output port.因此,如果您想遵循严格的规则,则必须将数据复制到新的数据结构中,否则将实体传递到输出端口。

But before you pass the entity directly to the output port, you should consider the pros and cons.但是在将实体直接传递到输出端口之前,您应该考虑利弊。

If you pass the entity to the output port, ou have a dependency from the interface adapters layer to the entity layer.如果您将实体传递到输出端口,则从接口适配器层到实体层存在依赖关系。 The dependency rule that each dependency should point inwards is still applied, but the dependency skips one layer.仍然应用每个依赖项应该指向内部的依赖项规则,但是依赖项跳过了一层。

Such a relaxed architecture introduces the risk that eg controllers might invoke business methods on entities and that is what they shouldn't do.这种宽松的架构引入了风险,例如控制器可能会调用实体上的业务方法,而这是他们不应该做的。 It's up to you to decide if you (and your colleagues) are diciplined enough to not invoke business methods from the controller or if you better protect yourself and others from doing it by introducing a new data structure and do the copy effort.由您来决定您(和您的同事)是否有足够的纪律性以不从控制器调用业务方法,或者您是否通过引入新的数据结构并进行复制工作来更好地保护自己和他人免于这样做。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM