近年微服務架構興起,對於規模較小的開發團隊而言,一開始就拆分為多個微服務是個沉重的負擔,所以大多還是從單體式架構 monolithic 出發,往後再逐步拆分為微服務。雖然這種開發方式廣泛被採用,但實務上並不是所有團隊都能輕鬆轉換至微服務架構,這取決於單體式架構裡的各個功能實作上是否能低耦合,甚至模組化。
另一方面,即使往後將各個功能拆分成微服務,因為通常拆分的時間點不一, 或是由不同的工程師負責實作,如果沒有事先規劃及規定統一的作法,就容易導致各個微服務在基礎設施功能方面的實作方式上有所差異,提高了往後開發及維運的成本。
本專案是以模組化為準則的單體式架構,在底層的基礎設施函式庫上建立多個子專案,當未來要將子專案拆分為微服務時,能把轉換工作降至最低。規劃子專案的方式,除了可以採用 Backend for Frontend (BFF) 的方式,簡單地把多個前端與有商業邏輯的後端分開之外,也可以遵循 DDD 的概念,將每個子專案對應到一個 Bounded Context。
在專案結構上,為了讓各個子專案儘量獨立不耦合,我採用 Gradle Multi-Projects Build 分隔子專案,強迫開發者意識到這是一段跨專案依賴呼叫的程式碼,未來拆分為微服務時,將會從直接函式呼叫改為遠端呼叫。
在架構設計方面,每個子專案都會有一個 projectId,因為底層的基礎設施函式庫多考慮了 project 維度,而且產生的資料也已經記錄了 projectId,當未來要把子專案拆分為微服務時,將會輕鬆許多,例如 Logging 功能會記錄這個請求是由那個 project 處理。除了 projectId 之外,每個子專案還可以定義自己的 User Type 及 User Role,還有 Notification Type,讓商業邏輯 Domain Model Object 可以獨立分開,然而底層的 API 權限檢查、寄送訊息通知…等功能實作程式碼都是相同的。
在實作及部署方面,每個子專案都是一個 Ktor Module,我們可以根據 Ktor Module 本身的設計,修改設定檔中的 ktor.application.modules 屬性值來決定要部署那些子專案。如果一個子專案沒有被部署,則執行期不會進行任何初始化動作,也就不會部署任何 API。理論上,我們可以把 A, B 2個子專案分開部署,然後設定 nginx 或 API Gateway 重新導向,如果發現 A 專案緊急有 Bug 需要修正,那麼 B 專案將不會被影響,即使 A, B 都是在同一個單體式架構開發的。不過我覺得這是單體式架構不得已的作法,所以也不是很推薦採用就是了。
這2天已說明我的 side project 目標、設計概念、實作項目及要解決的問題,明天開始就要進入實作程式碼。