repository - DDD:如何防止购买在没有相关撤回的情况下被持久化
问题描述
假设员工可以使用其雇主提供的某种“福利积分”购买产品。
我有一个CreditsAccount
持有信用额度和对员工的引用Purchase
的实体,我想将其建模为持有对 CreditsAccount 的引用并且产品正在购买的实体。
如果我有一个PurchaseRepository.save()
方法,我怎样才能防止 aPurchase
被保存而不做任何撤消CreditsAccount
?
解决方案 1
Purchase 是聚合 CreditsAccount 的子实体,我没有任何 PurchaseRepository 而是帐户上的一个方法,就像CreditsAccount.AddPurchase(purchase)
在内部进行提款一样。当我使用 a 持久化 CreditsAccount 时,CreditsAccountRepository.save(account)
我还将购买保留在正确的表中。
但这意味着每次我加载 CreditsAccount 进行新购买时,我还必须加载该帐户上的购买列表,对吗?
解决方案 2
我构建了一个新PurchaseTransaction
实体,它包含对 CreditsAccount 和 Product 的引用,并提供类似PurchaseTransaction.commit()
. 在提交时,它实际上根据产品价格从参考账户中提款,其内部状态从 切换uncommitted
到committed
。然后我有一个 PurchaseTransactionRepo.save(purchaseTransaction) :
- 如果事务未提交,则不执行任何操作或引发错误
- 如果事务已提交,则将 CreditsAccount 与新金额保持一致,并在适当的表上创建 Purchase。
在这种情况下,PurchaseTransactionRepo 仅提供一种在 db 上写入的方法,因为实际上 PurchaseTransaction 并不真正属于任何表,而 PurchaseRepository 是只读的并且仅提供获取“已提交”购买的方法。
我不喜欢这个解决方案是有一个 Repo,它实际上对 PurchaseTransaction 的状态(已提交或未提交)进行了一种验证逻辑,而且 Uncommitted PurchaseTransaction 并没有真正持久化似乎具有误导性。
您能否向我推荐其他符合 DDD 概念的解决方案?
一些上下文:不是一个真正的应用程序,而是一个 kata 练习,我必须在其中将一个贫血域重构为一个丰富的域。在贫血症中,有一项服务可以在一次交易中提取帐户并保存购买。然后我可以通过 api 调用获得购买清单。我想用丰富的实体对其进行建模并“强制”无效状态不是“可持久的”
解决方案
Purchase
似乎是一个很好的 saga 候选者,它可以被认为是一个代表一个过程的实体。在没有更多上下文的情况下,我将其建模为类似于现实世界中通常处理付款的方式(为非常 CQRS/演员模式拟人化表示歉意)并且在四种状态下购买:打开、完成、资助、过期和完成.
一旦购买实体知道要购买的物品及其成本,它就可以最终确定,这使其进入最终状态并防止将来更新物品和成本。它以这种状态保存。
然后,它会尝试从员工的账户中保留信用额度,并在最后期限内完成提款或取消保留。如果成功,它会进入“已资助”状态并保存。
我可能会对已资助并影响实际提款的购买进行一些其他流程扫描(因为我们不是这里的事件采购),这允许购买由履行组件提取,以及将购买保存在状态“已完成”(如果购买过期,有很多选择。
推荐阅读
- f# - 使用 F# ExcelProvider 处理多行标题和隐藏列
- php - count():参数必须是第8行实现Countable的数组或对象
- javascript - Ajax 与 Node.js/Express 404
- spring-boot - 未调用 Spring Batch 跳过策略
- python - matplotlib 中使用颜色图的条形图
- reactjs - 盖茨比:独特的“关键”道具
- python - 没有这样的文件或目录:使用绝对路径时
- android - 如何在颤动中获取小部件的坐标、比例和旋转数据?
- fedora - 使用 Fedora 32 安装 ROCm
- python - 将函数应用于我的数据框中的所有列