如何使用CQRS依据业务功能进有效地切割代码库?


项目的结构与讨论空格与制表符或缩进大小一样敏感。当我们进入某人的公寓时,即使它是根据宜家的默认设置进行装饰的,它看起来也总是有些不同。这是绝对合理的。不同的公寓大小和房间布局会有所不同。
它类似于我们的软件项目。每个都有略微不同的假设和特征,但可以区分出一些共同的特征,对吗?
和其他人一样,我从严格的技术拆分开始,在那里我有一个用于服务、合同、映射器、数据模型等的文件夹。当时,我认为那时我拥有一切原始和适当的东西。然而,这个假设已经改变。
前段时间,我在 .NET 示例中更改了我的事件源。现在,它们完全按照业务功能进行划分,而不是技术功能。我为什么这样做?
我做出这个决定的道路是渐进式的。当我创建存储库时,我正处于将系统划分为模块的阶段。每个模块都是一个单独的项目。该项目的第一个部门是聚合,因此是业务子模块。然而,在文件夹内,划分仍然是技术性的。命令、事件、值对象等分布在不同子文件夹。
这是一个可以接受的解决方案,但我仍然感到不安。突破发生在……
...我在 Angular 上工作的时间更长。在“新 Angular”中,将代码库分解为组件是必不可少的。在同一个文件夹中,我们保留了 HTML 视图和组件 TypeScript 代码。更重要的是,我们还将单元测试放在一起!起初,我觉得它很奇怪,但后来我意识到它有助于提高效率。
 
通常,当我们处理给定的业务功能时,我们使用相同的模板:

  • API端点,
  • 请求类型,
  • 一些类来处理它,
  • 数据模型。

传统上,我们将它传播到不同的地方。我们必须在多个文件夹、文件等之间跳转。当然,IDE 快捷方式可以帮助我们做到这一点,但我们必须不断地从一个地方跳到另一个地方这一点并没有改变。我们总是切换上下文。当我们被电话或社交媒体上的通知打扰时,我不需要告诉你,你会自己知道这种情况下工作有多低效。
文件之间的跳转可以与此进行类比:当我们把所有东西都放在附近时,最好是在一个文件中,我们可以更有效率。我们甚至可能不需要三台显示器或一台像橄榄球运动员肩膀一样宽的显示器。
保持这样的分割对 CQRS 非常有效。它隔离了我们的操作并垂直而不是水平地分割应用程序代码。事件溯源引入了更重要的改进,因为我们不需要统一的数据模型(例如 EntityFramework 中的 DBContext)。每个操作都以我们可以保存在命令文件夹中的事件。
它看起来像这样:

在Carts这个业务功能文件荚下有以下子文件目录:

  • 带有命令和处理程序的文件,例如Carts/AddingProduct/AddProduct.cs
  • 带有事件命令的文件正在创建,例如Carts/AddingProduct/ProductAdded.cs
  • 使用处理程序查询,例如Carts/GettingCartById/GetCartById.cs
  • 使用投影读取模型,例如Carts/GettingCartById/CartDetails.cs

当然,没有 CQRS 或 Event Sourcing 的项目也可以从中受益。经验法则:将一起变化的东西放在一起。除了减少上下文切换之外,这种拆分还可以提高对业务中正在发生的事情的理解,管理依赖关系甚至最终扩展。更容易将特征提取到专用微服务中。