首页 > 解决方案 > DDD 在更新前使用实体中的存储库进行验证

问题描述

假设我不想更新 Person 实体的昵称,并规定此昵称最多可供 5 个其他人使用。我该怎么做?

在调用更新之前,我应该在域服务中进行验证吗?

还是将 personRepository 传递给 PersonEntity.update(...) 方法,并在实体更新方法中进行搜索和验证是否更好?


补充说明:

@plalx 创建附加实体来执行此规则的想法很聪明。

当我阅读 DDD 时,我经常读到不推荐将存储库传递给实体(如果不是邪恶的话)。这就是为什么我试图找到一个我们需要存储库或实体中的其他服务的示例)。我实际上不知道是否只有传递存储库是不好的并且服务是好的,或者同样不鼓励将服务传递给实体。

让我们假设这条规则不是那么难和重要的业务规则,并且我们可以有多个类似的规则(如此之多,为我们要验证的每个属性的每个规则创建一个额外的实体有点过度设计)。假设我们可以在并发更新的情况下允许 6 或 7 个相同的昵称(我们只想将它们限制在相当小的数量)并检查

personRepository.usageOfNickname(this/person.nickname) < 5

足够了。在这种情况下,从 DDD 的角度来看,哪种设计更好?

标签: oopdomain-driven-designrepository-patternddd-repositoriesaggregateroot

解决方案


还是将 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为跟踪用户与其昵称之间关系的唯一实体(昵称根本不是UserAR 的一部分)。

最后,我在最终一致性方面没有太多经验,希望其他人能阐明什么是正确的方法,但如果您不想在每个事务中修改许多 AR,那么我认为您可以使用一些策略。

例如,您可以让 > 6 个人使用相同的昵称,但随后有一个流程来检测违规行为并使用昵称政策违规标记这些人,他们有宽限期来更改他们的昵称,否则它将被设置为else 自动(或任何其他补偿动作)。请注意,您仍然可以使用域服务进行检查以限制违规次数。

如果你想防止违规,你也可以使用某种saga,首先保留新昵称,然后释放旧昵称,最后更改该人的昵称。一个人会在短时间内实际保留 2 个昵称,但绝不会出现昵称被使用超过 6 次的情况。


推荐阅读