简洁的架构:在哪里进行 API 调用

Bra*_*ler 11 node.js microservices clean-architecture

我目前正在创建一个微服务项目,在其中实现 Bob Martin 创造的清洁架构模式。虽然我的代码运行良好,但我对干净的架构模式有疑问,特别是接口和 use_cases 层。该应用程序是我正在开发的一个小型电子商务 POC。话虽如此,由于它正在实现微服务,所以我有 3 个不同的服务。产品、图片和评论。每当发出获取“完整产品”的请求时,客户端都会 ping 完整产品的端点,它会抓取该端点,并使用其 id 来 ping 我的图像和评论服务,以获取该产品的所有图像和评论。

那么,我的问题是我应该在哪里实现创建这些调用的逻辑?我的直觉告诉我应该将它放在控制器层中,因为这是两个层中最抽象的,而且我不会因为将 axios 依赖项放在其中而感到难过。但可惜的是,我也觉得“完整的产品必须包括产品详细信息、图像和所有评论”这句话听起来很像商业规则。

我知道这个问题主要是主观的,但我想知道你如何实现这个逻辑,为什么?

我还应该提到,在我当前的解决方案中,我的 use_case 是我调用存储库并实际从数据库中获取产品的地方。话虽这么说,如果我将 API 调用放入控制器层,我必须首先调用我的 use_case 来获取产品,然后可能创建一个单独的 use_case 来检查我的最终对象是否实际上是“完整产品”。

Ren*_*ink 30

从产品用例的角度获取图像和评论就像数据库访问一样。唯一的区别是您不查询数据库,而是查询另一个服务。但从产品微服务的角度来看,两者都是为您的用例提供数据的外部系统。

当您查看干净的架构时,您会意识到控制器和网关位于同一架构层 - 接口适配器。该层被称为“接口适配器”,因为它适配下层的接口。

正如您所看到的,网关可以是数据库或外部接口(服务)。

清洁架构的网关、数据库和外部服务

因此,您应该以这种方式构建您的应用程序:

                       +------------------+
                       | product use case |
                       +------------------+
                                |
             +------------------+---------------------+
             |                  |                     |
             V                  V                     V
    +-----------------+ +------------------+ +-------------------+
    | ImageRepository | | ReviewRepository | | ProductRepository |
    +-----------------+ +------------------+ +-------------------+
            ^                     ^                    ^
            |                     |                    |
 ===========+=====================+====================+================
            |                     |                    |
 +--------------------+ +--------------------+ +--------------------+
 |   ImageRestClient  | | ReviewRestClient   | |   JDBCConnector    |
 +--------------------+ +--------------------+ +--------------------+
Run Code Online (Sandbox Code Playgroud)

您可能想要选择其他命名,但结构将保持不变。

易于测试,如果您决定有一天应该在产品微服务中管理图像而不是单独的服务,您可以将其替换ImageRestClientJDBCConnector.


编辑

@Rene,你能帮忙理解一下吗?我经常使用干净架构,但到目前为止我不明白为什么数据库层是干净架构中的外层。是的,这是与外部系统的通信,但是1)如果DB是一个框架,通常主应用程序会导入DB,反之亦然。

它是依赖倒置原理的应用,它告诉我们:

“高层政策不应依赖于低层细节。” -罗伯特. C·马丁

应用程序记住数据的方式是一个细节。我说“记住”,因为这是数据存储的抽象。也许数据存储在数据库中、只是文件中,甚至仅存储在 RAM 中。例如,Web 应用程序可以使用 aSessionCartRepository或 aDBCartRepository来存储购物车。

我想人们有时会对干净的架构感到有点困惑,因为他们之前看到的图表将数据库放在中心。但清晰的架构图只是可视化如何构建应用程序的一种方式。

在我听说干净架构的几年前,我已经应用了它,但我的图表看起来像这样。

 +--------------------+
 | ApplicationService | ---+
 +--------------------+    |
       |                   |        Use Case
 ======|===================|================
       V                   V          Domain
 +------------+      +----------------+
 | Repository | -->  | BusinessObject |
 +------------+      +----------------+
       ^
=======|====================================
       |                            Database
 +----------------+   
 | JdbcRepository | 
 +----------------+
Run Code Online (Sandbox Code Playgroud)

好吧,2014 年我的命名有所不同。我ApplicationService实现的用例BusinessObject是 CA 的实体。但其结构与 CA 提议的相同。当我第一次读鲍勃叔叔的书《干净的架构》时,我认为他的图表要好得多。从那时起我也使用CA图。但有时我会使用上面显示的图表,因为有些人喜欢将数据库层出于某种原因绘制在底部。

  1. 当外部服务有网络模型并且用例有域模型时,哪一层应该负责从网络模型映射到域模型(通常对我来说是用例层,但这意味着内层了解外层)。外部服务层是否应该进行映射

在两种类型之间映射的组件必须知道这两种类型,因此它对这两种类型都具有依赖关系。

+------------+       +--------+       +------------+
| SourceType | <---- | Mapper | ----> | TargetType |
+------------+       +--------+       +------------+
Run Code Online (Sandbox Code Playgroud)

因此,您不能将映射代码放置在实体或用例层中,因为这些层将依赖于外层,例如作为细节的网络模型。它会违反干净架构的依赖规则。

因此,您必须将映射代码放在外层(例如网络层)。

最后编辑

您提到您的图表有点旧,因为您之前使用过它。那么目前正确的做法是什么呢?

鲍勃叔叔的方式,因为它是广泛使用的方式,并且只有他的观点被称为“清洁架构”。我的没有名字。

如果我现在理解正确的话 1)存储库应该只位于外层,并且只有用例可以与存储库对话(当然通过抽象)?

存储库实现位于外层。定义(例如接口或抽象类)放置在用例层中。换句话说,用例告诉它需要什么,例如通过接口。像数据库这样的提供者实现它。

  1. 领域层是干净架构中的实体?3)或者一些Repos应该位于领域层并与DB层(外层)中的Repos对话(因此这意味着UseCases不与DB Repos对话)

这是我在听说干净架构之前的“旧”观点。我通常将存储库放在用例旁边。我通常还应用接口隔离原则,这意味着我创建特定于用例的存储库。例如

public interface PlaceOrderRepository {
   ...
}
Run Code Online (Sandbox Code Playgroud)

在听说 CA 之前,我将存储库定义作为抽象类放在域层中,以便可以使用 package 修饰符。这就是我今天不再做的事情。今天,我将工厂放在实体层中,可以从包范围中受益(如果我需要包范围),并让存储库使用这些工厂。

因此,我建议在用例用例层中定义用例特定存储库,并在外层(接口适配器层)中实现它们。

接口适配器层

我想这就是为什么这一层被称为接口适配器的原因,因为它是为内圈中定义的接口实现适配器的层。