domain-driven-design - 在 DDD/CQRS 中,ReadModel 是否应该充当 ViewModel,如果不是,那么映射的责任在哪里?
问题描述
假设读取模型ProductCatalogueItem
是从聚合/写入模型构建的,与写入模型分开存储,并包含每个可供销售的产品,并具有以下属性:
- 基础知识:
product_code
,name
,price
,number_of_available_stock
, - 文档:
short_description
,description
,... - 产品特性:
weight
,length
,depth
,width
,color
, ...
而且,有两种观点:
- 产品列表包含可用产品报价的列表/表格/网格,并且视图只需要以下基本属性:
product_code
,name
,price
,number_of_available_stock
, - 显示所有属性的产品详细信息 - 基础知识、文档、产品特性。
自然会想到两个 ViewModel:
ProductCatalogueListItem
仅包含基本属性,ProductCatalogueItemDetails
包含所有属性。
现在,.. 有两个选项(我可以看到)。
- ViewModel 是 ReadModel 的 1:1 表示
因此,这是两个读取模型,而不是一个,ProductCatalogueListItem
并且ProductCatalogueItemDetails
. 而且,读取服务将有两种方法:
List<ProductCatalogueListItem> searchProducts(FilteringOptions)
,ProductCatalogueItemDetails getProductDetails(product_code)
.
并且,控制器直接返回这些模型(或映射到传输层的 dto)。
这里的问题是过滤,.. 读取服务是否应该在不同的读取模型上执行搜索查询,而不是从方法调用返回?因为,ProductCatalogueListItem 没有足够的信息来执行过滤。
- ViewModels 是 ReadModels 的另一个项目
读取服务将有两种方法:
List<ProductCatalogueItem> searchProducts(FilteringOptions)
,ProductCatalogueItem getProduct(product_code)
.
而且,从 ReadModels 到 ViewModels 的映射是由上层(可能是控制器)完成的。
过滤没有问题,...但是,还有另一个问题,即离开域层的数据多于实际需要的数据。而且,控制器会随着更多逻辑而增长。由于不同的传输技术可能有不同的控制器,因此映射代码可能会在这些控制器中重复。
根据 DDD/CQRS,哪种组织职责的方法是正确的,或者完全是其他的?
重点是:
- 我应该建立两个读取模型,并使用一个搜索,然后返回另一个?
- 我应该构建使用的单读模型,然后映射到有限视图以仅包含视图的基本信息吗?
解决方案
首先,你做了一个错误的断言:
...读取模型 ProductCatalogueItem 是从聚合/写入模型构建的...
读取模型不知道聚合或写入模型的任何内容,您直接从数据库构建读取模型,返回 UI 所需的数据。
因此,视图模型是读取模型,它不涉及写入模型。这就是 CQRS 存在的原因:具有不同的模型,即读取模型,以优化查询以返回客户端所需的数据。
更新
我将尝试更好地解释自己:
CQRS 只是根据方法类型将一个对象一分为二。有两种方法类型:命令(任何改变状态的方法)和查询(任何返回值的方法)。就这样。
当您将此模式应用于应用程序的服务边界时,您将拥有一个写入服务和一个读取服务,因此您可以以不同方式扩展命令和查询处理,并且您还可以拥有两种模型。
但是 CQRS 没有两个数据库,没有消息传递,没有最终一致性,没有从写入模型更新读取模型,也不是事件溯源。您可以在没有它们的情况下进行 CQRS。我这样说是因为我在你的断言中看到了一些误解。
也就是说,读取模型的设计是根据用户希望在 UI 中看到的信息来完成的,即读取模型是视图模型,它们之间没有映射,它们都是同一个模型。您可以在下面的参考文献 (3) 和 (6) 中了解它。我认为这是您整个问题的答案。我不明白的是过滤问题。
一些很好的参考
(1) http://codebetter.com/gregyoung/2010/02/16/cqrs-task-based-uis-event-sourcing-agh/
(2) http://www.cqrs.nu/Faq/command-query-responsibility-segregation
(3) “实施领域驱动设计”一书,作者 Vaughn Vernon。第 4 章:架构,“命令-查询职责分离,或 CQRS”部分
(4) https://kalele.io/really-simple-cqrs/
推荐阅读
- python - 如何在 HoloViews 中获取小部件“处理程序”
- symfony - 在 Twig 中打印包含需要依次打印的子变量的变量
- css - 更改 wordpress 中的字体系列覆盖 WPBakery 页面构建器
- arrays - 如何在 PostgreSQL 中创建类型?
- apache-spark - 分别配置 Spark 和 Hadoop(haddop 和 spark 使用哪个版本)
- madge - Madge 跳过导入的打字稿文件
- powershell - 删除 Pester 模拟函数
- java - 我想创建一条将 2 个 JTable 连接在一起的直线
- docker - 使用 jhipster monolith 应用程序构建 docker 映像并推送到我的 dockerhub
- node.js - Unhandled promise rejection. This error originated either by throwing inside of an async function ... Node + Puppeteer