Nest.js 入手以及企業化的思考

XGHeaven發表於2018-04-15

本人是一名 Node.js 實習生,在進入大搜車之後,有幸見識到 Akyuu.js 這個框架。但是這個框架是使用 Express + Callback 的方式,我不是很喜歡。在我的推薦以及社群的發展下,組長決定用 TS + Async/Await 來試一試。於是我也去了解了一下 TS 的後端框架有哪些,結果經過別人推薦,找到了 Nest.js 這個想法幾乎和我一模一樣的框架。

框架簡介

因為我這個不是教程向,所以就不細講,可以檢視 Nest.js 官網。從我的感性角度來講,簡單說一下以下幾個特點:

  • 去中心化路由。所有的路由通過裝飾器與 Controller 繫結。簡單、明瞭,學習成本低。
  • TypeScript/Rx.js 加持。智慧補全,程式碼分析,靜態型別等等優點。如果你只是個人用用的話,可能會覺得很全。但是放在企業當中使用,是非常大的優點。
  • 依賴注入。從 Angular 那裡學習而來,但是進行了一些簡化,但是完全夠用。比如說簡化掉了 deps。
  • 模組思想。Node 社群的後端框架,其實都被 Express 導向到了中介軟體的模式。而 Nest.js 卻從 Angular 當中吸取到了模組的思想。不同的 Service、Controller、Component 組成不同的模組。模組之間可以相互依賴,也可以獨立存在,這大大減少了測試和邏輯的複雜度。
  • 易於擴充套件。以往的框架,你能做的就是編寫業務邏輯,而其他的你都很難去做到。於是傳統的後端框架不得不引入了一套外掛機制來增強框架的擴充套件性。但是 Nest.js 將外掛的功能直接內建到了框架當中。傳統的外掛在這裡可以認為就是一個模組,通過載入不同的模組來新增不同的功能。
  • Express 基石。有人會說,不是現在 Koa 才是更好的模型麼?洋蔥模型可以解決更多複雜的問題。沒錯,我不反對這個言論。但是我想說的是,Express 還是最簡單最通用的方式,因為他不賴 Generator/Promise,只需要你又一個 Node.js 執行環境,支援 Callback 就可以了。(話說應該沒有不支援 Callback 的 Node.js 環境吧,哈哈哈)不管怎麼樣,Express 的覆蓋面還是比 Koa 要廣不少。
  • 條條大路通羅馬。那麼有人就問了,那我要實現洋蔥模型怎麼辦呢?我想說,辦法總是會有的。而在 Nest.js 當中,通過 Interceptor ,可以很好的實現洋蔥模型。也就是說你可以通過 Interceptor 來記錄請求的耗時。
  • 同步程式碼。這裡所說的同步程式碼並不是單單指的是 async/await。在很多支援 async/await 的框架中,如果你想返回值,如果是 Express ,你還是需要呼叫 resp.send(await getValue()),而 koa 也是需要呼叫 ctx.body = await getValue()。但是在 Nest.js 中,只需要 return await getValue() 即可。實現真正的同步編寫業務邏輯程式碼。
  • 邏輯分層。其實很多功能,都是可以通過中介軟體來實現的。但是不同型別的功能有不同的需求,如果只是通過中介軟體來實現,勢必會導致有一些重複的程式碼。於是 Nest.js 裡面引入了 Pipe/Interceptor/Guard/ExceptionFilter 等邏輯層。不同的層裡面處理相似的事情,比如說 Pipe 處理的是輸入資料的轉換。而 Interceptor 來實現洋蔥模型。Guard 用於許可權校驗等攔截任務。ExceptionFilter 用來處理錯誤資料。這種分層帶來的好處就是可以讓程式碼更加清晰,主需要思考這個層需要做的事情,而不需要站在中介軟體的層面去考慮這個事情。
  • Validation。自帶校驗,而且和 TS 結合的非常完美,使用起來很舒服,請看教程
  • 輸入引數的轉換。這個其實是一個很方便的方面。有的時候你需要將輸入的引數轉換成一個類,這個時候你就可以通過 Validation 進行轉換。你要是不想用自動轉換,可以通過傳統的手動轉換的方式。
  • 測試功能完美。由於採用了依賴注入,所以測試簡直簡單的不得了。而且官方也提供了一系列測試工具,也能很好的解決單元測試的問題。

