从贫血模型到充血模型
本文转载自【何以解耦】:codedecoupled.com/anemic-domain-mo...
领域模型
领域模型是一种用于解决复杂业务逻辑的建模工具/思想。大神 Martin Fowler 定义其为包含行为和数据的对象模型。
使用领域模型时,我们的目标是建立丰富的充血领域模型,但由于某种原因,一种不被提倡的模式(贫血模型)被广泛使用。
贫血领域模型
贫血模型是一种众所周知的反模式(anti-pattern),但是要摈弃这种思想却不是简单的事情。因为这种设计开发迅速,易于理解。不经意间,它就可能出现在我们的代码中。
举个例子,我们的应用中有一个激活用户功能:
class User extends OrmModel
{
public setStatus($status)
{
$this->status = $status;
}
public getStatus()
{
return $this->status;
}
}
class UserService
{
public activate(User $user)
{
if ($user->getStatus() === 'blacklisted') {
throw new \Exception('User is blacklisted');
}
$user->setStatus('active');
$user->save();
}
}
乍一看,这种模式有模有样:数据库表通过映射建造一些 ORM 类,各类之间存在一定的关系,而且还使用了服务层(Service layer)进行分层管理。
但是让我们仔细想想。User 类无非一些 getter 和 setter,符合了领域模型包含数据的标准,却缺失了最重要的部分:行为。而将业务逻辑放入服务类(Service class)中,形成了一种类似于事务处理脚本的面向过程的编程方式,与领域模型所倡导的面向对象编程背道而驰。一个健康的服务层应该是很薄的,其主要职责是调用充血的领域模型完成业务流程。
这种贫血模型建立在领域模型的成本上,却没有带来领域模型的好处。
充血领域模型
充血领域模型是领域模型所提倡的建模方式。顾名思义,充血领域模型是包含行为和数据的对象模型。
举个例子,上文中的激活用户功能:
class User extends OrmModel
{
public function activate()
{
if ($user->status === 'blacklisted') {
throw new \Exception('User is blacklisted');
}
$this->status = 'active';
}
}
class UserService
{
public function activate(User $user)
{
$user->activate();
$user->save();
}
}
一个完善的充血领域模型能降低 Bug 风险,帮助我们更直观的建立业务模型。
建立充血领域模型需要开发者具备良好的面向对象思想。能够准确划分服务层逻辑和领域层(Domain Layer)逻辑。
经验法则是,在建立服务层时候,业务逻辑应被封至领域层,事务逻辑封装应被封至服务层,因此服务层应该是很薄的。此处的薄不能完全代表代码量,而是指业务逻辑含量。
对症下药
领域模型的目的是简化一个复杂的问题,而不是将一个简单的问题复杂化。正如 Martin Fowler 所说,领域模型并非包治百病的灵丹妙药,开发者在解决问题时还需对症下药:
如果你处理的问题使用增删改查即可完成,那么使用譬如 Active Record 这种数据驱动模式才是更适宜的方案。使用领域模型来解决简单的 CURD 问题,那就成了牛刀割鸡,得不偿失。
本文转载自【何以解耦】codedecoupled.com/anemic-domain-mo... ,如果你也对 TDD,DDD 以及简洁代码感兴趣,欢迎关注公众号【何以解耦】,一起探索软件开发之道。
本作品采用《CC 协议》,转载必须注明作者和本文链接
我看看有没有兄弟和我一样 兴冲冲的进来的
裤子都脱了你就给个这
:scream: 你们都充血了吗!
@jiangslee 脑子充了
老板不管你充血贫血,他只要你出血