effect-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseEffect-TS Best Practices
Effect-TS 最佳实践
This skill enforces opinionated, consistent patterns for Effect-TS codebases. These patterns optimize for type safety, testability, observability, and maintainability.
本规则针对Effect-TS代码库强制执行具有主见性、一致性的模式。这些模式在类型安全性、可测试性、可观测性和可维护性方面进行了优化。
Quick Reference: Critical Rules
快速参考:关键规则
| Category | DO | DON'T |
|---|---|---|
| Services | | |
| Dependencies | | Manual |
| Errors | | Plain classes or generic Error |
| Error Specificity | | Generic |
| Error Handling | | |
| IDs | | Plain |
| Functions | | Anonymous generators |
| Logging | | |
| Config | | |
| Options | | |
| Nullability | | |
| Atoms | | Creating atoms inside render |
| Atom State | | Forgetting keepAlive for persistent state |
| Atom Updates | | |
| Atom Cleanup | | Missing cleanup for event listeners |
| Atom Results | | Ignoring loading/error states |
| 类别 | 推荐做法 | 禁止做法 |
|---|---|---|
| 服务 | 使用带 | 对业务逻辑使用 |
| 依赖项 | 在服务中使用 | 在使用位置手动调用 |
| 错误 | 使用带 | 使用普通类或通用Error |
| 错误特异性 | 使用 | 使用通用的 |
| 错误处理 | 使用 | 使用 |
| ID | 使用 | 对实体ID使用普通 |
| 函数 | 使用 | 使用匿名生成器 |
| 日志 | 使用带结构化数据的 | 使用 |
| 配置 | 使用带验证的 | 直接使用 |
| 可选值 | 使用 | 使用 |
| 空值处理 | 在领域类型中使用 | 使用 |
| 原子 | 在组件外部使用 | 在渲染内部创建原子 |
| 原子状态 | 对全局状态使用 | 忘记为持久化状态添加keepAlive |
| 原子更新 | 在React组件中使用 | 从React中命令式调用 |
| 原子清理 | 对副作用使用 | 遗漏事件监听器的清理 |
| 原子结果 | 使用带 | 忽略加载/错误状态 |
Service Definition Pattern
服务定义模式
Always use for business logic services. This provides automatic accessors, built-in layer, and proper dependency declaration.
Effect.ServiceDefaulttypescript
import { Effect } from "effect"
export class UserService extends Effect.Service<UserService>()("UserService", {
accessors: true,
dependencies: [UserRepo.Default, CacheService.Default],
effect: Effect.gen(function* () {
const repo = yield* UserRepo
const cache = yield* CacheService
const findById = Effect.fn("UserService.findById")(function* (id: UserId) {
const cached = yield* cache.get(id)
if (Option.isSome(cached)) return cached.value
const user = yield* repo.findById(id)
yield* cache.set(id, user)
return user
})
const create = Effect.fn("UserService.create")(function* (data: CreateUserInput) {
const user = yield* repo.create(data)
yield* Effect.log("User created", { userId: user.id })
return user
})
return { findById, create }
}),
}) {}
// Usage - dependencies are already wired
const program = Effect.gen(function* () {
const user = yield* UserService.findById(userId)
return user
})
// At app root
const MainLive = Layer.mergeAll(UserService.Default, OtherService.Default)When is acceptable:
Context.Tag- Infrastructure with runtime injection (Cloudflare KV, worker bindings)
- Factory patterns where resources are provided externally
See for detailed patterns.
references/service-patterns.md始终为业务逻辑服务使用。它会提供自动访问器、内置的层以及正确的依赖声明。
Effect.ServiceDefaulttypescript
import { Effect } from "effect"
export class UserService extends Effect.Service<UserService>()("UserService", {
accessors: true,
dependencies: [UserRepo.Default, CacheService.Default],
effect: Effect.gen(function* () {
const repo = yield* UserRepo
const cache = yield* CacheService
const findById = Effect.fn("UserService.findById")(function* (id: UserId) {
const cached = yield* cache.get(id)
if (Option.isSome(cached)) return cached.value
const user = yield* repo.findById(id)
yield* cache.set(id, user)
return user
})
const create = Effect.fn("UserService.create")(function* (data: CreateUserInput) {
const user = yield* repo.create(data)
yield* Effect.log("User created", { userId: user.id })
return user
})
return { findById, create }
}),
}) {}
// Usage - dependencies are already wired
const program = Effect.gen(function* () {
const user = yield* UserService.findById(userId)
return user
})
// At app root
const MainLive = Layer.mergeAll(UserService.Default, OtherService.Default)可接受使用的场景:
Context.Tag- 需运行时注入的基础设施(Cloudflare KV、worker绑定)
- 资源由外部提供的工厂模式
详细模式请参考。
references/service-patterns.mdError Definition Pattern
错误定义模式
Always use for errors. This makes them serializable (required for RPC) and provides consistent structure.
Schema.TaggedErrortypescript
import { Schema } from "effect"
import { HttpApiSchema } from "@effect/platform"
export class UserNotFoundError extends Schema.TaggedError<UserNotFoundError>()(
"UserNotFoundError",
{
userId: UserId,
message: Schema.String,
},
HttpApiSchema.annotations({ status: 404 }),
) {}
export class UserCreateError extends Schema.TaggedError<UserCreateError>()(
"UserCreateError",
{
message: Schema.String,
cause: Schema.optional(Schema.String),
},
HttpApiSchema.annotations({ status: 400 }),
) {}Error handling - use /:
catchTagcatchTagstypescript
// CORRECT - preserves type information
yield* repo.findById(id).pipe(
Effect.catchTag("DatabaseError", (err) =>
Effect.fail(new UserNotFoundError({ userId: id, message: "Lookup failed" }))
),
Effect.catchTag("ConnectionError", (err) =>
Effect.fail(new ServiceUnavailableError({ message: "Database unreachable" }))
),
)
// CORRECT - multiple tags at once
yield* effect.pipe(
Effect.catchTags({
DatabaseError: (err) => Effect.fail(new UserNotFoundError({ userId: id, message: err.message })),
ValidationError: (err) => Effect.fail(new InvalidEmailError({ email: input.email, message: err.message })),
}),
)始终为错误使用。这会让错误可序列化(RPC场景必需)并提供一致的结构。
Schema.TaggedErrortypescript
import { Schema } from "effect"
import { HttpApiSchema } from "@effect/platform"
export class UserNotFoundError extends Schema.TaggedError<UserNotFoundError>()(
"UserNotFoundError",
{
userId: UserId,
message: Schema.String,
},
HttpApiSchema.annotations({ status: 404 }),
) {}
export class UserCreateError extends Schema.TaggedError<UserCreateError>()(
"UserCreateError",
{
message: Schema.String,
cause: Schema.optional(Schema.String),
},
HttpApiSchema.annotations({ status: 400 }),
) {}错误处理 - 使用/:
catchTagcatchTagstypescript
// CORRECT - preserves type information
yield* repo.findById(id).pipe(
Effect.catchTag("DatabaseError", (err) =>
Effect.fail(new UserNotFoundError({ userId: id, message: "Lookup failed" }))
),
Effect.catchTag("ConnectionError", (err) =>
Effect.fail(new ServiceUnavailableError({ message: "Database unreachable" }))
),
)
// CORRECT - multiple tags at once
yield* effect.pipe(
Effect.catchTags({
DatabaseError: (err) => Effect.fail(new UserNotFoundError({ userId: id, message: err.message })),
ValidationError: (err) => Effect.fail(new InvalidEmailError({ email: input.email, message: err.message })),
}),
)Prefer Explicit Over Generic Errors
优先选择显式错误而非通用错误
Every distinct failure reason deserves its own error type. Don't collapse multiple failure modes into generic HTTP errors.
typescript
// WRONG - Generic errors lose information
export class NotFoundError extends Schema.TaggedError<NotFoundError>()(
"NotFoundError",
{ message: Schema.String },
HttpApiSchema.annotations({ status: 404 }),
) {}
// Then mapping everything to it:
Effect.catchTags({
UserNotFoundError: (err) => Effect.fail(new NotFoundError({ message: "Not found" })),
ChannelNotFoundError: (err) => Effect.fail(new NotFoundError({ message: "Not found" })),
MessageNotFoundError: (err) => Effect.fail(new NotFoundError({ message: "Not found" })),
})
// Frontend gets useless: { _tag: "NotFoundError", message: "Not found" }
// Which resource? User? Channel? Message? Can't tell!typescript
// CORRECT - Explicit domain errors with rich context
export class UserNotFoundError extends Schema.TaggedError<UserNotFoundError>()(
"UserNotFoundError",
{ userId: UserId, message: Schema.String },
HttpApiSchema.annotations({ status: 404 }),
) {}
export class ChannelNotFoundError extends Schema.TaggedError<ChannelNotFoundError>()(
"ChannelNotFoundError",
{ channelId: ChannelId, message: Schema.String },
HttpApiSchema.annotations({ status: 404 }),
) {}
export class SessionExpiredError extends Schema.TaggedError<SessionExpiredError>()(
"SessionExpiredError",
{ sessionId: SessionId, expiredAt: Schema.DateTimeUtc, message: Schema.String },
HttpApiSchema.annotations({ status: 401 }),
) {}
// Frontend can now show specific UI:
// - UserNotFoundError → "User doesn't exist"
// - ChannelNotFoundError → "Channel was deleted"
// - SessionExpiredError → "Your session expired. Please log in again."See for error remapping and retry patterns.
references/error-patterns.md每个不同的失败原因都应拥有自己的错误类型。 不要将多种失败模式合并为通用HTTP错误。
typescript
// WRONG - Generic errors lose information
export class NotFoundError extends Schema.TaggedError<NotFoundError>()(
"NotFoundError",
{ message: Schema.String },
HttpApiSchema.annotations({ status: 404 }),
) {}
// Then mapping everything to it:
Effect.catchTags({
UserNotFoundError: (err) => Effect.fail(new NotFoundError({ message: "Not found" })),
ChannelNotFoundError: (err) => Effect.fail(new NotFoundError({ message: "Not found" })),
MessageNotFoundError: (err) => Effect.fail(new NotFoundError({ message: "Not found" })),
})
// Frontend gets useless: { _tag: "NotFoundError", message: "Not found" }
// Which resource? User? Channel? Message? Can't tell!typescript
// CORRECT - Explicit domain errors with rich context
export class UserNotFoundError extends Schema.TaggedError<UserNotFoundError>()(
"UserNotFoundError",
{ userId: UserId, message: Schema.String },
HttpApiSchema.annotations({ status: 404 }),
) {}
export class ChannelNotFoundError extends Schema.TaggedError<ChannelNotFoundError>()(
"ChannelNotFoundError",
{ channelId: ChannelId, message: Schema.String },
HttpApiSchema.annotations({ status: 404 }),
) {}
export class SessionExpiredError extends Schema.TaggedError<SessionExpiredError>()(
"SessionExpiredError",
{ sessionId: SessionId, expiredAt: Schema.DateTimeUtc, message: Schema.String },
HttpApiSchema.annotations({ status: 401 }),
) {}
// Frontend can now show specific UI:
// - UserNotFoundError → "该用户不存在"
// - ChannelNotFoundError → "该频道已被删除"
// - SessionExpiredError → "您的会话已过期,请重新登录。"错误重映射和重试模式请参考。
references/error-patterns.mdSchema & Branded Types Pattern
Schema与品牌类型模式
Brand all entity IDs for type safety across service boundaries:
typescript
import { Schema } from "effect"
// Entity IDs - always branded
export const UserId = Schema.UUID.pipe(Schema.brand("@App/UserId"))
export type UserId = Schema.Schema.Type<typeof UserId>
export const OrganizationId = Schema.UUID.pipe(Schema.brand("@App/OrganizationId"))
export type OrganizationId = Schema.Schema.Type<typeof OrganizationId>
// Domain types - use Schema.Struct
export const User = Schema.Struct({
id: UserId,
email: Schema.String,
name: Schema.String,
organizationId: OrganizationId,
createdAt: Schema.DateTimeUtc,
})
export type User = Schema.Schema.Type<typeof User>
// Input types for mutations
export const CreateUserInput = Schema.Struct({
email: Schema.String.pipe(Schema.pattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)),
name: Schema.String.pipe(Schema.minLength(1)),
organizationId: OrganizationId,
})
export type CreateUserInput = Schema.Schema.Type<typeof CreateUserInput>When NOT to brand:
- Simple strings that don't cross service boundaries (URLs, file paths)
- Primitive config values
See for transforms and advanced patterns.
references/schema-patterns.md为所有实体ID添加品牌标识,以确保跨服务边界的类型安全性:
typescript
import { Schema } from "effect"
// Entity IDs - always branded
export const UserId = Schema.UUID.pipe(Schema.brand("@App/UserId"))
export type UserId = Schema.Schema.Type<typeof UserId>
export const OrganizationId = Schema.UUID.pipe(Schema.brand("@App/OrganizationId"))
export type OrganizationId = Schema.Schema.Type<typeof OrganizationId>
// Domain types - use Schema.Struct
export const User = Schema.Struct({
id: UserId,
email: Schema.String,
name: Schema.String,
organizationId: OrganizationId,
createdAt: Schema.DateTimeUtc,
})
export type User = Schema.Schema.Type<typeof User>
// Input types for mutations
export const CreateUserInput = Schema.Struct({
email: Schema.String.pipe(Schema.pattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)),
name: Schema.String.pipe(Schema.minLength(1)),
organizationId: OrganizationId,
})
export type CreateUserInput = Schema.Schema.Type<typeof CreateUserInput>无需添加品牌标识的场景:
- 不跨服务边界的简单字符串(URL、文件路径)
- 原始配置值
转换和高级模式请参考。
references/schema-patterns.mdFunction Pattern with Effect.fn
使用Effect.fn的函数模式
Always use for service methods. This provides automatic tracing with proper span names:
Effect.fntypescript
// CORRECT - Effect.fn with descriptive name
const findById = Effect.fn("UserService.findById")(function* (id: UserId) {
yield* Effect.annotateCurrentSpan("userId", id)
const user = yield* repo.findById(id)
return user
})
// CORRECT - Effect.fn with multiple parameters
const transfer = Effect.fn("AccountService.transfer")(
function* (fromId: AccountId, toId: AccountId, amount: number) {
yield* Effect.annotateCurrentSpan("fromId", fromId)
yield* Effect.annotateCurrentSpan("toId", toId)
yield* Effect.annotateCurrentSpan("amount", amount)
// ...
}
)始终为服务方法使用。它会提供带有正确跨度名称的自动追踪功能:
Effect.fntypescript
// CORRECT - Effect.fn with descriptive name
const findById = Effect.fn("UserService.findById")(function* (id: UserId) {
yield* Effect.annotateCurrentSpan("userId", id)
const user = yield* repo.findById(id)
return user
})
// CORRECT - Effect.fn with multiple parameters
const transfer = Effect.fn("AccountService.transfer")(
function* (fromId: AccountId, toId: AccountId, amount: number) {
yield* Effect.annotateCurrentSpan("fromId", fromId)
yield* Effect.annotateCurrentSpan("toId", toId)
yield* Effect.annotateCurrentSpan("amount", amount)
// ...
}
)Layer Composition
Layer组合
Declare dependencies in the service, not at usage sites:
typescript
// CORRECT - dependencies in service definition
export class OrderService extends Effect.Service<OrderService>()("OrderService", {
accessors: true,
dependencies: [
UserService.Default,
ProductService.Default,
PaymentService.Default,
],
effect: Effect.gen(function* () {
const users = yield* UserService
const products = yield* ProductService
const payments = yield* PaymentService
// ...
}),
}) {}
// At app root - simple merge
const AppLive = Layer.mergeAll(
OrderService.Default,
// Infrastructure layers (intentionally not in dependencies)
DatabaseLive,
RedisLive,
)See for testing layers and config-dependent layers.
references/layer-patterns.md在服务中声明依赖项,而非在使用位置:
typescript
// CORRECT - dependencies in service definition
export class OrderService extends Effect.Service<OrderService>()("OrderService", {
accessors: true,
dependencies: [
UserService.Default,
ProductService.Default,
PaymentService.Default,
],
effect: Effect.gen(function* () {
const users = yield* UserService
const products = yield* ProductService
const payments = yield* PaymentService
// ...
}),
}) {}
// At app root - simple merge
const AppLive = Layer.mergeAll(
OrderService.Default,
// Infrastructure layers (intentionally not in dependencies)
DatabaseLive,
RedisLive,
)测试层和依赖配置的层请参考。
references/layer-patterns.mdOption Handling
Option处理
Never use . Always handle both cases explicitly:
Option.getOrThrowtypescript
// CORRECT - explicit handling
yield* Option.match(maybeUser, {
onNone: () => Effect.fail(new UserNotFoundError({ userId, message: "Not found" })),
onSome: (user) => Effect.succeed(user),
})
// CORRECT - with getOrElse for defaults
const name = Option.getOrElse(maybeName, () => "Anonymous")
// CORRECT - Option.map for transformations
const upperName = Option.map(maybeName, (n) => n.toUpperCase())永远不要使用。始终显式处理两种情况:
Option.getOrThrowtypescript
// CORRECT - explicit handling
yield* Option.match(maybeUser, {
onNone: () => Effect.fail(new UserNotFoundError({ userId, message: "未找到" })),
onSome: (user) => Effect.succeed(user),
})
// CORRECT - with getOrElse for defaults
const name = Option.getOrElse(maybeName, () => "匿名用户")
// CORRECT - Option.map for transformations
const upperName = Option.map(maybeName, (n) => n.toUpperCase())Effect Atom (Frontend State)
Effect Atom(前端状态管理)
Effect Atom provides reactive state management for React with Effect integration.
Effect Atom为React提供了集成Effect的响应式状态管理。
Basic Atoms
基础原子
typescript
import { Atom } from "@effect-atom/atom-react"
// Define atoms OUTSIDE components
const countAtom = Atom.make(0)
// Use keepAlive for global state that should persist
const userPrefsAtom = Atom.make({ theme: "dark" }).pipe(Atom.keepAlive)
// Atom families for per-entity state
const modalAtomFamily = Atom.family((type: string) =>
Atom.make({ isOpen: false }).pipe(Atom.keepAlive)
)typescript
import { Atom } from "@effect-atom/atom-react"
// Define atoms OUTSIDE components
const countAtom = Atom.make(0)
// Use keepAlive for global state that should persist
const userPrefsAtom = Atom.make({ theme: "dark" }).pipe(Atom.keepAlive)
// Atom families for per-entity state
const modalAtomFamily = Atom.family((type: string) =>
Atom.make({ isOpen: false }).pipe(Atom.keepAlive)
)React Integration
React集成
typescript
import { useAtomValue, useAtomSet, useAtom, useAtomMount } from "@effect-atom/atom-react"
function Counter() {
const count = useAtomValue(countAtom) // Read only
const setCount = useAtomSet(countAtom) // Write only
const [value, setValue] = useAtom(countAtom) // Read + write
return <button onClick={() => setCount((c) => c + 1)}>{count}</button>
}
// Mount side-effect atoms without reading value
function App() {
useAtomMount(keyboardShortcutsAtom)
return <>{children}</>
}typescript
import { useAtomValue, useAtomSet, useAtom, useAtomMount } from "@effect-atom/atom-react"
function Counter() {
const count = useAtomValue(countAtom) // 仅读取
const setCount = useAtomSet(countAtom) // 仅写入
const [value, setValue] = useAtom(countAtom) // 读取+写入
return <button onClick={() => setCount((c) => c + 1)}>{count}</button>
}
// 挂载副作用原子而不读取值
function App() {
useAtomMount(keyboardShortcutsAtom)
return <>{children}</>
}Handling Results with Result.builder
使用Result.builder处理结果
Use for rendering effectful atom results. It provides chainable error handling with :
Result.builderonErrorTagtypescript
import { Result } from "@effect-atom/atom-react"
function UserProfile() {
const userResult = useAtomValue(userAtom) // Result<User, Error>
return Result.builder(userResult)
.onInitial(() => <div>Loading...</div>)
.onErrorTag("NotFoundError", () => <div>User not found</div>)
.onError((error) => <div>Error: {error.message}</div>)
.onSuccess((user) => <div>Hello, {user.name}</div>)
.render()
}**使用**渲染有副作用的原子结果。它通过提供可链式调用的错误处理:
Result.builderonErrorTagtypescript
import { Result } from "@effect-atom/atom-react"
function UserProfile() {
const userResult = useAtomValue(userAtom) // Result<User, Error>
return Result.builder(userResult)
.onInitial(() => <div>加载中...</div>)
.onErrorTag("NotFoundError", () => <div>用户未找到</div>)
.onError((error) => <div>错误:{error.message}</div>)
.onSuccess((user) => <div>你好,{user.name}</div>)
.render()
}Atoms with Side Effects
带副作用的原子
typescript
const scrollYAtom = Atom.make((get) => {
const onScroll = () => get.setSelf(window.scrollY)
window.addEventListener("scroll", onScroll)
get.addFinalizer(() => window.removeEventListener("scroll", onScroll)) // REQUIRED
return window.scrollY
}).pipe(Atom.keepAlive)See for complete patterns including families, localStorage, and anti-patterns.
references/effect-atom-patterns.mdtypescript
const scrollYAtom = Atom.make((get) => {
const onScroll = () => get.setSelf(window.scrollY)
window.addEventListener("scroll", onScroll)
get.addFinalizer(() => window.removeEventListener("scroll", onScroll)) // 必须添加
return window.scrollY
}).pipe(Atom.keepAlive)完整模式包括原子家族、localStorage和反模式,请参考。
references/effect-atom-patterns.mdRPC & Cluster Patterns
RPC与集群模式
For RPC contracts and cluster workflows, see:
- - RpcGroup, Workflow.make, Activity patterns
references/rpc-cluster-patterns.md
关于RPC契约和集群工作流,请参考:
- - RpcGroup、Workflow.make、Activity模式
references/rpc-cluster-patterns.md
Anti-Patterns (Forbidden)
反模式(禁止使用)
These patterns are never acceptable:
typescript
// FORBIDDEN - runSync/runPromise inside services
const result = Effect.runSync(someEffect) // Never do this
// FORBIDDEN - throw inside Effect.gen
yield* Effect.gen(function* () {
if (bad) throw new Error("No!") // Use Effect.fail instead
})
// FORBIDDEN - catchAll losing type info
yield* effect.pipe(Effect.catchAll(() => Effect.fail(new GenericError())))
// FORBIDDEN - console.log
console.log("debug") // Use Effect.log
// FORBIDDEN - process.env directly
const key = process.env.API_KEY // Use Config.string("API_KEY")
// FORBIDDEN - null/undefined in domain types
type User = { name: string | null } // Use Option<string>See for the complete list with rationale.
references/anti-patterns.md这些模式绝对不允许:
typescript
// FORBIDDEN - runSync/runPromise inside services
const result = Effect.runSync(someEffect) // 绝对不要这样做
// FORBIDDEN - throw inside Effect.gen
yield* Effect.gen(function* () {
if (bad) throw new Error("错误!") // 请使用Effect.fail替代
})
// FORBIDDEN - catchAll losing type info
yield* effect.pipe(Effect.catchAll(() => Effect.fail(new GenericError())))
// FORBIDDEN - console.log
console.log("调试信息") // 请使用Effect.log
// FORBIDDEN - process.env directly
const key = process.env.API_KEY // 请使用Config.string("API_KEY")
// FORBIDDEN - null/undefined in domain types
type User = { name: string | null } // 请使用Option<string>完整的禁止模式列表及原因请参考。
references/anti-patterns.mdObservability
可观测性
typescript
// Structured logging
yield* Effect.log("Processing order", { orderId, userId, amount })
// Metrics
const orderCounter = Metric.counter("orders_processed")
yield* Metric.increment(orderCounter)
// Config with validation
const config = Config.all({
port: Config.integer("PORT").pipe(Config.withDefault(3000)),
apiKey: Config.secret("API_KEY"),
maxRetries: Config.integer("MAX_RETRIES").pipe(
Config.validate({ message: "Must be positive", validation: (n) => n > 0 })
),
})See for metrics and tracing patterns.
references/observability-patterns.mdtypescript
// 结构化日志
yield* Effect.log("处理订单", { orderId, userId, amount })
// 指标
const orderCounter = Metric.counter("orders_processed")
yield* Metric.increment(orderCounter)
// 带验证的配置
const config = Config.all({
port: Config.integer("PORT").pipe(Config.withDefault(3000)),
apiKey: Config.secret("API_KEY"),
maxRetries: Config.integer("MAX_RETRIES").pipe(
Config.validate({ message: "必须为正数", validation: (n) => n > 0 })
),
})指标和追踪模式请参考。
references/observability-patterns.mdReference Files
参考文件
For detailed patterns, consult these reference files in the directory:
references/- - Service definition, Effect.fn, Context.Tag exceptions
service-patterns.md - - Schema.TaggedError, error remapping, retry patterns
error-patterns.md - - Branded types, transforms, Schema.Class
schema-patterns.md - - Dependency composition, testing layers
layer-patterns.md - - RpcGroup, Workflow, Activity patterns
rpc-cluster-patterns.md - - Atom, families, React hooks, Result handling
effect-atom-patterns.md - - Complete list of forbidden patterns
anti-patterns.md - - Logging, metrics, config patterns
observability-patterns.md
如需详细模式,请查阅目录下的以下参考文件:
references/- - 服务定义、Effect.fn、Context.Tag例外情况
service-patterns.md - - Schema.TaggedError、错误重映射、重试模式
error-patterns.md - - 品牌类型、转换、Schema.Class
schema-patterns.md - - 依赖组合、测试层
layer-patterns.md - - RpcGroup、Workflow、Activity模式
rpc-cluster-patterns.md - - Atom、原子家族、React钩子、结果处理
effect-atom-patterns.md - - 完整的禁止模式列表
anti-patterns.md - - 日志、指标、配置模式
observability-patterns.md