【问题标题】:Business logic and rules - how to decouple them from the domain model业务逻辑和规则——如何将它们与领域模型解耦
【发布时间】:2018-11-10 03:37:58
【问题描述】:

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

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

那么,我们来做一个简单的例子

假设我们有一家有员工的公司。每个员工都必须有一个安全号码(业务逻辑)。安全号码的长度必须至少为 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 模型中完全删除验证器,转而使用工厂或外观?

【问题讨论】:

    标签: oop design-patterns business-rules decoupling business-logic-layer


    【解决方案1】:

    由于“每个员工都必须有一个安全号码”是您的业务逻辑,因此与业务无关的 Employee 定义将不包括 securityNumber 属性,因为该业务之外的员工可能没有安全号码。相反,您将编写一个扩展雇员的业务特定类 BusinessNameEmployee,并将安全号作为该类的属性。您可以选择考虑使用 IEmployee 接口而不是 Employee 类。然后可以将您的 BusinessRules 类(将包含长度验证器)传递到 BusinessNameEmployee 的构造函数中。

    【讨论】:

    • 好的 - 所以,你所描述的对我来说听起来像是贫血的领域模型,并将所有业务逻辑放入一个单独的层,比如服务层,对吧?
    • 是的,差不多就是这样——但是,您可以通过将规则对象传递给逻辑类来分离业务“逻辑”和“规则”。但这绝对是最能解决您的疑虑和问题的版本。
    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-30
      • 1970-01-01
      • 2014-02-24
      • 1970-01-01
      • 2011-04-21
      • 1970-01-01
      • 2021-02-02
      • 1970-01-01
      相关资源
      最近更新 更多