ワンクリックで
layer-design
Design and compose Effect layers for clean dependency management
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Design and compose Effect layers for clean dependency management
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Implement reactive state management with Effect Atom for React applications
Decide between Context Tag witness and capability patterns for dependency injection, understanding coupling trade-offs
Generate comprehensive predicates and orders for domain types using typeclass patterns
Implement Effect services as fine-grained capabilities avoiding monolithic designs
Implement typeclasses with curried signatures and dual APIs for both data-first and data-last usage
| name | layer-design |
| description | Design and compose Effect layers for clean dependency management |
Create layers that construct services while managing their dependencies cleanly.
Layer<RequirementsOut, Error, RequirementsIn>
▲ ▲ ▲
│ │ └─ What this layer needs
│ └─ Errors during construction
└─ What this layer produces
export class Config extends Context.Tag("Config")<
Config,
{
readonly getConfig: Effect.Effect<ConfigData>
}
>() {}
// Layer<Config, never, never>
// ▲ ▲ ▲
// │ │ └─ No dependencies
// │ └─ Cannot fail
// └─ Produces Config
export const ConfigLive = Layer.succeed(
Config,
Config.of({
getConfig: Effect.succeed({
logLevel: "INFO",
connection: "mysql://localhost/db"
})
})
)
export class Logger extends Context.Tag("Logger")<
Logger,
{ readonly log: (message: string) => Effect.Effect<void> }
>() {}
// Layer<Logger, never, Config>
// ▲ ▲ ▲
// │ │ └─ Needs Config
// │ └─ Cannot fail
// └─ Produces Logger
export const LoggerLive = Layer.effect(
Logger,
Effect.gen(function* () {
const config = yield* Config // Access dependency
return Logger.of({
log: (message) =>
Effect.gen(function* () {
const { logLevel } = yield* config.getConfig
console.log(`[${logLevel}] ${message}`)
})
})
})
)
Use Layer.scoped for resources that need cleanup:
// Layer<Database, DatabaseError, Config>
export const DatabaseLive = Layer.scoped(
Database,
Effect.gen(function* () {
const config = yield* Config
// Acquire resource with automatic release
const connection = yield* Effect.acquireRelease(
connectToDatabase(config),
(conn) => Effect.sync(() => conn.close()) // Cleanup
)
return Database.of({
query: (sql) => executeQuery(connection, sql)
})
})
)
Combine independent layers:
// Layer<Config | Logger, never, Config>
// ▲ ▲ ▲
// │ │ └─ LoggerLive needs Config
// │ └─ No errors
// └─ Produces both Config and Logger
const AppConfigLive = Layer.merge(ConfigLive, LoggerLive)
Result combines:
never | Config = Config)Config | Logger)Chain dependent layers:
// Layer<Logger, never, never>
// ▲ ▲ ▲
// │ │ └─ ConfigLive satisfies LoggerLive's requirement
// │ └─ No errors
// └─ Only Logger in output
const FullLoggerLive = Layer.provide(LoggerLive, ConfigLive)
Result:
never)Logger)Build applications in layers:
// Infrastructure: No dependencies
const InfrastructureLive = Layer.mergeAll(
ConfigLive, // Layer<Config, never, never>
DatabaseLive, // Layer<Database, never, Config>
CacheLive // Layer<Cache, never, Config>
).pipe(
Layer.provide(ConfigLive) // Satisfy Config requirement
)
// Domain: Depends on infrastructure
const DomainLive = Layer.mergeAll(
PaymentDomainLive, // Layer<PaymentDomain, never, Database>
OrderDomainLive, // Layer<OrderDomain, never, Database>
).pipe(
Layer.provide(InfrastructureLive)
)
// Application: Depends on domain
const ApplicationLive = Layer.mergeAll(
PaymentGatewayLive,
NotificationServiceLive
).pipe(
Layer.provide(DomainLive)
)
Switch implementations for different environments:
// Production
export const DatabaseLive = Layer.scoped(
Database,
Effect.gen(function* () {
const connection = yield* connectToProduction()
return createDatabaseService(connection)
})
)
// Test
export const DatabaseTest = Layer.succeed(
Database,
Database.of({
query: () => Effect.succeed({ rows: [] })
})
)
// Use in application
const program = myProgram.pipe(
Effect.provide(process.env.NODE_ENV === "test" ? DatabaseTest : DatabaseLive)
)
Layers are memoized - same instance shared across program:
// Config is constructed once and shared
const program = Effect.all([
Effect.gen(function* () {
const config = yield* Config
// Uses shared instance
}),
Effect.gen(function* () {
const config = yield* Config
// Same instance
})
]).pipe(Effect.provide(ConfigLive))
Handle construction errors:
export const DatabaseLive = Layer.effect(
Database,
Effect.gen(function* () {
const connection = yield* connectToDatabase().pipe(
Effect.catchTag("ConnectionError", (error) =>
Effect.fail(new DatabaseConstructionError({ cause: error }))
)
)
return createDatabaseService(connection)
})
)
*Live - Production implementation*Test - Test implementation*Mock - Mock for testingacquireRelease if neededLayers should make dependency management explicit while keeping service interfaces clean and focused.