第IV部分有效应用程序的设计模式
第IV部分有效应用程序的设计模式
- 第IV部分有效应用程序的设计模式
- 第23章:应用程序用户界面的架构设计
- 23.1设计考量
- 23.2示例1:用于非分布式有界上下文的一个基于HTMLAF的、服务器端的UI
- 23.3示例2:用于分布式有界上下文的一个基于数据API的客户端UI
- 23.4要点
- 第24章:CQRS:一种有界上下文的架构
- 24.1为两个上下文维护单个模型的挑战
- 24.2用于复杂有界上下文的一种更好的架构
- 24.3 命令端:业务任务
- 24.4查询端:领域报告
- 24.5 对CQRS的误解
- 24.6可以扩展应用程序的模式
- 24.7要点
- 第25章:命令:用于处理业务用例的应用程序服务模式
- 25.1区分应用程序逻辑和领域逻辑
- 25.2应用程序服务模式
- 25.3测试应用程序服务
- 25.4要点
- 第26章:查询:领域报告
- 26.1有界上下文中的领域报告
- 26.2跨有界上下文的领域报告
- 26.3要点
第IV部分有效应用程序的设计模式
第23章:应用程序用户界面的架构设计
23.1设计考量
23.1.1 占有式UI与构成式UI的对比
-
自治性
-
授权性
23.1.2 HTML API与数据API的对比
23.1.3客户端与服务器端聚合/协作对比
23.2示例1:用于非分布式有界上下文的一个基于HTMLAF的、服务器端的UI
23.3示例2:用于分布式有界上下文的一个基于数据API的客户端UI
23.4要点
- 后端有界上下文的结构将极大影响用户界面,反之亦然。
- 决定哪个团队占有一个UI将显著影响该团队的活力以及工程解决方案。
- 可以在客户端上用JavaScript或者在服务器端使用你喜欢的技术从多个有界上下文中提取数据。
- 客户端构成可以降低复杂性以及一个额外服务器端组件的耦合。
- 服务器端聚合和编排免除了对JavaScript的依赖以及在浏览器中运行的性能约束。
- UI可以由HTML构成,或者它们可以从每个有界上下文中以JSON或XML格式提取数据。
- 使用HTML的构成为每个有界上下文提供了更多的控制,但分散了展现的关注点。
- 数据的聚合会将展现关注点隔离到单个Web应用程序,但从每个有界上下文中移除了展现关注点的占有关系。
第24章:CQRS:一种有界上下文的架构
CQRS(命令查询职责分离)是一种简单模式,可以将其应用到有界上下文。它会将领域
模型分离成两个模型:读取模型和写入模型(有时也称为事务模型)。
24.1为两个上下文维护单个模型的挑战
24.2用于复杂有界上下文的一种更好的架构
24.3 命令端:业务任务
24.3.1显式建模意图
24.3.2不受展现干扰所影响的模型
24.3.3处理业务请求
24.4查询端:领域报告
24.4.1直接映射到数据模型的报告
24.4.2从领域事件中构建的具体化视图
- 订阅者订阅写事件,在订阅者里边更新了视图数据
24.5 对CQRS的误解
24.5.1 CQRS很难
- 不难,它是应用在领域模型层的单一职责原则的实现。它有助于解决展现模型与事务模型不一致时所引发的复杂性。CQRS不会指定框架、多个数据库,或者设计模式。它只表明,为了更高效,应该单独处理两个上下文。它是一种概念上的思维转换,而非需要接受的复杂模式和原则的集合。
24.5.2 CQRS是最终一致的
- 最终一致性是使用在进程外更新的读取模型并且异步于事务模型更新的实践。这并非CQRS的先决条件,但它通常用于让模型的读取端能够扩展。最终一致的读取模型会为应用程序增加一个复杂性的额外层,正如检查查看其操作结果的用户可能会对看到过期界面感到吃惊一样。CQRS不需要你保持最终一致性。可以使用相同的数据库和事务来更新读取模型结构。实际上,如果大量使用了缓存,那么你的应用程序的读取存储可能已经是最终一致的了。如果接受CQRS的原因是由于展现和事务问题之间不一致所造成的复杂性,那么一开始可以尝试即时一致,只有在碰到性能问题时才应用最终一致性。
24.5.3模型需要源自事件
- 使用事件溯源是同时构建读取和写入模型的有效方法;不过,使用事件溯源或实际上使用CQRS的领域事件并没有先决条件。事件溯源是确保审计追踪准确无误这个问题的解决方案,但它确实让读取模型的构建更为容易,因为可以从历史事件数据中创建你想要的任何投影。
24.5.4命令应该是异步的
- CQRS不会坚持以一劳永逸的方式发送命令。对于高度协作的领域,其中会有多个用户对相同数据进行变更,使用异步命令就是合理的。这使得这些命令可以依次被处理,并且允许应用程序扩展以及不会被负载所拖垮。然而,不返回与成功或失败有关的确认的命令需要其他方法来通知用户一项操作是否成功。这可以通过电子邮件或者处理失败消息的额外行为来达成。在商品购买的例子中,这种情况可以是为顾客采购替代品,而不是让订单直接完全失效。
24.5.5 CQRS仅适用于消息传递系统
- 如果在谋求应用一个最终一致性读取存储或者异步处理命令,那么使用一个消息传递框架大概会是一个好主意。不过,如果并非这样的话,那么将一个消息传递系统添加到你的应用程序只会增加无用的复杂性。
24.5.6需要将CQRS用于领域事件
- 使用事件构建一个具体化读取模型是保持读取和写入模型分离的一种有效方法;不过,它并非关键,并且可以使用其他方法创建具体化读取存储。如第21章“存储库”中所述,聚合可以通过使用备忘录模式来揭示状态。还可以使用第26章中介绍的一些模式以非阻塞式方式从领域对象中直接提供信息呈现。最后,可以基于写入模型的关系数据模型构建视图。
24.6可以扩展应用程序的模式
24.6.1扩展读取端:一个最终一致的读取模型
-
对用户体验的影响
- 需要保存最终一致性,允许出现临时不一致,不一致的数据得有失效时间
-
使用读取模型合并许多有界上下文
-
使用报告数据库或缓存层
24.6.2扩展写入端:使用异步命令
-
命令验证
-
对用户体验的影响
- 成功与非是另外一个程序控制,在电子商务的订单种常见。
24.6.3 对一切进行扩展
24.7要点
-
如果一个领域模型不能在无损的情况下满足复杂展现和领域逻辑的需要,那么就可以使用 CQRS。将该模型划分成两个特定模型:一个用于读取上下文,一个用于写入上下文。
-
通过使用分离,你就能在用于行为的写入端上塑造聚合并且围绕不变条件而非报告需要来设计聚合。
-
在读取端为报告需要定制的视图模型可以绕过领域模型并直接从数据库中提取数据,可以调整它以便提供更好的性能。
-
CQRS 是一种简单模式。它是在模型层面应用单一职责原则、创建两个模型而非一个模型的简单情况。
-
CQRS 通常被错误地看作消息传递、最终一致、领域事件和事件溯源的应用程序。尽管以上所有这些都能增强系统并且适用于特定上下文,但它们并非应用 CQRS 模式的要素。
-
如果有大量的读取操作,那么通过允许你借助从写入端数据模型中分离读取端数据模型的方式引入最终一致性,CQRS 就可以让你进行扩展。
-
如果正在为具有对相同聚合集的大量写入操作的高度协作式领域设计系统的话,则可以引入在分离的写入端上处理的异步请求。
-
对写入模型或读取模型进行扩展并且引入最终一致性存在一些取舍考量。业务人员必须理解并接受这些取舍,必须从用户体验方面考虑这些取舍。
-
CQRS 让你的系统能够同时在读取和写入端具有无限的扩展性。
第25章:命令:用于处理业务用例的应用程序服务模式
25.1区分应用程序逻辑和领域逻辑
25.1.1 应用程序逻辑
25.1.2来自应用程序服务角度的领域逻辑
25.2应用程序服务模式
25.2.1命令处理程序
- 将命令模式和责任链模式做了组合 —— 也可以称之为一种“命令处理管道”或者“命令中间件链”。
对比项 | 命令处理器模式 | 责任链模式 |
---|---|---|
🎯 目的 | 将“命令”与其“处理逻辑”分离,解耦发送者与执行者 | 动态决定由谁来处理请求,可以链式尝试多个处理器 |
🧱 结构 | 通常有命令、处理器注册表(Dispatcher 或 HandlerFactory),每个命令由唯一处理器处理 | 一条链,每个处理器都有机会处理请求,并可选择传递给下一个 |
🔄 请求流动 | 一次命中:命令由 一个特定处理器 处理 | 多次尝试:请求沿着链条传递,直到被处理或被忽略 |
🔧 控制权 | 调度器(如 CommandBus)控制哪一个处理器来执行命令 | 链条内部控制是否继续传递请求 |
💡 典型用途 | CQRS、任务调度系统、领域驱动设计中的 UseCase 执行器 | 责任委托(如权限检查、日志处理、过滤器、异常处理链) |
25.2.2 发布/订阅
25.2.3 请求/回复模式
25.2.4 async/await
25.3测试应用程序服务
25.3.1使用领域专业术语
25.3.2测试尽可能多的功能
25.4要点
- 应用程序服务和一层服务层允许你将技术问题和领域逻辑分离。
- 技术问题包括事务、数据库连接和电子邮件。
- 应用程序服务负责协调领域以便执行完整的业务用例。
- 在与领域通信时,应用程序服务应该调用领域对象上具有高水平表述性的API。
- 应用程序服务的一个重要职责是保护领域结构,这要通过使用更稳定的接口呈现较高级别的层和外部组件以便进行耦合来实现。
- 可以在服务层中使用像命令处理程序模式和异步模式这样的设计模式。
- 测试应用程序服务是在覆盖高比例的实现时,用通用语言表述高级别行为或完整业务用例的一个机会。
第26章:查询:领域报告
26.1有界上下文中的领域报告
26.1.1从领域对象中派生报告
26.1.2直接访问数据存储
26.1.3 从事件流构建投影
26.2跨有界上下文的领域报告
26.2.1复合UI
26.2.2单独的报告上下文
26.3要点
- 可以使用各种能避免领域耦合性的工具和技术来创建报告。
- 一些报告会操作一个有界上下文中的数据,但另一些可能需要从多个有界上下文中查询数据。
- 从领域对象映射到视图模型通常是最快速的方法,但它只能提供很少的对于低层次数据访问的控制权。
- 像中介模式这样的设计模式可用于构建报告或者兼顾要权衡的问题,比如耦合性。
- 如果需要低效的查询,可以直接访问数据存储,但重复概念和对于不要做重复工作(DRY)的原则的违背是一个需要注意的问题。
- 查询主数据库和查询非规范化视图缓存是两种直接数据访问方法。
- 非规范化视图缓存会将所有困难的工作移动到非规范化处理过程中,从而实现更简单的查询。
- 使用事件流的投影也会牺牲后台处理来换取更简单的读取。
- 可以通过在一些情况下使用复合UI以及在另一些情况下使用单独的报告上下文来从多个有界上下文中查询数据。