Nest.js 企業化當中的問題

  • 目錄無約束。在企業當中,不對目錄進行約束會導致程式碼越來越亂。從而降低了程式碼可維護性。
  • 沒有配置管理功能。在框架開發中,配置往往是一個很重要的功能。比如說配置資料庫的連線,配置監聽的埠。
  • 沒有程式管理。雖然有提供 @nestjs/cli,但是這個提供的僅僅是一個專案的建立的能力。
  • 部分文件講解不詳細,會提高入門的門檻。

不過總的來說,前面幾點也正是 Nest.js 靈活性的保證。但是我們真正在開發當中,還是需要一種合理的約束來保證開發的統一。

Nest.js 企業化的嘗試

那麼我們這裡針對上面的幾個問題,嘗試採用一些方式來進行約束。

目錄結構

我們對專案指定如下的規則:

  • 全部通過 TypeScript 書寫,並且全部位於 src 目錄下
  • 入口檔案是 main.ts 如果沒有特殊情況,不動這個檔案
  • 配置放在 src/config 資料夾下
  • 所有的 Service/Controller/Logic/Component 等都掛載到 MainModule 下。
  • 其中 module 資料夾存放自定義的 Module,或者說希望獨立成模組但是還沒有完全獨立出來的。其中目錄結構和這個專案目錄結構類似
  • boot 資料夾是專案啟動程式碼的時候執行的,這部分在 Nest.js 當中沒有給出。我這裡打算新增這個功能,但是還沒有想好具體的實現形式,所以待定。
  • interface/enum 等資料隨著對應的 service 匯出。不另做說明。比如說 car.service.ts 除了可以匯出 CarService 類以外,還可以匯出 CarType enum。
  • dest 資料夾是編譯之後的檔案,可以直接輸入 node dest/main.js 執行。
  • 命名規則
    • 所有的檔案除了 main.ts 和類檔案以外,都要新增型別字尾,比如說 user.model.ts car.controller.ts google.logic.ts。但是比如說只是一個 Car 類,那麼可以直接命名成 car.ts
    • 不允許通過 export default 匯出資料。一方面是為了方便匯入的時候保證命名的統一,另一方面可以隨時匯出 interface/enum 等內容。
    • 所有的測試檔案字尾名都以 .spec.ts.test.ts 結尾。
|-- dest
    |--- ...
|-- src
    |-- config
    |-- controller
    |-- model
    |-- service
    |-- logic
    |-- component
    |-- boot
    |-- module
        |-- module-name
            |-- config
            |-- index.ts
            |-- module-name.module.ts
    |-- main.ts
    |-- main.module.ts
複製程式碼

配置管理

我目前初步的想法是通過提供一個 ConfigModule 暴露出一個 ConfigService 來提供配置的獲取和檢視。

在某些情況下,可能需要多級配置,模組級別的配置,應用級別的配置。那麼 ConfigService 可以在獲取配置的時候自動合併這些規則。

程式管理

現在已經是 18 年了,不用 Docker 你真的對得起自己麼?很明顯是對不起的。所以程式管理這一塊,我們就交給 Docker 來處理。包括啟動、停止、重啟、日誌等,都交給 Docker。

於是啟動命令就可以簡化成 node dest/main.js 即可。

那麼你可能會想到,如果一個 Docker 環境給你分配了兩個 u,那豈不是會浪費一個 u。理論上是的,那麼你就可以通過 pm2 啊啥的自己去管理吧,哈哈哈,不管。

Iron.js

說了這麼多,把上面的內容都沉澱下來,我得要給他取個名字,於是我就取成了 Iron。為啥叫 Iron 呢?因為 Iron Man。那為啥因為 Iron Man 呢?因為他製作的盔甲可以自由拆分,自動拼合。非常適合我們這個專案的形態。

不過這個專案什麼時候能沉澱下來,看我心情了。不過定個時間線吧,就在 4 月底,爭取搞定。

因為這裡面最大的問題就是配置的問題,需要深入依賴注入,所以會麻煩一些。但是其他的方面更多的只是一種約束吧。

這就是我用 Nest.js 一週以來的心得。暫時就想到這麼多,更多的內容等我後面再分析吧。

寫完睡覺,答應女票了,啦啦啦~

相關文章