首页 > 解决方案 > 存储库可以调用清洁架构中的用例吗?

问题描述

这是一个非常棘手的问题,因为当我们检查规则时,并不明确说明存储库不能调用 UseCase。然而,这似乎不合逻辑。是否有任何定义/良好做法以及为什么不应该这样做?

谢谢!

标签: repositorycoding-styleuse-caseclean-architecturedependency-inversion

解决方案


简短的回答是“不” - 无论上下文如何(在大多数情况下),都不应该。至于为什么——定义、原则和良好实践——考虑清楚地分离整个清洁架构实现中的关注点可能会有所帮助。

考虑这个插图,作为思考如何组织清洁架构主要部分之间的交互(和依赖关系)的背景。

在此处输入图像描述

说明的主要原则是——

  • 通过执行,用例具有不同的“数据需求”(AB)。它本身并没有实现实现它们的逻辑(因为它们需要一些特定的技术)。因此Gateway,在本例中,用例将它们声明为两个接口(“端口”)。然后在其逻辑中调用它们。

  • 这两个接口都声明了一些不同的操作集,应该从“外部”提供(实现)。用例在其逻辑中需要并调用所有这些 A 和 B 操作。它们分为 A 和 B,因为它们是不同类型的职责 - 并且可能由系统的不同部分实现(但不一定)。假设用例需要加载持久域对象(作为A操作的一部分),但它还需要检索配置(作为一些键值对),即B操作。这些接口是隔离的,因为两组操作都为用例服务于不同的目的。无论如何,重要的是设计方面,它们都明确地“服务”了用例需求——也就是说,它们不是通用的以实体为中心的 DAO / 存储库接口;它们只有用例实际需要和调用的操作,完全符合用例特别需要的形状和形式(参数、返回值)。作为整个用例的一部分,它们是要“插入”的“端口”。

  • 这些职责的“外部”提供者是这些需求的适配器(实施者)。为了实现它们,它们通常使用一些特定的技术或框架——数据库、对某些服务器的网络调用、消息生产者、文件操作、Spring 的配置属性等。

  • 用例仅由架构的驱动端(即发起端)调用(调用)。事实上,用例本身是其进一步协作部分(例如,适配器)的“发起者”之一。

  • 另一方面,用例由架构的适配器端“技术支持”(其需求的声明部分“实现”)。

  • 实际上,谁调用了什么有一个明确的分离 - 意思是,在运行时调用堆栈在整个架构中以明确的方向控制流进行。

  • 控制流总是从驱动程序流向适配器(通过用例),而不是相反。

这些是我在不同项目的职业生涯中纯粹学习、研究、实施和纠正的原则。换句话说,根据我的经验,它们已经在实际和有用的方面受到现实世界的影响 - 就关注点分离和明确的职责分工而言。您的自然可能会有所不同,并且没有通用的适合-CA 不是秘诀,它是软件设计的一种思维方式,可以(更好和更糟)多种方式实现。

简单地思考一下,我想在您的情况下Repository是您的用例 (Data) 的“数据存储网关”实现Gateway。UC 需要来自“某处”的数据——而不关心它的来源或存储方式。这非常重要——整个核心领域以及用例都需要与框架和 I/O 无关

Repository满足了这一需求 - 提供持久的域对象。但是用例不能直接调用它,而是声明一个网关(在 Hexagonal 中,例如 Ports & Adapters 架构,名为 a Port) - 具有您的存储库需要实现的所需操作。通过使用一些特定的(数据库/持久性)技术,您可以Repository实现它 - 它实现了用例的“端口”之一,作为适配器

如上所述 - 在极少数情况下,某些Gateway实现可能需要例外。他们可能需要多次来回交互,甚至在您的架构中也是如此。它们是罕见且确实复杂的情况 - 可能不是Repository实施所必需的。

但是,如果这确实是一个不可避免的情况——那么最好用例在调用网关时提供一个回调 接口作为调用的参数。因此,在处理过程中,网关的实现者可以使用该接口中的操作进行回调——有效地实现来回的必要性。但是,在大多数情况下,这意味着适配器级别的逻辑和复杂性过多,应该避免这种情况 - 并且强烈提示应该重新设计当前的解决方案。


推荐阅读