基于yaf的模塊化開發方案

471415609 8年前發布 | 10K 次閱讀 PHP開發 yaf

來自: http://www.imsiren.com/archives/1236

隨著我們網站流量越來越大,用戶越來越多,需求也越來越復雜,一站式開發已經遠遠不能支撐我們的業務發展需要,在產品發展期內碰到的問題越來越多,技術架構限制了業務的發展,這對于研發人員來講是不能忍的,業務制約技術發展可以接受,但技術制約了業務的發展就是問題了。

我先來總結下一站式開發將面臨的問題:

1、代碼量大,開發、部署、測試都將是問題

這個很容易理解,我們的業務需求全部在一個系統中的時候,開發、部署、測試必然都會存在很大的風險,比如開發,即使從一開始就參與項目的成員,在業務規模龐大的時候,也很難做把控控制風險;比如部署,因為量大,導致代碼發布會非常慢,大部分發布平臺會先打包,到線上服務器解壓,這個過程當在緊急上線發布的時候會急死人的,一旦上線出現問題,回滾成本將會更高,影響的不單單是發布系統,嚴重的將導致服務不可用的狀態。

2、嚴重制約業務發展

有一個好的習慣,開發人員會在開發過程中習慣性的將公用的功能封裝成類或方法,如果這是核心功能,那它可能出現在整個系統的任何地方,當核心功能需要開發的時候,則會牽一發動全身,很可能面臨的將是系統重構。

舉個很簡單的例子。

如我們有一個函數(或者一個類)

</div>

function custom_function($a,$b) {
//TODO
}

實現了最基礎的業務功能,一開始它滿足了當前的業務,實現了基本功能,但隨著業務發展,這個基礎功能變得更強大,支持的特性更多了,為了支持業務,我們把函數擴展根據參數做兼容,如:

function custom_function($a,$b,$c){
if(empty($c)){
//兼容老業務
}else if($c== "XXX"){
//支持新業務
}else if($c == "ccc"){
//新業務
}
}

雖然能滿足需求,但可以想像維護這樣一段代碼會讓后來人罵娘多少次?當然,雖然我們聽不見,但畢竟讓后人非常不爽。

3、不得已的推倒重構

基于各種壓力,更多的是基于業務發展的壓力,我們不得已痛苦的對整個系統進行重構,這個過程漫長而痛苦,一方面要支持現有系統的業務,另一方面還要對整個系統進行技術重構,讓他們之間的耦合更少,這個成本和風險將會讓我們痛不欲生。

那我們在項目最初是不是就該考慮從業務上拆分成多個模塊?答案是肯定的。但是也將帶來一些問題:

1、PM不懂技術,不清楚怎么拆分

這個問題還好解決,在規劃和項目評審的時候,跟技術一起討論,側重于在業務功能上進行拆分,比如用戶相關操作全部歸為用戶模塊,后臺相關歸為后臺模塊,瀏覽的歸為view模塊

2、怎么控制粒度,控制不好反而會增加更多的成本

這個要在技術內部好好的做溝通和規劃,控制好邊界

3、數據共享?

雖然拆成了多個模塊,但是對于數據倆講,它遍布整個系統,模塊與模塊之間的數據如何共享?

在項目初期的解決方案是,

- 將最基礎的數據獨立成library模塊,它提供最細單元的數據共享,這些可以在整個模塊中調用;

- 提供跨模塊調用的業務接口,比如在模塊A下,讓框架支持調用B模塊下的接口;

- 提供http服務化接口,這個大多數是在項目成熟階段的時候才會做的事情;

</div>

解決困難問題的方向就在框架身上了,它將具備以下特性:

1、代碼量少、精簡

2、可跨模塊調用,模塊中幾乎不用包含任何框架的實現

3、可調用最基礎的library模塊

4、管理配置化

在目前所有php框架中,要想實現模塊化編程還是比較困難的,大部分的框架實現都是在框架內部有自己的application或module,這里面是具體的模塊化的業務代碼。

還有一種完全的模塊化,每一個模塊業務都有這樣一套框架實現:

但是這樣的缺點顯而易見,一方面很難做到跨模塊調用,需要在外層在包一層邏輯實現,另一方面框架維護和代碼量讓模塊顯得更重。

所以更多的是采用第一種方式,今天的主題yaf,其實也是這樣一種架構,只是框架邏輯實現在so擴展中了,

但今天所要談的是基于這兩種方案之中的辦法,既能讓模塊之間完全獨立,又能讓它跨模塊調用接口,而且模塊中基本不用維護任何框架代碼,只需要按照框架的規范寫代碼就可以了。

每個模塊之間提供interface,供其他模塊直接調用,每個模塊只處理自己的業務,當有業務需要共享時直接按照規范寫interface就可以了。

項目目錄如下:

但是yaf不支持跨模塊的調用,模塊與模塊之間是不能通過interface來通信的,為了解決這個問題,我增加了兩個類Yaf_Init和Yaf_Caller

Yaf_Init

$app = Yaf_Init::init();
$app->Bootstrap()->run();

這個只是一層封裝,根據目錄解析出當前模塊名,省去配置config.ini的煩惱,為了靈活性,Yaf_init::init()后面將增加參數可以靈活自定義配置,每一個webroot/module/index.php中都是上面的代碼。

等同于下面

$config = array(
'application'=> array(
'directory' => app目錄
)
)
$application = new Yaf_Application($config);
$application->Bootstrap()->run();

為了能夠找到app的目錄,還需要在php.ini中增加一個配置,

yaf.app_path = /home/admin/web/htdocs/app/

如果訪問/home/admin/web/webserver/webroot/module/index.php時,

會解析出module拼成app目錄:

/home/admin/web/htdocs/app/module/傳給$config.application.directory,

將$config 配置傳遞給yaf_application的構造函數進行初始化

</div>

Yaf_Caller

$Object = new Yaf_Caller('moduleName','className');
$Object->test();

這個類用來實例化其他模塊中interface的類庫,其實它也是一層簡單的包裝,保存環境變量,切換到某模塊,然后調用Yaf_loader::autoload(),該構造函數接收兩個參數,第一個是模塊名,第二個是interface的類名,

interface的命名規范:

/app/htdocs/moduleA/Interface/Admin.php

比如模塊A提供了一個interface,類名是admin

則Admin.php的類寫法

</div>

class Interface_Admin{
   function test(){
        //TODO
   }
}

其他模塊調用時

//object返回的是Interface_Admin類
$object = new Yaf_Caller('moduleA','Admin');
它直接可以調用Interface_Admin下的public方法。
$object->test();

這套方案有以下優點:

- 模塊代碼量小,只需要實現業務代碼就可以

- 模塊之間可以無縫調用inteface接口

</div>

未完待續……

原文出處:

</div>

 本文由用戶 471415609 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!