业务逻辑和规则——如何将它们与领域模型解耦
Posted
技术标签:
【中文标题】业务逻辑和规则——如何将它们与领域模型解耦【英文标题】: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;
我发现这种方法存在一些问题......
-
设置安全号码需要您在
安全号码本身。我认为这对二传手来说有点讨厌。
Employee 对象可能留在无效
状态,因为可以在不设置的情况下实例化它们
安全号码。
要解决第二个问题,我可以为Employee
类创建一个构造函数,如下所示
public function __constructor(string $name,
string $securityNumber,
SecurityNumberValidatorInterface $validator)
$this->name = $name;
$this->setSecurityNumber($securityNumber, $validator);
由于在构造函数中调用了 setter,这可能是一种反模式...
有什么更好的方法呢?是否会从 Employee
模型中完全删除验证器,转而使用工厂或外观?
【问题讨论】:
【参考方案1】:由于“每个员工都必须有一个安全号码”是您的业务逻辑,因此与业务无关的 Employee 定义将不包括 securityNumber 属性,因为该业务之外的员工可能没有安全号码。相反,您将编写一个扩展雇员的业务特定类 BusinessNameEmployee,并将安全号作为该类的属性。您可以选择考虑使用 IEmployee 接口而不是 Employee 类。然后可以将您的 BusinessRules 类(将包含长度验证器)传递到 BusinessNameEmployee 的构造函数中。
【讨论】:
好的 - 所以,你所描述的对我来说听起来像是贫血的领域模型,并将所有业务逻辑放入一个单独的层,比如服务层,对吧? 是的,差不多就是这样——但是,您可以通过将规则对象传递给逻辑类来分离业务“逻辑”和“规则”。但这绝对是最能解决您的疑虑和问题的版本。【参考方案2】:有方法调用值对象,它是实体的一部分。在这种情况下,您可以将安全号码包装在一个类(它是一个值对象)调用 SecurityNumber 中,并在那里添加验证。可以参考这个例子:https://kacper.gunia.me/ddd-building-blocks-in-php-value-object/
在 DDD 中,有一个反模式叫 Primitive Obsession,你的思维可能深陷这个陷阱。
【讨论】:
以上是关于业务逻辑和规则——如何将它们与领域模型解耦的主要内容,如果未能解决你的问题,请参考以下文章