The Clean Architecture in PHP 读书笔记(八)

Clean Architecture

上篇简要介绍了Clean Architecture和union architecture,并给出clean architecture的一些共同点:框架无关,可测性,UI无关,数据库无关,无外部依赖,本篇会具体介绍里面的一些点。

本文为系列文章的第八篇,完成的目录请查看Clean Architecture

框架无关(Framework Independence)

首先我们必须说:框架是好的!大大的提高了我们的开发速度,像市面上流行的框架如:laravel,symfony,zend framework提供了一些通用问题的解决方案,如认证,数据库交互,MVC,路由等,最重要的是这些方案一般都是一些久经考验的方案。正是由于这些方案,我们能更关注我们的业务逻辑,不必陷入一些重复的、细节的问题中。

使用框架的另一个好处是:快速的进步,因此快去使用、学习框架吧。框架定义好了设计模式,你如果不按照框架定义好的模式去做,你就run不起来,于是你就必须去用正确的,好的模式,这样你就可以不断进步。

但是,我们不得不承认,使用框架都是有成本的,在正式开始项目之前,你必须要去学习它,但是一旦学习过后,你就不用再去做那些恼人的重复工作了,辛苦一次,快乐一生_

框架的一些不足

讲了这么多框架的好处,但是必须不幸的告诉你,所有的框架都有一个共同的问题:耦合。你越是使用这个框架,你越是离不开他,你跟他的耦合也越深,一旦这个框架某一天“消失”了,你就game over了!此处的消失,可能是框架升级了,不兼容了,或者是作者不维护了,等等。

框架无关指的是什么

框架无关到底指的是什么?

我们能够快速的切换框架,可能今天laravel挺火,我们用这个,明天突然symfony挺好,换换换的!

当我们在写中大型应用的时候,我们可能会有些处理表单的代码,有些和数据库交互的代码,有些辅助函数,但是这些是我们的业务逻辑吗?NO!

那什么才是我们的业务逻辑呢,或者说是我们的应用。答案是:domain modeldomain services

领域模型和领域服务包括了:services,repositories,factories和entities,这些才是我们真正的应用。至于其他的,都是在领域模型和领域服务基础上构建的UI。

为了达到框架无关,下面是一些建议。

对于框架的使用进行抽象

我们没多写一行使用框架的代码,我们都在增加一分对于框架的依赖。那怎么做才能减少对于框架的依赖呢?

  • 尽可能使用接口

    尽可能依赖于接口,然后通过依赖注入实现依赖反转

  • 使用适配器模式

    通过适配器模式来使用第三方库,实现定义好的接口

  • 坚持SOLID原则和clean code

    坚持SOLID和clean code原则,使得我们代码能组织的很好,并且减少依赖

说完这么多,可能大家还是不是很懂,还是让我们上代码的。

talk is cheap, show me the code

路由和控制器

路由是控制器是我们应用程序的入口,我们真的很难想象不依赖框架提供的路由和框架,怎么写我们的代码,下面是我们开发中最常见的一段代码:

class CustomersController extends BaseController { }

写下这行代码的同时,意味着我们接下去控制器中的每一行都依赖于BaseController,怎么办?

使用适配器模式来适配控制器

namespace MyApp\Controller;

class Customers { 
  public function index() { 
    return [
      'users' => $this->customerRepository->getAll() ]; 
  } 
}

然后适配器如下:

class CustomersController extends AbstractActionController {
    protected $controller;
    public function __construct( Customers $controller )
    {
        $this->controller = $controller;
    }
    public function indexAction()
    {
        return $this->controller->index();
    }
}

适配器做的事情就是包裹着我们自己的控制器Customers,然后进行调用。到这里,我们不禁会问自己,这么做是否值得?

我们做的这一切工作都是为了让我们的代码不耦合于框架

另一个解决方案是:尽可能保持控制器简单。

就像SRP(单一职责原则)倡导的,我们要使得我们的控制器尽可能的功能单一。如果我们将控制器比喻为一个产生response的工厂,那控制器的职责只负责将输入转换为输出,至于具体的业务逻辑,都应该封装在领域模型和领域服务中。

我们坚持的一个原则是:胖model,瘦controller。基于这个原则,我们的控制器应该是下面这样的:

class CustomersController extends AbstractActionController {
    public function indexAction()
    {
        return [
            'users' => $this->customerRepository->getAll() ];
    }
}

上面的控制器很好的说明了我们原则:控制器尽可能简单,将所有逻辑放入领域层。

