在 DDD 和 CQRS 中使用计算属性设计领域模型的最佳实践

问题描述 投票:0回答:1

我目前正在开发一个遵循领域驱动设计 (DDD) 原则的系统,我遇到了一个设计挑战,我相信社区中的其他人也可能遇到过。

在我的领域模型中,我有具有计算属性的实体。例如,我有一个实体类,其中包含一个方法 IsActive,该方法根据某些域逻辑确定活动状态:

public bool IsActive() 
{
    return !_disabled && expiryDate > DateTime.UtcNow && _owner.IsActive;
}

此属性的域逻辑可能会变得复杂,因为它检查多个字段并可能调用关联实体或聚合的方法。

虽然这将域逻辑巧妙地封装在实体内,但当我尝试使用命令查询职责分离 (CQRS) 进行读取时,它提出了挑战。通常,使用 CQRS,我更喜欢直接从数据库加载数据传输对象 (DTO),这样性能更高。然而,由于这种计算的动态性质,我发现我需要先加载整个聚合,然后执行投影、过滤等。

在考虑读取方面时尤其令人担忧,我可能需要加载许多聚合,这可能会对性能产生重大影响。

考虑到这种情况:

如何在实体中正确封装域逻辑和确保读取端的高效查询之间取得平衡? 是否有任何最佳实践或模式来解决这种情况? 在设计领域模型时,尤其是在使用 CQRS 时,是否有推荐的方法来处理此类计算属性? 我非常感谢社区的见解和指导。预先感谢!

domain-driven-design cqrs clean-architecture
1个回答
0
投票

需要明确的一件事是,聚合是写入端关注的问题(因为它成为聚合的原因是它作为一个单元“变化”)。如果将 DDD 与 CQRS 相结合,则聚合结构与您在任何读取模型中公开的内容之间没有必然的关系。事实上,有一个思想流派建议严格根据它们将处理的命令来设计聚合,这些命令需要相互一致(在 CAP 意义上,而不是“C”的 ACID 意义上):如果某些状态(无论是字段还是计算属性)在聚合中都不会影响命令的处理方式,那么它根本不需要在聚合中。 一个非常有用的通用策略是对读取模型进行非规范化:如果有一个查询将使用

IsActive

,则将其设为一个字段。读取模型的重点是优化查询:封装并不能真正帮助您。您还可以自由地为特定查询定制模型(如果一个查询的选择会对另一个查询产生不利影响,您可以让每个查询由不同的读取模型处理):您也许可以选择一个模型,让您在执行查询的过程中计算属性。

顺便说一句,我通常会敦促您在计算不纯粹的聚合属性(例如取决于当前时钟)时保持一定程度的谨慎。我强烈考虑使用一个调度程序(可能由一个仅跟踪到期日期的简单读取模型提供)来代替时钟,安排处理一个在适当时间将聚合移动到到期的命令。

© www.soinside.com 2019 - 2024. All rights reserved.