oop - DDD 在更新前使用实体中的存储库进行验证
问题描述
假设我不想更新 Person 实体的昵称,并规定此昵称最多可供 5 个其他人使用。我该怎么做?
在调用更新之前,我应该在域服务中进行验证吗?
还是将 personRepository 传递给 PersonEntity.update(...) 方法,并在实体更新方法中进行搜索和验证是否更好?
补充说明:
@plalx 创建附加实体来执行此规则的想法很聪明。
当我阅读 DDD 时,我经常读到不推荐将存储库传递给实体(如果不是邪恶的话)。这就是为什么我试图找到一个我们需要存储库或实体中的其他服务的示例)。我实际上不知道是否只有传递存储库是不好的并且服务是好的,或者同样不鼓励将服务传递给实体。
让我们假设这条规则不是那么难和重要的业务规则,并且我们可以有多个类似的规则(如此之多,为我们要验证的每个属性的每个规则创建一个额外的实体有点过度设计)。假设我们可以在并发更新的情况下允许 6 或 7 个相同的昵称(我们只想将它们限制在相当小的数量)并检查
personRepository.usageOfNickname(this/person.nickname) < 5
足够了。在这种情况下,从 DDD 的角度来看,哪种设计更好?
将 personRepository 传递给 Person 实体
class Person { ... boolean changeNickname(newNickname, nicknameUsageService) { if (personRepository.usageOfNickname(this.nickname) < 5) { this.nickname = newNickname; return true; } else { return false; //or throw } } }
这对我来说似乎是最明显和最直接的,好处是逻辑包含在实体中,但困扰我的是这种将存储库传递到实体的可悲传递以及埃文斯不鼓励这样做的感觉
而不是将 personRepository 传递给 Person Entity ,将 personService 传递给 Person Entity(类似于@plalx 的示例)——将服务传递给 Entity 比存储库更好吗?
- 在服务中进行验证,类似于在@plalx 示例
changePersonNickname(personId, newNickname){...}
中,但在@plalx 的示例中使用服务似乎是合理的,因为它在两个实体上运行,这里我们只有一个实体,我担心如果把这个逻辑放在服务而不是实体中担心不会转向贫血域模型并离开 DDD。
解决方案
还是将 personRepository 传递给 PersonEntity.update(...) 方法,并在实体更新方法中进行搜索和验证是否更好?
不过,这并不能防止规则因并发而被违反,因为一旦您检查personRepo.usageCountOfNickname(nickname) <= 5
它可能会立即更改。
如果您想要强一致性,您可以引入NicknameUsage
聚合根来执行该策略。您将在一个事务中修改超过 1 个 AR,但这可能不是什么大问题,因为无论如何都不太可能对相同的昵称有很多争用,对吧?
例如
changePersonNickname(personId, newNickname) {
transaction {
person = personRepository.personOfId(personId);
currentNicknameUsage = nicknameUsageRepository.usageOfNickname(person.nickname);
currentNicknameUsage.release();
newNicknameUsage = nicknameUsageRepository.usageOfNickname(newNickname);
nicknameUsage.reserve(); //throws if 6 already
person.changeNickname(newNickname);
}
}
您也可以将昵称的使用管理逻辑封装在域服务中,然后将其注入 AR 的changeNickname
操作中。
例如
class Person {
...
void changeNickname(newNickname, nicknameUsageService) {
nicknameUsageService.reserveAndRelease(newNickname, this.nickname);
this.nickname = newNickname;
}
}
如果您希望消除NicknameUsage
与关系不同步的所有风险,User-Nickname
您可以设计NicknameUsage
为跟踪用户与其昵称之间关系的唯一实体(昵称根本不是User
AR 的一部分)。
最后,我在最终一致性方面没有太多经验,希望其他人能阐明什么是正确的方法,但如果您不想在每个事务中修改许多 AR,那么我认为您可以使用一些策略。
例如,您可以让 > 6 个人使用相同的昵称,但随后有一个流程来检测违规行为并使用昵称政策违规标记这些人,他们有宽限期来更改他们的昵称,否则它将被设置为else 自动(或任何其他补偿动作)。请注意,您仍然可以使用域服务进行检查以限制违规次数。
如果你想防止违规,你也可以使用某种saga,首先保留新昵称,然后释放旧昵称,最后更改该人的昵称。一个人会在短时间内实际保留 2 个昵称,但绝不会出现昵称被使用超过 6 次的情况。
推荐阅读
- javascript - 我得到了正确的结果,但在控制台选项卡的调试器中,我总是看到两倍的预期结果
- image - 如何在 Swift 中将 3 张图像(100x100 像素)合并为一张新的大图(300x100 像素)
- sql - 如何在 MS SQL 中将十六进制编码的字符串转换为整数?
- apache-spark - Spark 无法识别来自 String 的换行符、& 等
- python - 'int' 对象没有属性 'append' 没有真正的原因
- javascript - ASP.net 找不到 javascript 函数
- azure - 使用带有 Flask 的 Azure Functions,我如何实现 2 个 Azure 函数 webhook 以同时从控制台运行
- node.js - 无法 Docker 将基于 nuxt 的快速服务器端口转发到主机
- java - 为什么我的计时器没有每秒打印一次?
- spring-boot - 使用 Sprint Boot 和 GraphQL 进行集成测试时出现 Jackson 错误