昨天我們創建了四個 Layer
,今天跟大家分享如何把多個它們組合起來,並最終提供給需要的 Effect 函式。
組合 Layer 有兩種方法,分別是 Merging,還有 Composing。
Merging 用於兩個沒有依賴關係的 Layer 相互結合,例如上圖,MongoDB Service 和 MinIO Service 沒有依賴關係,所以我們可以用 Layer.merge 把它們合起來,像是以下這樣
const PersistenceLayer = Layer.merge(MongoLayer, MinIoSLayer)
合併後的 Layer 的型別會變成
Layer.Layer<EnvService, never, MongoService | MinIoService>
更通用一點來說,我們假設有兩個 Layer ,他們各自有不同的依賴與不同的輸出
Layer.Layer<A1, never, B1> //Layer1
Layer.Layer<A2, never, B2> //Layer2
那最後經過 merge 操作,會變成下面這樣
Layer.Layer<A1 | A2, never, B1 | B2>
新產生的 Layer 會同時兼具他們的輸入與輸出
用示意圖總結,經過合併後我們可以想像原本的兩個服務合成了一個新的服務,最終使依賴關係變成了如下的線性排列。
像是上面這種排列方式,其實我們在 D05 就有提過類似的概念,也就是 pipe
還有 flow
。只是因為服務的提供關係,我們要把順序反過來。想像最下層服務需要上層的服務,結合以後又再需要更上層的服務,最後產生出一個新的服務,就跟我們在用 pipe 做函式的串接非常相似,只是串接順序改變,還有串接的目標變成另外一個抽象型別而已。
具體來說可以這樣做
const FileLayerLive = pipe(
FileServiceLayer,
Layer.use(PersistanceLayer),
Layer.use(EnvLayer)
)
FileServiceLayer 本身型別是
Layer.Layer<MinIoService | MongoService, never, FileService>
串接 PersistanceLayer 以後輸出型別變成
Layer.Layer<EnvService, never, FileService>
最後完整的輸出型別變成
Layer.Layer<never, never, FileService>
前面做了好多準備工作就是為了這一刻,假設我們有一個功能,新增課程
const program = pipe(
FileService.context,
Effect.flatMap((fs) => fs.createCourse('data')({} as unknown as Course)),
)
Effect.Effect<FileService, FileServiceError, Course>
最終產生的型別請參考上方程式碼區塊。這個型別表示他需要提供 FileService 才可以正常運行,可是 FileService 本身又需要另外的 PersistenceService 作為依賴,而 PersistenceService 又有 EnvService 作為依賴,這樣套下去有點麻煩。幸好我們一開始就利用 Layer 把依賴注入的工作都整合在一起,最終只要多加一行 Effect.provide(FileServiceLive)
就可以簡單完成依賴注入。
const program = pipe(
FileService.context,
Effect.flatMap((fs) => fs.createCourse('data')({} as unknown as Course)),
Effect.provide(FileServiceLive)
)
Effect.Effect<never, FileServiceError, Course>
最終 Effect<R, E, A> 裡面的 R 變成了 never,我們只要再搭配 runPromise
之類的運行函式,就可以把程式執行起來。