头图

从贫血模型到充血模型

XuDing

本文转载自【何以解耦】:https://codedecoupled.com/ane...

领域模型

领域模型是一种用于解决复杂业务逻辑的建模工具/思想。大神 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 所说,领域模型并非包治百病的灵丹妙药,开发者在解决问题时还需对症下药:

image.png

如果你处理的问题使用增删改查即可完成,那么使用譬如 Active Record 这种数据驱动模式才是更适宜的方案。使用领域模型来解决简单的 CURD 问题,那就成了牛刀割鸡,得不偿失。

本文转载自【何以解耦】:https://codedecoupled.com/ane...,如果你也对 TDD,DDD以及简洁代码感兴趣,欢迎关注公众号【何以解耦】,一起探索软件开发之道。

阅读 373

全职编程,兼职跑步,临时健身。

7 声望
0 粉丝
0 条评论
你知道吗?

全职编程,兼职跑步,临时健身。

7 声望
0 粉丝
文章目录
宣传栏