问题标签 [hexagonal-architecture]

For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.

0 投票
4 回答
2589 浏览

architecture - 包含演示者或返回数据的用例?

考虑到清洁架构的定义,尤其是描述控制器、用例交互器和演示者之间关系的小流程图,我不确定我是否正确理解了“用例输出端口”应该是什么。

清洁架构,如端口/适配器架构,区分主端口(方法)和辅助端口(由适配器实现的接口)。按照通信流程,我希望“用例输入端口”是一个主要端口(因此,只是一个方法),而“用例输出端口”是一个要实现的接口,也许是一个构造函数参数,采用实际的适配器,以便交互者可以使用它。

做一个代码示例,这可能是控制器代码:

演示者界面:

最后,交互器本身:

上述图表本身似乎证实了这种解释,其中控制器和输入端口之间的关系由带有“尖”头的实心箭头表示(UML 为“关联”,意思是“有一个”,其中控制器“有”用例),而演示者和输出端口之间的关系由带有“白色”头部的实心箭头表示(UML 表示“继承”,不是“实现”,但可能就是这样反正意思)。

但是,我对这种方法的问题是用例必须处理演示本身。现在,我看到Presenter接口的目的是足够抽象以表示几种不同类型的演示者(GUI、Web、CLI 等),并且它实际上只是意味着“输出”,这是用例可能的含义很好,但我仍然对它并不完全有信心。

现在,在网上寻找干净架构的应用程序,我似乎只发现人们将输出端口解释为返回一些 DTO 的方法。这将是这样的:

这很有吸引力,因为我们正在将“调用”表示的责任从用例中移出,因此用例不再关心知道如何处理数据,而只是提供数据。此外,在这种情况下,我们仍然没有打破依赖规则,因为用例仍然不知道外层的任何信息。

但是,用例不再控制执行实际演示的时刻(这可能很有用,例如在那一刻做额外的事情,比如记录,或者在必要时完全中止它)。另外,请注意我们丢失了用例输入端口,因为现在控制器只使用getData()方法(这是我们的新输出端口)。此外,在我看来,我们在这里打破了“告诉,不问”的原则,因为我们要求交互者提供一些数据来处理它,而不是告诉它在第一名。

那么,这两种选择中的任何一种都是根据清洁架构对用例输出端口的“正确”解释吗?它们都可行吗?

对另一个问题的回答中,Robert Martin 准确地描述了交互者根据读取请求调用演示者的用例。没有提到MVC,MVVC等,所以我猜Clean Architecture一般不能很好地与MVC配合使用?

单击地图会导致调用 placePinController。它收集点击的位置和任何其他上下文数据,构造一个 placePinRequest 数据结构并将其传递给 PlacePinInteractor,后者检查 pin 的位置,在必要时对其进行验证,创建一个 Place 实体来记录 pin,构造一个 EditPlaceReponse对象并将其传递给 EditPlacePresenter,它会显示位置编辑器屏幕。

一种可能的解释是,传统上会进入控制器的应用程序逻辑在这里被移动到交互器,因为我们不希望任何应用程序逻辑泄漏到应用程序层之外。因此,这里的模型不是调用演示者,因为交互者不是模型,而是控制器的实际实现。模型只是被传递的数据结构。这似乎得到了证实:

该层中的软件是一组适配器,可将数据从最适合用例和实体的格式转换为最适合某些外部机构(如数据库或 Web)的格式。

从原始文章中,谈论接口适配器。由于控制器必须只是将一种数据格式转换为另一种数据格式的瘦适配器,因此它不能包含任何应用程序逻辑,因此将其移至交互器。

0 投票
1 回答
196 浏览

architecture - 什么时候实例化应用服务层类?

我正在尝试将六边形架构应用于一个项目,该项目将有多个应用程序必须处理相同的域模型。

例如,我们将有一个请求应用程序服务的 mvc 网站,然后该服务将处理域模型和数据访问层。到目前为止一切都很好。

我的问题是什么时候应该实例化一个应用程序服务类。我应该为所有用户(单例)只拥有任何类的一个实例吗?或者我应该为每个用户实例化一个,然后让它保存一些用户数据并存储在用户会话中?或者我应该为每个请求实例化一个新类?

当然,这些选项中的任何一个都是可能的,但我想获得您的最佳实践建议。

我猜这个类越无状态越好,但是无状态类意味着我需要通过每个我不特别喜欢的方法调用来传递用户特定的数据。

