【问题标题】:How to structure separation and namespaces in CQRS?如何在 CQRS 中构造分离和命名空间?
【发布时间】:2020-04-24 14:40:39
【问题描述】:

我正在寻找有关如何在 CQRS 结构化应用程序中构建命名空间的建议。

目前,命令端和查询端在每个有界上下文中位于同一个命名空间中,但随着复杂性的增加,它开始产生问题。

目前该结构有以下文件夹,每个文件夹都包含实现:

Application
+ Api
+ Cli
+ Web
Domain
+ Action (Command and Command Handlers in one - we are not using a CommandBus) 
+ Event
+ Model
+-- Project
  |-- Project.file
  |-- ProjectRepository.file
Infrastructure
+ Consumer (Projections and ProcessManagers)
+ EventStore
+ Persistance (Denormalized read side)
+-- Project
  |-- SqlProjectRepository.file
Common (Supporting namespace)

现在的问题是域模型当前包含实体和事件源聚合根,它们基本上分别只是查询端和命令端的一部分。

查询端和命令端的聚合没有重叠。

在对分离的重构中,应该在哪里进行切片?

建议一

一个完整的切片产生一个查询和一个命令端,这意味着即使应用层也有读写端。

建议二

仅在域层上制作的切片,因此查询端包含读取模型的(相当贫乏的)实体,而命令端包含事件、事件来源聚合根等。

如果我的不适用,请提出第三个建议。谢谢。

【问题讨论】:

  • 请在下面查看我的答案,只是想补充一点意见,您的应用层对我来说是错误的,并不罕见 - 但错误。您的“应用程序”不是 API、Cli 或 Web - 它们都只是实现层(用户或应用程序接口),您的应用程序是在 API、Cli 和 Web 调用之后发生的业务逻辑。
  • 感谢您的评论。基本上这是一个简单的解决方法,因为这些 UI 部分是分开的,并且可以更改它们的命名空间。这使得应用层非常空,因为动作包含业务逻辑,但它们目前是域层的一部分——正如许多文献中所推荐的那样。你建议他们移到应用层吗?
  • 如果您在下面查看我的答案,应用程序是您进行分离的主要文件夹。 Imo,应用程序应包含两个文件夹 - 命令和查询。然后在其中的每一个中,您都有服务(处理程序 - 取决于实现 - 验证命令、检查身份验证等、询问域问题)、域(命令模型/域模型等)和数据(保存状态/加载查询模型) .这 3 层的实现 - 对于命令与查询将有所不同。

标签: namespaces domain-driven-design cqrs event-sourcing


【解决方案1】:

这是我采用的方法 - 基于 CQRS 和 DDD,并指出我们的 DDD 扩展到拥有单独的解决方案(.NET - 用于不同域有界上下文的 Web API)。

其次,我们所有的基础架构代码、身份验证处理和共享代码 - 都在私有包管理器中完成,这消除了单个解决方案的一些混乱,我们在 StartUp (DI) 中对其进行 DI。另请注意,我们使用 EntityFramework 作为我们的数据库实现。

但是,鉴于此,我和我的团队是这样区分 CQRS 和 DDD 的。

+ App
+- Command
+-- Application.Command
+-- Data.Command.EntityFramework
+-- Domain.Command

+- Query
+-- Application.Query
+-- Data.Query.EntityFramework
+-- Domain.Query

+ Build
+- Pipeline

+ Database
+- Data.Database.EntityFramework
+- Data.Database.Model

+ Test

Api (API app)
Cli

【讨论】:

  • 如果您想了解有关这些项目内部的更多信息,很高兴为您提供。
【解决方案2】:

目前该结构具有以下文件夹,每个文件夹都包含实现...

这很不幸。

如果您将名称空间安排得更紧密,那么您可能会对维护负担感到更满意。您的 FrobMarbleRepository 属于 frobmarble 命名空间,而不是 repository 命名空间。

我认为我不同意 Jimmy Bogard 在这里的完整分析,但关注特征的教训很重要

https://jimmybogard.com/vertical-slice-architecture/

如果您碰巧需要在一个功能中为两个不同的想法使用相同的名称,那么您最终可能会将该功能拆分为两个或多个命名空间;另一方面,需要重用名称可能表明您实际上正在处理多个功能。

【讨论】:

  • 我不确定我是否理解你为什么说这很不幸。实现不遵循类型,它们遵循功能。项目聚合根当前与它自己的 ProjectRepository 一起位于 Domain/Project 中,ProjectRepository 是由例如实现的接口。基础设施/持久性/项目/SqlProjectRepository。可能是我的解释没有说清楚。
  • 我的意思是域/模型/项目 :-)
猜你喜欢
  • 1970-01-01
  • 2016-03-16
  • 2012-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-31
  • 2015-07-28
  • 1970-01-01
相关资源
最近更新 更多