视图层

视图层中都是一些展示逻辑,但是我们需要注意的是:每个框架都提供了一些辅助函数来生成一些html代码,如果换框架,这会是很头痛的一部分。

因此我们在写下每一行代码的同时,需要时刻提醒自己:尽量减少对于框架的依赖。

表单

表单是我们项目中最难处理一部分,同样的,我们也很难做到和框架解耦。

在使用表单的过程中,我们应该牢记:表达只包含验证和过滤规则,和业务逻辑相关的都应该放入领域层中。

框架服务

大多数框架都提供一些封装好的服务,如laravel中的发送email,我们只需简单的调用:

Mail::send( 'emails.hello', $data, function ( $message ) {
    $message->to( 'you@yoursite.com', 'You' )->subject( 'Hello, You!' );
} );

但是一旦我们换框架,我们就只能痛苦的重构了,一个解决方案是使用适配器:

interface MailerInterface {
    public function send( $template, array $data, callable $callback );
}

class LaravelMailerAdapter implements MailerInterface {
    protected $mailer;
    public function __construct( Mailer $mailer )
    {
        $this->mailer = $mailer;
    }
    public function send( $template, array $data, callable $callback )
    {
        $this->mailer->send( $template, $data, $callback );
    }
}
class MailController extends BaseController {
    protected $mailer;
    public function __construct( MailerInterface $mailer )
    {
        $this->mailer = $mailer;
    }
    public function sendMail()
    {
        $this->mailer->send( 'emails.hello', $data, function ( $message ) {
            $message->to( 'you@yoursite.com', 'You' )->subject( 'Hello, You!' );
        } );
    }
}

App::bind('MailerInterface', function($app) {
  return new LaravelMailerAdapter($foo['mailer']); 
});

上面的一段代码给我们很好的示范了怎么使用适配器模式来减少对于框架的依赖。

总结

以上介绍的一些方法具体在实际使用时候,还需要细细斟酌,特别是要视你项目规模来酌情使用。

如果你项目非常小,那就放开手脚,想怎么弄就怎么弄,但是如果你是做ERP这种应用,那就请好好设计的,前期良好的设计会让你后期的维护成本大大降低。

数据库无关(Independent of Database)

我们大多数的应用后端存储都是使用数据库,自然而然应用也是维护数据库的表结构设计的,我们的应用所有逻辑都是围绕着数据库展开,前期这没什么问题,但是随着应用继续开发,带来的问题有:

  1. 代码中到处都是和数据库的交互,我们看业务逻辑的时候,完全没办法关注于业务,只能看到数据库交互,更糟糕的是:一旦我们需要换数据库抽象层,那将是一场噩梦
  2. 由于我们使用数据库,我们基本上不可能测试我们代码,每次测试一个功能,我们都必须要保证数据库可用,然后数据库中的数据符合我们的预期,这种痛苦只有做过的才知道

那如果数据库不是中心,那什么是我们应用的中心呢?

前面我们讲过clean architecture,最核心的就是领域模层,我们应用的中心也应该是领域层,领域层有可以分为领域模型和领域服务。

领域模型

领域模型在php中就是最简单的php对象,可能是下面这个样子的:

class Customer {

    protected $id;
    protected $name;
    protected $creditLimit;
    protected $status;

    public function getId()
    {
        return $this->id;
    }

    public function setId( $id )
    {
        $this->id = $id;
        return $this;
    }

    public function getName()
    {
        return $this->name;
    }

    public function setName( $name )
    {
        $this->name = $name;
        return $this;
    }

    // ...
}

由于是纯的php类,所以不会有什么依赖了,因此是完全解耦的,是能够方便测试的。

但是如果只有领域模型,意义不大,要配合上领域服务,才能真正的发挥作用。

领域服务

领域服务内部可以细分为3层:

  • Repositories

    服务领域对象的存取,如果后端是数据库,就是负责将数据从数据库中取出,将对象存入数据库。

  • Factories

    负责对象的创建。

  • Services

    具体的业务逻辑,通过调用多个对象和其他服务来完成一个业务目标。

具体可以参考之前的文章:The Clean Architecture in PHP 读书笔记(六)之你不知道的MVC

讲到这,介绍clean architecture的内容就都结束了,下一篇将会以一个实际的例子来加深对clean architecture的理解,尽情期待。

这是The Clean Architecture in PHP的第八篇,你的鼓励是我继续写下去的动力,期待我们共同进步。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容