基于 Module 的 Laravel API 架構(gòu)

file

轉(zhuǎn)自 PHP / Laravel 開(kāi)發(fā)者社區(qū) https://laravel-china.org/topics/21913

我非常喜歡編寫(xiě)基于模塊化設(shè)計(jì)的軟件和編程方式唯灵,但我不太喜歡依賴第三方軟件包和類(lèi)庫(kù)來(lái)處理一些瑣碎的事情橘荠,因?yàn)樗鼈儾粫?huì)讓你的編程水平得到很好的提升院水。所以這兩年來(lái)狸相,我一直在用Laravel編寫(xiě)基于模塊的軟件特愿,現(xiàn)在我對(duì)這個(gè)結(jié)果非常滿意勋拟。

推動(dòng)我走向基于模塊化設(shè)計(jì)的軟件和編程方式的決定性因素是我想持續(xù)提升我的編程水平。想象一下蜘矢,你構(gòu)建了一個(gè)項(xiàng)目結(jié)構(gòu),6個(gè)月后你發(fā)現(xiàn)這個(gè)項(xiàng)目存在很多bug综看。在不影響6個(gè)月現(xiàn)有代碼的情況下品腹,通常不會(huì)輕易改變項(xiàng)目架構(gòu)。在分析這個(gè)項(xiàng)目時(shí)红碑,我注意到了兩個(gè)要點(diǎn):你要么在整個(gè)項(xiàng)目中都有一個(gè)標(biāo)準(zhǔn)舞吭,要么堅(jiān)持下去,要么模塊化并逐個(gè)模塊地改進(jìn)析珊。

有些人傾向于不惜一切代價(jià)羡鸥、固守標(biāo)準(zhǔn)地開(kāi)發(fā),即使這可能意味著要堅(jiān)持一個(gè)你不再喜歡的標(biāo)準(zhǔn)忠寻。就我個(gè)人來(lái)言惧浴,我更喜歡持續(xù)地改進(jìn),若是第 20 個(gè)模塊和第 1 個(gè)模塊寫(xiě)得完全不一樣也沒(méi)關(guān)系奕剃。如果某天我需要回到模塊 1 修復(fù) BUG 或重構(gòu)衷旅,我可以將其改進(jìn)為第 20 個(gè)模塊使用的最新標(biāo)準(zhǔn)。

假設(shè)纵朋,你也像我一樣喜歡基于模塊化開(kāi)發(fā) Laravel 應(yīng)用柿顶、盡可能避免在項(xiàng)目中添加不必要的第三方依賴——本文是我的一點(diǎn)經(jīng)驗(yàn)。

1- 路由服務(wù)提供者

Laravel 路由系統(tǒng)可以說(shuō)是整個(gè)應(yīng)用的入口操软。首先需要修改的是默認(rèn)的 RouteServiceProvider.php 文件嘁锯,它應(yīng)當(dāng)將現(xiàn)有路由模塊化。

<?php
namespace App\Providers;
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class RouteServiceProvider extends ServiceProvider
{
    /**
     * 定義應(yīng)用路由寺鸥。
     *
     * @return void
     */
    public function map()
    {
        $this->mapModulesRoutes();
    }
    protected function mapModulesRoutes()
    {
        // 如果你在編寫(xiě)傳統(tǒng) Web 應(yīng)用而非 HTTP API,請(qǐng)使用 `web` 中間件品山。 
        Route::middleware('api')
             ->group(base_path('routes/modules.php'));
    }
}

如上胆建,我們可以直接擺脫該文件的整個(gè)樣板,只需設(shè)置一個(gè)模塊化的路由文件即可肘交。

2- 模塊文件

Laravel 在 routes 文件夾中自帶了一些文件笆载。由于我們已經(jīng)不在 RouteServiceProvider 中映射這些路由,所以可以直接刪除它們涯呻。接下來(lái)凉驻,我們創(chuàng)建一個(gè) modules.php 路由文件。

<?php
use Illuminate\Support\Facades\Route;
Route::group([], base_path('app/Modules/Books/routes.php'));
Route::group([], base_path('app/Modules/Authors/routes.php'));

3- Books 模塊

app 文件夾中复罐,創(chuàng)建 Modules/Books/routes.php 文件涝登。在此文件中,我們可以定義該應(yīng)用 Books 模塊的路由規(guī)則效诅。

