我目前正在尝试消化与 DDD 中的不变量和验证相关的信息。如果我得到正确验证不是域的问题,应该在外部完成以防止不变量发生。另一方面,必须在域中强制执行不变量,特别是在聚合中。
让我感到困惑的实际上是两件事:
- 如何区分业务规则(不变量)与验证
- 如何尊重 DRY 原则
让我详细说明一下。考虑我们有一个涵盖Tenders的领域模型。两个主要参与者是投标过程组织者(Organizer)和投标过程参与者(Participant)。主办方发布招标公告,其中包含有关阶段条款和要求的信息(例如起始最高价格)。招标是一个由几个阶段组成的过程。每个阶段都有它的条款。第一阶段是“招标”。在这个阶段,参与者被允许发送他的提议(提案)。
有两个基本要求:
- 投标价格必须低于起始最高价格
- 参与者只能在“招标”阶段提交报价
从技术上讲,我们可以这样实现(细节省略):
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();
}
}
考虑到上面提到的一切,在给定的上下文中,不变量和验证之间的细微差别是什么?代码是否应该在验证器和聚合中重复?(我不想将验证器注入实体)
非常感谢。
更新:
我想我不够清楚。长话短说,我有两件事要考虑:
- 业务规则和不变量之间的区别。
- 坚持 DRY 违反 SRP,反之亦然。
我和另一位开发人员最近进行了讨论,我们得出的结论如下:
- 不变量是必须独立于业务规则而遵守的一些规则。即使在概念上它们是两个不同的东西,代码也可能是相同的。
- 为了遵守 SRP 原则,在这种情况下可能会违反 DRY 原则。
如果我错了,请纠正我。