0 投票
3 回答
5947 浏览

java - 带有弹簧数据的六边形结构

我要开始一个新项目,学习spring boot、spring data和六边形架构。据我了解,六边形架构旨在将核心或域层与数据库操作(基础设施层)分开。我已经看到了这个架构的以下项目结构。

核心层有:

服务-> 逻辑去向(接口及其实现)。

实体 -> 这些将在整个应用程序中使用。

Repository-> 基础设施层必须实现的接口。

基础设施层实现了存储库接口、JPA 实体、对数据库的调用(休眠)以及将 JPA 实体转换为核心实体(映射器?)的某种功能。

Spring data 有一种非常有用的方式来实现 CRUD 操作:

但是,我认为如果我使用 spring 数据,如果 UserRepository 是核心层的一部分,那么 JPA 实体将不会成为基础设施层的一部分。这意味着核心实体将毫无用处。我应该创建另一个属于核心层的 UserRepository 接口还是我遗漏了什么?

更新:

我对使用 spring 数据的担忧来自于我必须在域中包含 JPA 实体,这在理论上会违反六边形架构。

所以我正在考虑将域实体与 JPA 实体分开。但是如果我这样做,我不知道 Spring Data 的存储库应该去哪里,并且还找到一种将 JPA 实体转换为域实体的方法。

为了更好地说明,我将假设我需要从我的应用程序连接到数据库来读取用户表。

这可能是域实体:

据我了解,服务应该包含逻辑并操作域实体。

实施:

以上以及存储库接口是我认为的域(如果我错了,请纠正我)。接下来,基础设施由 JPA 实体组成

我认为我调用 Spring Data 的接口应该在基础设施部分,因为稍后我需要将 JPA 实体映射到域实体中,也许我需要使用另一个类(和适配器?)来进行映射。这种方法正确还是有其他方法?抱歉发了这么长的帖子,我希望我已经说清楚了。

0 投票
1 回答
541 浏览

hexagonal-architecture - 六边形架构中的数据库存储库依赖项

我正在将 nTier 架构迁移到六边形架构中。我的域现在定义良好,并且我拥有所有基础设施依赖项的接口。查看数据库存储库,我有几个数据库,并且在 Infra 端有一个实现每个 repo 的类。我的问题是关于数据库依赖的正确方法应该是什么:

1-在域端有一个接口来处理数据访问,然后依赖于基础设施实现,并有一个类作为所有数据库存储库的入口点,就像一个门面,从那里调用所有存储库实现。这种方法与我在 nTier 应用程序中已有的方法相同,外观和存储库是当前的数据库层。

2-在域端为我需要的每个数据库提供接口。每个接口都将在 Infra 端实现以访问相应的 DB。它使层保持薄,但这种方法将数据管理逻辑添加到域。域不应该关心数据在哪里,基础设施应该处理这个问题。如果将某些数据移动到另一个数据库,则需要在域端更改相应的接口(例如,将暴露此数据的方法移动到另一个接口)

让我知道,

0 投票
2 回答
1072 浏览

domain-driven-design - DDD 存储库可以是有状态的吗?

我正在设计一个运输应用程序并尝试使用 Clean Architecture。我试图找出在哪里保存 Shipment 对象的状态,这样我就不必在每次用户单击 UI 中的按钮时重新实例化一个新对象。这是流程。

  1. 用户在 UI 中输入交货编号
  2. UI 控制器处理 UI 事件并实例化用例交互器的一个实例。传递给用例交互器的构造函数的存储库实例
  3. 用例交互器通过调用工厂(例如 CREATE_BY_DELIVERY)来实例化 Shipment 的实例。工厂调用 Repository 从数据库中收集数据。
  4. 交付数据在 UI 上填充
  5. 用户然后单击“报价单”按钮
  6. UI Controller 处理按钮点击事件并调用 Use Case Interactor a 的 RATE_QUOTE 方法。用例交互器是否需要像第 3 步一样再次调用 Shipment 工厂,或者用例交互器是否可以获取已在第 3 步中创建的 Shipment 对象的实例?
  7. 费率显示在 UI 上
  8. 用户然后单击处理发货按钮
  9. UI Controller 处理按钮点击事件并调用 Use Case Interactor a 的 PROCESS_SHIPMENT 方法。用例交互器是否需要像第 3 步一样再次调用 Shipment 工厂,或者用例交互器是否可以获取已在第 3 步中创建的 Shipment 对象的实例?

