上篇簡要介紹了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 model和domain 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ā)衡蚂,帶來的問題有:
- 代碼中到處都是和數(shù)據(jù)庫的交互窿克,我們看業(yè)務(wù)邏輯的時候,完全沒辦法關(guān)注于業(yè)務(wù)毛甲,只能看到數(shù)據(jù)庫交互年叮,更糟糕的是:一旦我們需要換數(shù)據(jù)庫抽象層,那將是一場噩夢
- 由于我們使用數(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)步南誊。