Effect 深度课effect@4.1.4 · LTS
依赖与资源

L14 Layer 是构造配方,不是服务本身

⏱ 30 min难度 3/5前置:L13
本课目标
  • 读懂 Layer<ROut, Err, RIn> 三个参数
  • 区分 succeed / effect / scoped
前置课时尚未完成:L13

可以自行跳转学习,但按顺序推进会更省力;学情分析会把这类缺口归入「前置缺口」。

把依赖放在构造期

如果 Logger 内部直接去读 Config,服务的形状就会被实现细节污染。Layer 把「怎么造这个服务」单独抽出来:它是一份构造配方,声明产出什么、可能构造失败成什么、以及造它需要先有哪些服务。

三个参数与三种构造
← 代码可左右滑动 →
import { Context, Effect, Layer } from "effect"

// Layer<ROut, Err, RIn>
//       产出服务  构造错误  构造所需依赖

const ConfigLive: Layer.Layer<Config> =
  Layer.succeed(Config, { get: () => Effect.succeed({ dsn: "..." }) })

const LoggerLive: Layer.Layer<Logger, never, Config> =
  Layer.effect(Logger, Effect.gen(function* () {
    const config = yield* Config
    return { log: (m: string) => Effect.log(m) }
  }))

// 需要清理的资源用 Layer.scoped / Effect.acquireRelease
const DbLive: Layer.Layer<Db, DbError, Config | Logger> =
  Layer.scoped(Db, Effect.acquireRelease(connect, (conn) => close(conn)))
💡 Layer.scoped 让服务的生命周期与 Scope 绑定,关闭时自动释放。
🔬 Live / Test 约定

同一个服务通常配两份配方:XxxLive 用于生产、XxxTest 用于测试。类型系统保证两者形状一致,测试代码只改应用根部的那一处装配。

交互演练
关键术语
Layer<ROut, Err, RIn>
服务构造蓝图的三段式类型
scoped
带资源生命周期的构造方式
常见误区
× 把 Layer 当成「服务容器对象」,在业务代码里直接读它的属性。
随堂测验(即时反馈,答题计入掌握度)
Q1Layer<Logger, never, Config> 中的 Config 表示?
Q2服务需要在关闭时释放连接,用哪个构造?