Effect v4 Best Practices
SkillDev toolsEffect v4 patterns guide. Use when creating Effect services, defining error types, writing Effect programs, managing Layer composition, or wrapping async operations with Effect
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Effect v4 Best Practices skill
What this skill tells your AI
The instructions your AI receives, as published by zhe-qi/clhoria-template in .agents/skills/effect-v4/SKILL.md and read by ahel’s review.
本 skill 为 Effect v4 (4.0.0-beta.48) 在本项目中的使用提供规范化指南。
技术栈
- Effect: 4.0.0-beta.48(
Context.Service、Data.TaggedError、Layer) - HTTP: Hono(非 Effect HttpApi)
- 验证: Zod v4(非 Effect Schema)
- ORM: Drizzle(PostgreSQL)
- 日志: Pino(非 Effect.log)
- 队列: BullMQ + Redis (ioredis)
文件结构
src/lib/infrastructure/effect/
├── index.ts # Barrel exports
├── errors.ts # Data.TaggedError 定义
├── services/ # Context.Service 定义
│ ├── db.ts # DbService
│ ├── logger.ts # LoggerService
│ └── bullmq.ts # BullMQService
└── layers/
└── live.ts # InfraLayer = Layer.mergeAll(...)
核心规则速查
| 类别 | DO | DON'T |
|---|---|---|
| Service 定义 | Context.Service<Self, Shape>()("tag") | Effect.Service (v3 API) |
| Service 构造 | Layer.succeed(Tag, instance) / Layer.effect(Tag, ...) | 在使用处 Effect.provide |
| 错误类型 | Data.TaggedError("Tag")<{ fields }> | Schema.TaggedError / 普通 Error |
| 错误处理 | catchTag / catchTags 按标签匹配 | catchAll / mapError 丢失类型 |
| 追踪函数 | Effect.fn("Service.method")(function* ...) | 匿名 generator 无追踪名 |
| 异步包装 | Effect.tryPromise({ try, catch }) | Effect 内部 raw await |
| 错误抛出 | yield* Effect.fail(new XxxError(...)) | Effect.gen 内 throw |
| Layer 组合 | Layer.mergeAll 平铺同级 | 深层嵌套 Layer.provide |
| Layer 链式 | Layer.provideMerge 增量组合 | 多层 Layer.provide(类型爆炸) |
| 执行边界 | Effect.runPromise 仅在 Hono handler 入口 | Service 内部 runPromise |
v3 → v4 迁移对照
| v3 | v4 | 说明 |
|---|---|---|
Effect.Service<T>()("name", { accessors, dependencies, effect }) | Context.Service<T, Shape>()("name") + Layer.succeed/effect | Service 类只是标签,Layer 独立构造 |
accessors: true → Service.method() | yield* ServiceTag → service.method() | 先获取 service 实例再调方法 |
dependencies: [Dep.Default] | Layer.provide(depLayer) / Layer.provideMerge(depLayer) | 依赖关系在 Layer 组合时声明 |
Schema.TaggedError<T>()("tag", { fields }) | Data.TaggedError("tag")<{ fields }> | 不需要 Schema 字段,用 TS readonly |
HttpApiSchema.annotations({ status: 404 }) | Hono handler 中 c.json(Resp.fail(...), 404) | HTTP 状态码在路由层处理 |
Config.string("KEY") | import env from "@/env" | 项目用 Zod 验证的 env |
Effect.log("msg") | logger.info({ data }, "[Module]: msg") | 项目用 Pino |
Schema.Option(Schema.String) | field: string | null | 项目用 Drizzle/Zod 可空类型 |
与项目集成
Effect 在本项目中的定位是 基础设施编排层,不是全栈框架。边界清晰:
Hono Handler (async/await)
└─ Effect.runPromise(program) ← 边界:进入 Effect 世界
└─ Effect.gen(function* () {
const db = yield* DbService; ← 获取 service
const result = yield* Effect.tryPromise(...); ← 异步操作
return result;
})
入口点(允许 Effect.runPromise):
- Hono route handler
- BullMQ Worker processor
- Bootstrap 初始化
- Singleton destroy 回调
Service 内部(禁止 Effect.runPromise):
- 返回
Effect.Effect<A, E, R> - 用
yield*组合其他 Effect - 用
Effect.tryPromise包装异步
参考文档
- Service 模式 — Context.Service + Layer 定义、Effect.fn 追踪
- 错误模式 — Data.TaggedError + catchTag/catchTags
- Effect 组合 — Effect.gen/fn/tryPromise/并发
- 资源管理 — acquireUseRelease/Scope/优雅关闭
- 反模式 — 禁止模式 + 项目例外
Signals
- GitHub stars
- 190
- Forks
- 19
- Last commit
- Jul 2026
Advanced
- Catalog kind
- skill
- Gateway key
effect-v4-zhe-qi- Source
- github.com/zhe-qi/clhoria-template