装运对象的状态应该是 UI 控制器、用例交互器还是存储库上的实例变量?理想情况下,我想将它保存在某个地方,这样我就不需要在每次用户单击 UI 上的按钮时都创建一个新对象。

先感谢您!

0 投票
2 回答
1538 浏览

design-patterns - 端口和适配器架构中的域模型实际上是什么?

当我最近阅读了很多关于端口和适配器架构的内容时,我偶然发现了这段代码,它是按照上述架构构建的应用程序的一部分:

由于端口和适配器架构的主要意图是将域层与任何技术细节和实现分开和隔离,并记住这个用户实体实际上是域层,它不包含对 java 的依赖持久性库?据我了解,域层仅负责实现用例。对于这种架构中的域层实际上应该是什么,我真的很困惑。

0 投票
5 回答
22474 浏览

architecture - 洋葱结构与六边形相比

它们之间有什么区别吗(洋葱|六边形),据我了解,它们是相同的,它们专注于应用程序核心的领域,并且应该与技术/框架无关。

如果有的话,它们之间有什么区别?

此外,我认为使用一个比另一个甚至反对 N 层架构没有真正的优势,如果做得不好,仅仅遵循它们中的任何一个都不会产生任何影响

使用其中一个有什么好处,为什么要使用它?什么时候使用它?

谢谢

0 投票
2 回答
448 浏览

java-8 - Java 8 应用层和具体的输出转换

我有一个 gradle 多项目,其中有 2 个子项目试图模拟六边形架构:

  1. 休息适配器
  2. 应用层

我不希望应用程序服务公开域模型,也不希望强制将特定表示作为输出。所以我想要应用程序服务消耗 2 个参数(一个命令和something)并返回一个T. 客户端配置服务。

其余适配器无法访问域模型,因此我无法返回域模型并让适配器创建其表示。

怎么样something。我试过 :

  1. 有签名<T> List<T> myUseCase(Command c, Function<MyDomainModel, T> fn)。应用层是转换函数的所有者(因为签名使用 MyDomainModel)并公开函数字典。所以其余控制器引用了 Fn 之一。有用。我正在寻找更好的方法。如果它存在,则更优雅的方式。
  2. 有一个签名<T> List<T> myUseCase(Command c, FnEnum fn)对于每个枚举我都关联了一个函数。有了这个,我发现签名更优雅:消费者从枚举中提供了它想要的转换。但不起作用,因为通用方法无法编译。无法解决。目前,我没有找到方法。
  3. 与 java 8 消费者或供应商或其他东西有关的东西,但我没能解决问题。

我觉得对于这类问题有一个更优雅的解决方案:接受一个函数的服务,该函数可以转换和构建客户端提供的输出。

0 投票
1 回答
439 浏览

architecture - 在六边形架构中动态加载适配器的最佳方式?

我想将六边形架构应用于我的userManagement boundedContext. 所以我想定义 2 个端口(一个用于UI,另一个用于serviceBus集成,用于侦听来自另一个服务的事件)。

问题是我是否想使用不同的技术WCFOwinadapters来实现UI port(我不确定是否应该调用它UI port,基本上这是操作所在的接口) 。如何将它们添加到我的控制台应用程序?CreateNewUser, BlockUser, CheckIfUserExists

假设我想Soap adapter使用WCF实现并使用Owin休息。在许多示例中,我看到人们为每个驱动适配器创建单独的控制台应用程序,即:MyDDD.UserManagement.Api.Rest.HostMyDDD.UserManagement.Api.Soap.Host. 我想要实现的是一个主机应用程序以及以某种方式将适配器连接到它的能力。请分享你的想法!

0 投票
1 回答
111 浏览

domain-driven-design - 我的集成测试是否应该重用持久性库来访问数据库?

我有一个包含 IUserRepository 抽象的 DDD.IdentityAccess.Domain 的实现。还有另一个 dll -> DDD.IdentityAccess.Persistence.Sql 包含 IUserRepository 抽象的实现。现在,我想一直测试我的 IdentityAccess -> Api -> DomainLogic -> 数据库。让我们看一个“CreateUser”用例。我通过我的 httpClient 调用“CreateUser”,然后我想查询 db 以检查用户是否实际添加到 db。我的rest api上没有'user/id'操作,所以唯一的选择是使用DDD.IdentityAccess.Persistence.Sql,但它会给DDD.IdentityAccess.Domain带来依赖。我应该重用这个 dll,还是创建另一个与域无关的 DAL?