雙旦已過,新年將至,midwayJs 向大家獻上賀禮。
之前我們向社群開放了我們的治理工具,也就是 Pandora.js 工具包,用於整個 Node.js 應用的監控和治理,我們承諾這不是結束,只是開源的開始。
隨著內部全棧應用數的越來越多,以及阿里業務不斷提升的複雜度,比如店鋪,搭建以及渲染等服務,隨著人員的不斷調動,產品的結構,程式碼的層級都隨著不斷的調整,我們急需一個能降低程式碼複雜度的解決方案,幫助我們渡過人員寒冬,這就對我們內部的基礎架構體系提出了不同的要求。
以往我們只需要讓使用者啟動伺服器,滿足 RPC/HTTP 服務即可,而在真正的全棧領域,似乎沒有太多的鑽研和沉澱。對此,我們將內部使用的 midway 整體解決方案進行了一次重塑,並且在設計之初就提出未來將對外進行開源。
正巧我們的第一款 Typescript 產品 Pandora.js 開源完畢,給了我們將程式碼用 Typescript 重寫的信心,也隨著 Egg.js 社群的壯大,我們相信,在不同的領域中,一定會有不同的產品,不同的解決方案。
Midway 正式基於這些考慮,將 IoC 引入到了框架中,同時學習了 NestJs ,引入了不少自定義的裝飾器,增強開發體驗,也將搭配團隊的其他產品,Pandora.js 和 Sandbox,將 Node.js 的開發體驗朝著全新的場景發展,讓使用者在開發過程中享受到前所未有的愉悅感。
在這裡感謝前期的 beta 測試中向我們提意見以及試用的同學,感謝大家的包容和支援,特別是 @ZQun 和 @yuu2lee4 兩位的積極參與。
下面來介紹新版本 midway 的一些特性。
-
基於 IoC 體系業務程式碼進行解耦,依賴統一管理統一初始化
-
常見的 web 場景裝飾器簡化業務開發
-
支援 Egg.js 的所有外掛體系,框架裝飾器統一編碼風格
-
基於 Typescript ,面向介面程式設計的編碼體驗
依賴注入疑問
在一年前,我們的業務程式碼是重重耦合,到處初始化,例項重複,但這並不是業務同學在程式碼架構方面的問題,而是在不斷的業務迭代,交接下,早就脫離了最初的設想,程式碼的設計跟不上需求的速度。
為此,我們嘗試引入了依賴注入的方案。依賴注入最早聽到是在 Java 端的 spring 框架,在 JS 方面,最早我們使用了 XML 做為基礎的 IoC 方案,雖然解決了不少耦合和初始化的問題,也發現前端在 XML 的感受吐槽頗多。
去年 Typescript 的大力發展之後,內部的很多專案都切換了過來,經過我們的調研,除了 NestJs 進行了自研以及在 Typescript 領域比較出名的 Inversify 模組,似乎很少有現成的易於擴充套件的模組。
基於這些情況,我們進行了這方面的自建,一方面方便內部的擴充套件,能更好的在現有的體系上擴充套件裝飾器,請求作用域等,另一方面也可以提升本身的能力,方便後續迭代。
我們產出了 injection
模組,作為我們整個框架的依賴注入基礎。
如今,injection
承載起了整個 midway 體系,它將框架程式碼,業務程式碼,外掛等都組合到了一起,像一個紐帶在這些之間傳輸資料。
通過依賴注入容器的管理,如上圖非常複雜的應用也能良好的維護和運作。
想看完整大圖,可以點選這裡。
面向裝飾器開發
得益於 Typescript 對 ES6 的良好支援,提供了一種為類宣告和成員新增註釋和超程式設計語法的方法。裝飾器作為TypeScript的實驗性功能能夠讓我們在開發中簡化程式碼。雖然是語法糖,但是帶來的好處卻不少。
我們拿一個簡單的例子,從 Controller 一步步經過 Service/Manager 向資料庫拿資料,在多層的架構體系下,以往的程式碼大概率需要 new 出不同的例項,並且需要繫結到路由層,這邊為了方便理解,程式碼放到了一起。
export = (app) => {
const home = new HomeController();
app.get('/', home.index);
}
class HomeController extends Controller {
reportService: IReportService;
constructor() {
this.reportService = new ReportService();
}
async index(ctx) {
ctx.body = await this.reportService.getReport();
}
}
class ReportService implements IReportService {
reporter: IReportManager;
constructor() {
this.reporter = new ReporterManager();
}
async getReport(id: number) {
return await this.reporter.get(id);
}
}
class ReporterManager implements IReportManager {
db;
constructor() {
this.initDB();
}
initDB() {
// open connection
}
async get() {
// return data from db;
}
}
複製程式碼
經過 IoC 相關的 @provide
和 @inject
裝飾器修飾以及其他 web 層的裝飾器修飾過後,不僅僅只是程式碼量的減少,業務的程式碼也不再有例項化的過程。以往還需要考慮在構造器中做非同步的操作,比如初始化時需要做非同步連線資料庫,這個時候也不再需要考慮,直接使用 @init
裝飾即可。
至此,我們會更加專注於面向介面進行程式設計,抽象,將程式碼設計的時間更多的花在理解需求,解決問題上。
@provide()
@controller()
export class HomeController {
@inject()
reportService: IReportService;
@get('/')
async index(ctx) {
ctx.body = await this.reportService.getReport();
}
}
@provide()
class ReportService implements IReportService {
@inject()
reporter: IReportManager;
async getReport(id: number) {
return await this.reporter.get(id);
}
}
@provide()
class ReporterManager implements IReportManager {
@inject()
db;
@init()
initDB() {
// open connection
}
async get() {
// return data from db;
}
}
複製程式碼
入口能力
就像上面提到的 @controller
裝飾器類似,針對入口型的程式碼,我們在框架層面擴充套件了其他裝飾器,比如針對計劃任務形式我們提供了 @schedule
裝飾器,簡化使用者開發的程式碼量。
import { schedule } from 'midway';
@schedule({
interval: 2333, // 2.333s 間隔
type: 'worker', // 指定某一個 worker 執行
})
export class HelloCron {
// 定時執行的具體任務
async exec(ctx) {
ctx.logger.info(process.pid, 'hello');
}
}
複製程式碼
在下一版本中,我們將開放自定義裝飾器的能力,方便更多場景的使用。
框架擴充套件
由於在大多數場景下,使用了裝飾器已經依賴注入的寫法,使得自己的業務程式碼,乃至三方的模組都能很好的融在一起,除了這些之外,有的同學會疑問,原本的外掛,配置,上下文部分如何融入到這個體系,我們這就來解答。
在原本熟悉的體系中,只要有 app
, ctx
物件就無敵了,所有的東西都可以拿。而在 midway 中,為了和 web 層進行解耦,我們隱去了這些物件,只希望業務程式碼和 IoC 容器打交道。
為此我們提供了 @config
和 @plugin
裝飾器用於獲取不同的方法,通過這樣的形式和框架進行解耦,比如在任意程式碼中如下使用。
@provide()
class ReportService implements IReportService {
@config('env')
env;
@plugin('httpclient')
httpclient;
@inject()
reporter: IReportManager;
async getReport(id: number) {
const rid = this.httpclient.request('/api/' + id);
return await this.reporter.get(rid);
}
}
複製程式碼
正是這樣一點點的調整,我們將整個應用的程式碼風格保持了到了一致,不管程式碼幾經易手,維護的同學也能快速上手,並且繼續迭代下去。
最後
正向我們在 Pandora.js 釋出時說的那樣,midway 也是 MidwayJs 團隊長期維護的一款產品,同樣不會是最後一款,前幾個月,我們就計劃將我們的監控平臺 Sandbox 帶出來回饋給社群,雖然道阻且長,任務艱辛,我們依舊在努力前行,歡迎關注。
最後,midway 的地址在這 github.com/midwayjs/mi…,歸屬在 midwayJs Group 下。歡迎走過路過點個 Star,給我們提提建議,提提程式碼。
Midway 官網:midwayjs.org/midway/