The Clean Architecture in PHP 讀書筆記(八)

Clean Architecture

上篇簡要介紹了Clean Architecture和union architecture包蓝,并給出clean architecture的一些共同點:框架無關(guān)始苇,可測性搁拙,UI無關(guān)云挟,數(shù)據(jù)庫無關(guān)矫限,無外部依賴哺哼,本篇會具體介紹里面的一些點。

本文為系列文章的第八篇叼风,完成的目錄請查看Clean Architecture

框架無關(guān)(Framework Independence)

首先我們必須說:框架是好的取董!大大的提高了我們的開發(fā)速度,像市面上流行的框架如:laravel无宿,symfony茵汰,zend framework提供了一些通用問題的解決方案,如認(rèn)證孽鸡,數(shù)據(jù)庫交互经窖,MVC坡垫,路由等,最重要的是這些方案一般都是一些久經(jīng)考驗的方案画侣。正是由于這些方案冰悠,我們能更關(guān)注我們的業(yè)務(wù)邏輯,不必陷入一些重復(fù)的配乱、細(xì)節(jié)的問題中溉卓。

使用框架的另一個好處是:快速的進(jìn)步,因此快去使用搬泥、學(xué)習(xí)框架吧桑寨。框架定義好了設(shè)計模式忿檩,你如果不按照框架定義好的模式去做尉尾,你就run不起來,于是你就必須去用正確的燥透,好的模式沙咏,這樣你就可以不斷進(jìn)步。

但是班套,我們不得不承認(rèn)肢藐,使用框架都是有成本的,在正式開始項目之前吱韭,你必須要去學(xué)習(xí)它吆豹,但是一旦學(xué)習(xí)過后,你就不用再去做那些惱人的重復(fù)工作了理盆,辛苦一次痘煤,快樂一生_

框架的一些不足

講了這么多框架的好處猿规,但是必須不幸的告訴你衷快,所有的框架都有一個共同的問題:耦合。你越是使用這個框架坎拐,你越是離不開他,你跟他的耦合也越深养匈,一旦這個框架某一天“消失”了哼勇,你就game over了!此處的消失呕乎,可能是框架升級了积担,不兼容了,或者是作者不維護(hù)了猬仁,等等帝璧。

框架無關(guān)指的是什么

框架無關(guān)到底指的是什么先誉?

我們能夠快速的切換框架,可能今天laravel挺火的烁,我們用這個褐耳,明天突然symfony挺好,換換換的渴庆!

當(dāng)我們在寫中大型應(yīng)用的時候铃芦,我們可能會有些處理表單的代碼,有些和數(shù)據(jù)庫交互的代碼襟雷,有些輔助函數(shù)刃滓,但是這些是我們的業(yè)務(wù)邏輯嗎?NO!

那什么才是我們的業(yè)務(wù)邏輯呢耸弄,或者說是我們的應(yīng)用咧虎。答案是:domain modeldomain services

領(lǐng)域模型和領(lǐng)域服務(wù)包括了:services计呈,repositories砰诵,factories和entities,這些才是我們真正的應(yīng)用震叮。至于其他的胧砰,都是在領(lǐng)域模型和領(lǐng)域服務(wù)基礎(chǔ)上構(gòu)建的UI。

為了達(dá)到框架無關(guān)苇瓣,下面是一些建議尉间。

對于框架的使用進(jìn)行抽象

我們沒多寫一行使用框架的代碼,我們都在增加一分對于框架的依賴击罪。那怎么做才能減少對于框架的依賴呢哲嘲?

  • 盡可能使用接口

    盡可能依賴于接口,然后通過依賴注入實現(xiàn)依賴反轉(zhuǎn)

  • 使用適配器模式

    通過適配器模式來使用第三方庫媳禁,實現(xiàn)定義好的接口

  • 堅持SOLID原則和clean code

    堅持SOLID和clean code原則眠副,使得我們代碼能組織的很好,并且減少依賴

