首页 > 解决方案 > 业务逻辑和规则——如何将它们与域模型解耦

问题描述

我在弄清楚如何使我的设计松散耦合时遇到了一些麻烦。具体来说,如何将业务逻辑和规则实现到域模型中,以及将代码的不同部分放在哪里——即文件夹结构。

澄清我如何理解这些术语:
业务逻辑:特定领域的问题解决。
业务规则:特定领域的规则。
领域模型:领域特定的、现实世界对象的抽象,例如员工。

所以,让我们做一个简单的例子

假设我们有一家有员工的公司。每个员工都必须有一个安全号码(业务逻辑)。安全号码的长度必须至少为 10 个字符(业务规则)。

我在建模时的镜头看起来像:

# Conceptual model of an employee within the company
class Employee {

    private $name;
    private $securityNumber;

    // Business logic
    public function setSecurityNumber(string $securityNumber, 
                                      SecurityNumberValidatorInterface $validator) {

        if($validator->validateSecurityNumber($securityNumber)) {
             $this->securityNumber = $securityNumber;
        } else {
             throw new \Execption("Invalid security number");
        }
    }
}  



# Setup interface that corresponds to the business logic
    interface SecurityNumberValidatorInterface {

    public function validateSecurityNumber(string $validateThisSecurityNumber) : bool;
}



# Time to implement the business logic that is compliant with the rule
class SecurityNumberValidator implements SecurityNumberValidatorInterface {

    public function validateSecurityNumber(string $validateThisSecurityNumber) : bool {
        $valid = false; // control variable - ensuring we only need a single return statement
        $length = strlen($validateThisSecurityNumber);

        if ($length < 10) {
            $valid = true;
        }

       return $valid;
    }
}


我看到这种方法存在一些问题......

  1. 设置安全号码要求您将对象与安全号码本身一起传递。我认为这对于二传手来说看起来有点讨厌。
  2. Employee 对象可能会处于无效状态,因为可以在不设置安全号的情况下实例化它们。

为了解决第二个问题,我可以为Employee类创建一个构造函数,如下所示

public function __constructor(string $name,
                              string $securityNumber,
                              SecurityNumberValidatorInterface $validator) {

    $this->name = $name;
    $this->setSecurityNumber($securityNumber, $validator);
}


由于在构造函数中调用了 setter,这可能是一种反模式……
有什么更好的方法呢?是否会从Employee模型中完全删除验证器,而转而使用工厂或外观?

标签: oopdesign-patternsbusiness-rulesdecouplingbusiness-logic-layer

解决方案


有方法调用值对象,它是实体的一部分。在这种情况下,您可以将安全号码包装在一个类(它是一个值对象)调用 SecurityNumber 中,并在那里添加验证。可以参考这个例子:https ://kacper.gunia.me/ddd-building-blocks-in-php-value-object/

在 DDD 中,有一个反模式叫 Primitive Obsession,你的思想可能深陷这个陷阱。


推荐阅读