<?php
use App\Modules\Books\ListBooks;
use Illuminate\Support\Facades\Route;
Route::get('/books', ListBooks::class);

你可以使用基于控制器——也就是 Laravel 中默認(rèn)標(biāo)準(zhǔn)的路由方式胀滚,但我個(gè)人更喜歡 Good bye controllers, hello Request Handlers(放棄控制器趟济,采用請(qǐng)求處理器) 的方式。 如下是 ListBooks 的實(shí)現(xiàn)咽笼。

<?php
namespace App\Modules\Books;
use App\Eloquent\Book;
use App\Modules\Books\Resources\BookResource;
class ListBooks
{
    public function __invoke(Book $book)
    {
        return BookResource::collection($book->paginate());
    }
}

以上代碼中 BookResource 是 Laravel 的資源轉(zhuǎn)換層顷编。按照官方對(duì)于命名空間的建議,我們可以在 app/Modules/Books/Resources 文件夾中創(chuàng)建它剑刑。

<?php
namespace App\Modules\Books\Resources;
use Illuminate\Http\Resources\Json\Resource;
class BookResource extends Resource
{
    public function toArray($request)
    {
        return [
            'id' => $this->resource->id,
            'title' => $this->resource->title,
        ];
    }
}

4- Authors 模塊

我們還可以通過(guò) Routes 文件來(lái)啟動(dòng) Authors 模塊媳纬。

<?php
use App\Modules\Authors\ListAuthors;
use Illuminate\Support\Facades\Route;
Route::get('/authors', ListAuthors::class);

注意: app/Modules/Authors 這個(gè)命名空間正表示我們所編寫(xiě)的文件,對(duì)于請(qǐng)求處理程序來(lái)說(shuō)也是非常簡(jiǎn)單的施掏。

<?php
namespace App\Modules\Authors;
use App\Eloquent\Author;
use App\Modules\Authors\Resources\AuthorResource;
class ListAuthors
{
    public function __invoke(Author $author)
    {
        return AuthorResource::collection($author->paginate());
    }
}

最后钮惠,我們將編寫(xiě)的 Resource 類(lèi)轉(zhuǎn)變?yōu)轫憫?yīng)式的 JSON 格式。

<?php
namespace App\Modules\Authors\Resources;
use App\Modules\Books\Resources\BookResource;
use Illuminate\Http\Resources\Json\Resource;
class AuthorResource extends Resource
{
    public function toArray($request)
    {
        return [
            'id' => $this->resource->id,
            'name' => $this->resource->name,
            'books' => $this->whenLoaded('books', function () {
                return BookResource::collection($this->resource->books);
            })
        ];
    }
}

注意資源是如何進(jìn)入另一個(gè)模塊以重用 BookResource 其监。 這通常不是一個(gè)比較好的選擇萌腿,因?yàn)槟K應(yīng)該是完全自給自足的,并且只能重用標(biāo)準(zhǔn)類(lèi)抖苦,例如 Eloquent Models 或設(shè)計(jì)用于在任何模塊上通用的通用的組件毁菱。 這個(gè)問(wèn)題的解決方案通常是將 BookResource 復(fù)制到 Authors 模塊中,從而可以在不使用另一個(gè)模塊的情況下進(jìn)行更改锌历,反之亦然贮庞。 我決定保留這個(gè)跨模塊的用法,這個(gè)例子表現(xiàn)出一個(gè)很好的經(jīng)驗(yàn)方法究西,就是讓模塊之間彼此隔離窗慎,但是如果你認(rèn)為上面的例子很簡(jiǎn)單并且不太可能帶來(lái)任何問(wèn)題。 始終確保編寫(xiě)測(cè)試以涵蓋您編寫(xiě)的功能卤材,以避免其他人在不知不覺(jué)中修改您的應(yīng)用程序遮斥。

5- 結(jié)語(yǔ)

