effect
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseEffect-TS Best Practices
Effect-TS 最佳实践
Opinionated patterns for Effect-TS codebases. Effect provides typed functional programming with composable errors, dependency injection, and observability.
针对 Effect-TS 代码库的约定式模式。Effect 提供具备可组合错误处理、依赖注入和可观测性的类型化函数式编程能力。
Critical Rules
核心规则
- NEVER use or type casts (
any) - Useas Typefor branded types,Schema.make()for parsingSchema.decodeUnknown() - Don't use when error type is
catchAll- No errors to catchnever - Never use global in Effect channels - Use
Errorfor domain errorsSchema.TaggedError - Ban - Lint against this
{ disableValidation: true } - Don't wrap safe operations in Effect - Only use for throwing operations
Effect.try() - Use not
mapError- Distinguish expected errors from bugscatchAllCause - Never silently swallow errors - Failures MUST be visible in the Effect's error channel E
- 绝对不要使用或类型断言(
any) - 使用as Type创建品牌类型,使用Schema.make()进行解析Schema.decodeUnknown() - 当错误类型为时,不要使用
never- 没有需要捕获的错误catchAll - 绝对不要在Effect通道中使用全局- 针对领域错误使用
ErrorSchema.TaggedError - 禁止使用- 通过Lint规则禁用该选项
{ disableValidation: true } - 不要将安全操作包裹在Effect中 - 仅对可能抛出异常的操作使用
Effect.try() - 使用而非
mapError- 区分预期错误与程序缺陷catchAllCause - 绝对不要静默吞掉错误 - 失败必须在Effect的错误通道E中可见
Quick Reference
快速参考
| Pattern | DON'T | DO |
|---|---|---|
| Service definition | | |
| Error types | Generic | |
| Branded IDs | Raw | |
| Running effects | | Return |
| Logging | | |
| Configuration | | |
| Method tracing | Manual spans | |
| Nullable results | | |
| State | Mutable variables | |
| Time | | |
| 模式 | 不要做 | 要做 |
|---|---|---|
| 服务定义 | | 带 |
| 错误类型 | 通用 | 带上下文字段的 |
| 品牌化ID | 原生 | |
| 运行Effect | 在服务中使用 | 返回 |
| 日志 | | 带结构化数据的 |
| 配置 | | 带验证的 |
| 方法追踪 | 手动Span | |
| 可空结果 | | |
| 状态 | 可变变量 | |
| 时间 | | |
Service Pattern
服务模式
typescript
class UserService extends Effect.Service<UserService>()("UserService", {
dependencies: [DatabaseService.Default],
effect: Effect.gen(function* () {
const db = yield* DatabaseService
return {
findById: Effect.fn("UserService.findById")(
(id: UserId) => db.query(/* ... */)
),
}
}),
}) {}
// Usage - dependencies auto-provided
UserService.findById(userId)typescript
class UserService extends Effect.Service<UserService>()("UserService", {
dependencies: [DatabaseService.Default],
effect: Effect.gen(function* () {
const db = yield* DatabaseService
return {
findById: Effect.fn("UserService.findById")(
(id: UserId) => db.query(/* ... */)
),
}
}),
}) {}
// 使用方式 - 自动提供依赖
UserService.findById(userId)Error Handling
错误处理
typescript
// Define domain-specific errors
class UserNotFoundError extends Schema.TaggedError<UserNotFoundError>()(
"UserNotFoundError",
{ userId: UserId, message: Schema.String }
) {}
// Handle with catchTag (preserves type info)
effect.pipe(
Effect.catchTag("UserNotFoundError", (e) => /* handle */),
Effect.catchTag("AuthExpiredError", (e) => /* handle */)
)typescript
// 定义领域专属错误
class UserNotFoundError extends Schema.TaggedError<UserNotFoundError>()(
"UserNotFoundError",
{ userId: UserId, message: Schema.String }
) {}
// 使用catchTag处理(保留类型信息)
effect.pipe(
Effect.catchTag("UserNotFoundError", (e) => /* 处理逻辑 */),
Effect.catchTag("AuthExpiredError", (e) => /* 处理逻辑 */)
)Schema Pattern
模式定义
typescript
// Branded ID
const UserId = Schema.String.pipe(Schema.brand("@App/UserId"))
// Domain entity with Schema.Class
class User extends Schema.Class<User>("User")({
id: UserId,
email: Schema.String,
createdAt: Schema.DateFromSelf,
}) {
get displayName() { return this.email.split("@")[0] }
}typescript
// 品牌化ID
const UserId = Schema.String.pipe(Schema.brand("@App/UserId"))
// 基于Schema.Class的领域实体
class User extends Schema.Class<User>("User")({
id: UserId,
email: Schema.String,
createdAt: Schema.DateFromSelf,
}) {
get displayName() { return this.email.split("@")[0] }
}Layer Composition
层组合
typescript
// Declare dependencies in service, not at usage
const MainLayer = Layer.mergeAll(
UserServiceLive,
AuthServiceLive,
DatabaseLive
)
// Run program
Effect.runPromise(program.pipe(Effect.provide(MainLayer)))typescript
// 在服务中声明依赖,而非在使用时
const MainLayer = Layer.mergeAll(
UserServiceLive,
AuthServiceLive,
DatabaseLive
)
// 运行程序
Effect.runPromise(program.pipe(Effect.provide(MainLayer)))Detailed Guides
详细指南
- Anti-Patterns - Forbidden patterns with fixes
- Error Patterns - Domain errors, rich context, HTTP mapping
- Schema Patterns - Branded types, transforms, validation
- Service Patterns - Effect.Service, dependency injection
- Layer Patterns - Composition, memoization, testing
- Observability Patterns - Logging, tracing, metrics
- SQL Patterns - Database integration, transactions
- Testing Patterns - effect-vitest, property testing, Testcontainers
- Atom Patterns - React state management with Effect
- RPC & Cluster Patterns - Distributed systems
- 反模式 - 包含修复方案的禁用模式
- 错误模式 - 领域错误、丰富上下文、HTTP映射
- 模式定义 - 品牌类型、转换、验证
- 服务模式 - Effect.Service、依赖注入
- 层模式 - 组合、记忆化、测试
- 可观测性模式 - 日志、追踪、指标
- SQL模式 - 数据库集成、事务
- 测试模式 - effect-vitest、属性测试、Testcontainers
- Atom模式 - 结合Effect的React状态管理
- RPC与集群模式 - 分布式系统