說完這么多竣稽,可能大家還是不是很懂囱怕,還是讓我們上代碼的。

talk is cheap, show me the code

路由和控制器

路由是控制器是我們應(yīng)用程序的入口毫别,我們真的很難想象不依賴框架提供的路由和框架娃弓,怎么寫我們的代碼,下面是我們開發(fā)中最常見的一段代碼:

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砾肺,然后進(jìn)行調(diào)用挽霉。到這里防嗡,我們不禁會問自己,這么做是否值得侠坎?

我們做的這一切工作都是為了讓我們的代碼不耦合于框架

另一個解決方案是:盡可能保持控制器簡單蚁趁。

就像SRP(單一職責(zé)原則)倡導(dǎo)的,我們要使得我們的控制器盡可能的功能單一硅蹦。如果我們將控制器比喻為一個產(chǎn)生response的工廠荣德,那控制器的職責(zé)只負(fù)責(zé)將輸入轉(zhuǎn)換為輸出,至于具體的業(yè)務(wù)邏輯童芹,都應(yīng)該封裝在領(lǐng)域模型和領(lǐng)域服務(wù)中涮瞻。

我們堅持的一個原則是:胖model,瘦controller假褪∈鹧剩基于這個原則,我們的控制器應(yīng)該是下面這樣的:

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

上面的控制器很好的說明了我們原則:控制器盡可能簡單生音,將所有邏輯放入領(lǐng)域?qū)印?/p>

視圖層

視圖層中都是一些展示邏輯宁否,但是我們需要注意的是:每個框架都提供了一些輔助函數(shù)來生成一些html代碼,如果換框架缀遍,這會是很頭痛的一部分慕匠。

因此我們在寫下每一行代碼的同時,需要時刻提醒自己:盡量減少對于框架的依賴域醇。

表單

表單是我們項目中最難處理一部分台谊,同樣的,我們也很難做到和框架解耦譬挚。

在使用表單的過程中锅铅,我們應(yīng)該牢記:表達(dá)只包含驗證和過濾規(guī)則,和業(yè)務(wù)邏輯相關(guān)的都應(yīng)該放入領(lǐng)域?qū)又小?/p>

框架服務(wù)

大多數(shù)框架都提供一些封裝好的服務(wù)减宣,如laravel中的發(fā)送email盐须,我們只需簡單的調(diào)用:

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

但是一旦我們換框架,我們就只能痛苦的重構(gòu)了漆腌,一個解決方案是使用適配器:

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']); 
});

上面的一段代碼給我們很好的示范了怎么使用適配器模式來減少對于框架的依賴贼邓。

總結(jié)

以上介紹的一些方法具體在實際使用時候,還需要細(xì)細(xì)斟酌闷尿,特別是要視你項目規(guī)模來酌情使用塑径。

如果你項目非常小,那就放開手腳悠砚,想怎么弄就怎么弄晓勇,但是如果你是做ERP這種應(yīng)用堂飞,那就請好好設(shè)計的灌旧,前期良好的設(shè)計會讓你后期的維護(hù)成本大大降低绑咱。

數(shù)據(jù)庫無關(guān)(Independent of Database)

我們大多數(shù)的應(yīng)用后端存儲都是使用數(shù)據(jù)庫,自然而然應(yīng)用也是維護(hù)數(shù)據(jù)庫的表結(jié)構(gòu)設(shè)計的枢泰,我們的應(yīng)用所有邏輯都是圍繞著數(shù)據(jù)庫展開描融,前期這沒什么問題,但是隨著應(yīng)用繼續(xù)開發(fā)衡蚂,帶來的問題有:

  1. 代碼中到處都是和數(shù)據(jù)庫的交互窿克,我們看業(yè)務(wù)邏輯的時候,完全沒辦法關(guān)注于業(yè)務(wù)毛甲,只能看到數(shù)據(jù)庫交互年叮,更糟糕的是:一旦我們需要換數(shù)據(jù)庫抽象層,那將是一場噩夢
  2. 由于我們使用數(shù)據(jù)庫玻募,我們基本上不可能測試我們代碼只损,每次測試一個功能,我們都必須要保證數(shù)據(jù)庫可用七咧,然后數(shù)據(jù)庫中的數(shù)據(jù)符合我們的預(yù)期跃惫,這種痛苦只有做過的才知道

