在 Hexagonal 架构中显示 UI 上的数据

Showing data on the UI in the Hexagonal architecture

我正在学习 DDD 和 Hexagonal 架构,我想我已经掌握了基础知识。但是,有一件事我不确定如何解决:我如何向用户显示数据?

因此,例如,我得到了一个简单的域,其中包含一个具有某些功能的 Worker 实体(某些方法导致实体发生更改)和一个 WorkerRepository,因此我可以保留 Workers。我得到了一个带有一些命令和命令总线的应用层来操作域(比如创建 Worker 和更新他们的工作时间,持久化更改),以及一个基础设施层,它实现了 WorkerRepository 和一个 GUI 应用程序。

在此应用程序中,我想向所有工作人员展示他们的一些数据,并随时修改它们。如何显示数据?

  1. 我可以参考WorkerRepository的实现。 我认为这不是一个好的解决方案,因为这样我可以跳过命令总线在存储库中插入新的 Worker。我希望所有更改都通过命令总线。
  2. 好吧,我将 WorkerRepository 拆分为 WorkerQueryRepository 和 WorkerCommandRepository(根据 CQRS),并仅提供对 WorkerQueryRepository 的引用。它仍然不是一个好的解决方案,因为 repo 返回具有更改它们的方法的 Worker 实体,以及如何保留这些更改?
  3. 我应该创建两种类型的存储库吗?一个用于域和应用层,另一个仅用于向外界提供数据。第二个不会 return 成熟的 Worker 实体,只有 WorkerDTOs 只包含 GUI 需要的数据。这样,GUI 就没有其他方式可以改变 Workers,只能通过命令总线。

第三种方法对吗?还是我强制更改必须通过命令总线是错误的?

Should I create two type of Repositories? One would be used in the domain and application layer, and the other would be used only for providing data to the outside world. The second one wouldn't return full-fledged Worker entities, only WorkerDTOs containing only the data the GUI needs.

这就是 CQRS 方法;它工作得很好。

Greg Young (2010)

CQRS is simply the creation of two objects where there was previously only one. The separation occurs based upon whether the methods are a command or a query (the same definition that is used by Meyer in Command and Query Separation, a command is any method that mutates state and a query is any method that returns a value).

您提议的 WorkerDTO 的当前术语是 "Projection"。你通常会有不止一个;也就是说,您可以在 GUI 中为工作人员的每个视图进行单独的投影。 (这有一个让视图更容易的副作用——它不需要考虑给它的数据,因为数据已经被格式化了)。

另一种思考方式是,您有一个 "write-only" 表示(聚合)和 "read-only" 表示(投影)。在这两种情况下,您都是从记录簿(通过存储库)中读取当前状态,然后使用该状态构建您需要的表示。

由于不需要保存读取的模型,您最好在读取端考虑 factory,而不是存储库。 (2009 年,Greg Young 出于同样的原因使用了 "provider"。)

一旦您迈出了分离这两个对象的第一步,您就可以开始独立处理它们的不同用例了。

例如,如果您需要扩展读取性能,您可以选择将记录簿复制到一堆从属副本,并让您的投影工厂从从属而不是主控加载。或者开始探索不同的持久化存储(键值存储、图形数据库、全文索引器)是否更合适。 Udi Dahan 在 CQRS - but different (2015) 中回顾了其中的一些想法。

"read models don't need to be saved" Is not correct.

正确;但它可能并不像它应该的那样清晰和具体。

我们不需要创建读取模型的持久表示,因为描述读取模型实例之间差异的所有信息都已被我们的写入捕获。

我们经常想要缓存读取模型(或它的表示),这样我们就可以在许多查询中分摊创建读取模型的工作。并且各种权衡可能表明缓存的表示应该持久存储。

但是,如果流星出现并破坏了我们的读取模型缓存,我们将损失工作投资,但不会丢失信息。