Validation 不变量、验证和干燥原理
我目前正试图在DDD中消化与不变量和验证相关的信息。如果我理解正确,验证不是域的问题,应该在外部进行,以防止不变量发生。另一方面,必须在域中强制执行不变量,特别是在聚合中 让我困惑的其实是两件事:Validation 不变量、验证和干燥原理,validation,domain-driven-design,dry,invariants,Validation,Domain Driven Design,Dry,Invariants,我目前正试图在DDD中消化与不变量和验证相关的信息。如果我理解正确,验证不是域的问题,应该在外部进行,以防止不变量发生。另一方面,必须在域中强制执行不变量,特别是在聚合中 让我困惑的其实是两件事: 如何区分业务规则(不变量)和验证 如何尊重干燥原则 让我详细说明一下。考虑到我们有一个覆盖标号的域模型。两个主要参与者是投标过程组织者(组织者)和投标过程参与者(参与者)。组织者发布一份招标公告,其中包含有关阶段条款和要求(例如起始最高价格)的信息投标是一个由几个阶段组成的过程。每个阶段都有自己的
- 如何区分业务规则(不变量)和验证
- 如何尊重干燥原则
- 不变量是一些必须独立于规则而遵守的规则 商业规则。代码可能是相同的,即使在概念上是相同的 两件不同的事情
- 在这种情况下,可能会违反DRY原则,以便 遵守SRP原则
如果我错了,请纠正我。尽管我写了很多DDD代码,但坦率地说,我仍然不确定术语,也不确定是否有社区共识。我基本上已经不再使用DDD的行话了,我发现像你提出的问题那样让人头疼的问题要少得多 所以,用实用术语来说明问题的另一种方法是 被出价较低的人出价过高会让你很生气 用户,就像在封闭拍卖中出价一样。所以我们需要确保这不会发生 读取数据时的验证 当您显示屏幕供用户输入出价时,您当然会向用户确认出价必须大于上一次出价(例如通过jQuery),并且只有在
callbanks
阶段(例如仅显示表单)才能接受出价
你必须进行此验证,否则会给用户带来非常糟糕的体验-允许他们在得知拍卖结束后才进行竞价。因此我们知道,在阅读数据时,你必须以某种方式表达这些规则。然而,关键是:
您不能保证屏幕上显示的任何内容都是基于
在用户访问时,A时的信息将是真实的
执行在时间B写入数据的操作
因此,这里的验证不必是密封的。别担心。即使你复制逻辑搞砸了,我们也不能100%保证写操作会通过
写入数据时的验证
屏幕上的数据变得陈旧,正如我们在上面所观察到的:用户可能在拍卖结束后输入了出价,或者自数据显示在屏幕上以来,最低出价可能已经上升。因此,为了避免系统状态违反业务规则(因此不可靠且不完整)
在写入数据时,您必须对照业务规则进行检查
您必须在同一事务中执行此操作,否则无法执行
保证一致性
(也有最终的一致性,但这是一个完全不同的蜡球,超出了这个答案的范围。)
那对你来说意味着什么
- 您的命令处理程序是聚合/事务边界/本周人们管它叫什么
- 如果这两条规则中的任何一条被破坏,该命令将无法成功(应引发异常)。不应更改任何状态
- 应该假设该命令成功。也就是说,从您的MVC控制器中,不要为失败的命令添加任何特殊的错误处理。命令失败非常罕见,但它会不时发生
- 任何内部实现都不重要:如果您想要有两个类,一个用于执行每个规则,请直接执行。只要规则在单个事务中执行,这对业务来说并不重要。这就是您应该关注的
- 这也是您应该测试的内容-只有当命令的参数相对于系统状态无效时才会引发异常(只有当命令发布将触发其他处理程序的事件时,您才会测试成功。)
希望这能澄清问题。欢迎来到StackOverflow!这是一个好问题,但我认为需要对它进行一些改进,然后才能成为一个好问题
class SubmitHandler
{
/**
* Send proposal
*
* @param SubmitCommand $command
*/
public function execute($command)
{
$this->isReadyToBeSend($command);
$participant = $this->participantRepository->find($command->id);
$participant->submitProposal();
}
private function isReadyToBeSend($command)
{
$result = $this->validate($command);
if (!$result->isValid()) {
throw new ProposalException($result->getMessages()[0]->getMessage());
}
}
public function validate($command)
{
// Here we check if starting price is provided
// and it is less than starting maximum price
// as well as the Call for bids Stage is still active
// so that we are allowed to submit proposals
return Validator::validateForSending($command);
}
public function canBeExecuted($command)
{
return $this->validate($command)->isValid();
}
}
// In the UI we send command to the handler
$commandHandler->handle($submitCommand);
class Participant extends AggregateRoot
{
public function submitProposal()
{
// here we must enforce the invariants
// but the code seems to be almost the same as
// in the validator in the Command Handler
$this->isReadyToBeSent();
}
// throws exceptions if invariants are broken
private function isReadyToBeSent()
{
$this->isPriceCorrect();
$this->AreTermsCorrect();
}
}