那如果數(shù)據(jù)庫不是中心,那什么是我們應(yīng)用的中心呢艾栋?

前面我們講過clean architecture爆存,最核心的就是領(lǐng)域模層,我們應(yīng)用的中心也應(yīng)該是領(lǐng)域?qū)踊壤I(lǐng)域?qū)佑锌梢苑譃轭I(lǐng)域模型和領(lǐng)域服務(wù)先较。

領(lǐng)域模型

領(lǐng)域模型在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類遥诉,所以不會有什么依賴了拇泣,因此是完全解耦的,是能夠方便測試的矮锈。

但是如果只有領(lǐng)域模型霉翔,意義不大,要配合上領(lǐng)域服務(wù)苞笨,才能真正的發(fā)揮作用债朵。

領(lǐng)域服務(wù)

領(lǐng)域服務(wù)內(nèi)部可以細(xì)分為3層:

  • Repositories

    服務(wù)領(lǐng)域?qū)ο蟮拇嫒。绻蠖耸菙?shù)據(jù)庫瀑凝,就是負(fù)責(zé)將數(shù)據(jù)從數(shù)據(jù)庫中取出序芦,將對象存入數(shù)據(jù)庫。

  • Factories

    負(fù)責(zé)對象的創(chuàng)建粤咪。

  • Services

    具體的業(yè)務(wù)邏輯谚中,通過調(diào)用多個對象和其他服務(wù)來完成一個業(yè)務(wù)目標(biāo)。

具體可以參考之前的文章:The Clean Architecture in PHP 讀書筆記(六)之你不知道的MVC

講到這宪塔,介紹clean architecture的內(nèi)容就都結(jié)束了磁奖,下一篇將會以一個實際的例子來加深對clean architecture的理解,盡情期待某筐。

這是The Clean Architecture in PHP的第八篇比搭,你的鼓勵是我繼續(xù)寫下去的動力,期待我們共同進(jìn)步南誊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末身诺,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子抄囚,更是在濱河造成了極大的恐慌霉赡,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件幔托,死亡現(xiàn)場離奇詭異同廉,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)柑司,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進(jìn)店門迫肖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人攒驰,你說我怎么就攤上這事蟆湖。” “怎么了玻粪?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵隅津,是天一觀的道長。 經(jīng)常有香客問我劲室,道長伦仍,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任很洋,我火速辦了婚禮充蓝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘喉磁。我一直安慰自己谓苟,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布协怒。 她就那樣靜靜地躺著涝焙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪孕暇。 梳的紋絲不亂的頭發(fā)上仑撞,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天赤兴,我揣著相機(jī)與錄音,去河邊找鬼隧哮。 笑死搀缠,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的近迁。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼簸州,長吁一口氣:“原來是場噩夢啊……” “哼鉴竭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起岸浑,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤搏存,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后矢洲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體璧眠,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年读虏,在試婚紗的時候發(fā)現(xiàn)自己被綠了责静。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡盖桥,死狀恐怖灾螃,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情揩徊,我是刑警寧澤腰鬼,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站塑荒,受9級特大地震影響熄赡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜齿税,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一彼硫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧凌箕,春花似錦乌助、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至仆葡,卻和暖如春赏参,著一層夾襖步出監(jiān)牢的瞬間志笼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工把篓, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留纫溃,地道東北人。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓韧掩,卻偏偏與公主長得像紊浩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子疗锐,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內(nèi)容