雖然這是一個(gè)非常簡(jiǎn)單的例子,但我希望它能夠讓人們根據(jù)自己的需要來(lái)輕松操作使用 Laravel 框架的結(jié)構(gòu)標(biāo)準(zhǔn)扇丛。您可以非常輕松地更改文件的位置术吗,以便構(gòu)建基于模塊化的應(yīng)用程序。我的大多數(shù)項(xiàng)目都附帶了 App / Components 模塊帆精,可以適用于任何模塊可重用的泛類(lèi)型的基礎(chǔ)類(lèi)较屿; App / EloquentModules 文件夾可以用于保存 Eloquent 模型和數(shù)據(jù)庫(kù)關(guān)系模型卓练,我們可以在其中構(gòu)建任何基于模塊化的功能隘蝎。 這是我最近開(kāi)始研究的應(yīng)用程序的文件夾目錄結(jié)構(gòu):

file

我希望每個(gè)人都能從中得到這個(gè)概念,每個(gè)模塊都有自己的需求襟企,并且可以擁有自己的文件夾/實(shí)體/類(lèi)/方法/屬性嘱么。沒(méi)有必要將所有模塊標(biāo)準(zhǔn)化完全相同,因?yàn)槟承┠K比其他模塊簡(jiǎn)單得多顽悼,并且不需要大量的結(jié)構(gòu)設(shè)計(jì)拱撵。此示例顯示AccountChurn模塊通過(guò) HTTP 文件夾提供 API辉川,同時(shí)仍通過(guò)控制臺(tái)提供 Artisan 命令。另一方面拴测,AccountOverview則僅提供 HTTP API乓旗,并且依賴倉(cāng)庫(kù)、值對(duì)象(bags)以及服務(wù)類(lèi)(paginators)來(lái)提供更大的數(shù)據(jù)價(jià)值集索。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末屿愚,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子务荆,更是在濱河造成了極大的恐慌妆距,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件函匕,死亡現(xiàn)場(chǎng)離奇詭異娱据,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)盅惜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)中剩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人抒寂,你說(shuō)我怎么就攤上這事结啼。” “怎么了屈芜?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵郊愧,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我井佑,道長(zhǎng)属铁,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任躬翁,我火速辦了婚禮焦蘑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘姆另。我一直安慰自己喇肋,他們只是感情好坟乾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開(kāi)白布迹辐。 她就那樣靜靜地躺著,像睡著了一般甚侣。 火紅的嫁衣襯著肌膚如雪明吩。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,215評(píng)論 1 299
  • 那天殷费,我揣著相機(jī)與錄音印荔,去河邊找鬼低葫。 笑死,一個(gè)胖子當(dāng)著我的面吹牛仍律,可吹牛的內(nèi)容都是我干的嘿悬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼水泉,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼善涨!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起草则,我...
    開(kāi)封第一講書(shū)人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤钢拧,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后炕横,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體源内,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年份殿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了膜钓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡伯铣,死狀恐怖呻此,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腔寡,我是刑警寧澤焚鲜,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站放前,受9級(jí)特大地震影響忿磅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜凭语,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一葱她、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧似扔,春花似錦吨些、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至黔寇,卻和暖如春偶器,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工屏轰, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留颊郎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓霎苗,卻偏偏與公主長(zhǎng)得像姆吭,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子唁盏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

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

  • “公元12018年猾编,偉大的紀(jì)元科學(xué)家埃德加·星于3月14日逝世了∩茫” 主持的人拉開(kāi)一張帷幕答倡,一個(gè)黑色的不明物質(zhì)出現(xiàn)...
    晚灼閱讀 646評(píng)論 3 7
  • 2017年9月4號(hào)港庄,我在這里倔既,從北極到南極的第14天。自從2017年8月25日登上了探險(xiǎn)船以后鹏氧,映入眼簾最多就是洶...
    翹楚東方huilife閱讀 230評(píng)論 0 0
  • 生活是一本書(shū)把还,一本從出生開(kāi)始实蓬,主線貫穿一生,作者是自己的吊履,只有自己才能夠?qū)懙臅?shū) 生活不是由進(jìn)入社會(huì)才開(kāi)始安皱,從我們呱...
    大樹(shù)的快樂(lè)生活閱讀 1,204評(píng)論 0 0
  • 1 表哥的名字叫成剛。小姨說(shuō)希望他長(zhǎng)大后艇炎,能夠有所成就酌伊,做個(gè)剛正善良的人。 我跟表哥是同一天出生缀踪,他比我早來(lái)到這個(gè)...
    江隅閱讀 583評(píng)論 1 6