layer-design
from front-depiction/claude-setup
Reusable Claude Code configuration for Effect TypeScript projects with specialized agents and skills
10 stars4 forksUpdated Jan 19, 2026
npx skills add https://github.com/front-depiction/claude-setup --skill layer-designSKILL.md
Layer Design Skill
Create layers that construct services while managing their dependencies cleanly.
Layer Structure
import { Layer } from "effect"
// Layer<RequirementsOut, Error, RequirementsIn>
// ▲ ▲ ▲
// │ │ └─ What this layer needs
// │ └─ Errors during construction
// └─ What this layer produces
Pattern: Simple Layer (No Dependencies)
import { Context, Effect, Layer } from "effect"
interface ConfigData {
readonly logLevel: string
readonly connection: string
}
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"
})
})
)
Pattern: Layer with Dependencies
import { Context, Effect, Layer, Console } from "effect"
interface ConfigData {
readonly logLevel: string
readonly connection: string
}
export class Config extends Context.Tag("Config")<
Config,
{
readonly getConfig: Effect.Effect<ConfigData>
}
>() {}
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
yield* Console.log(`[${logLevel}] ${message}`)
})
})
})
)
Pattern: Layer with Resource Management
Use Layer.scoped when resources need cleanup:
- Database connections
- File handles, network connections
- Any resource requiring
Effect.acquireReleaseoraddFinalizerfor cleanup
Use Layer.effect for stateless services without cleanup needs.
import { Context, Effect, Layer } from "effect"
interface ConfigData {
readonly logLevel: string
readonly connection: string
}
interface Connection {
readonly close: () => void
}
interface DatabaseError {
readonly _tag: "DatabaseError"
}
export class Config extends Context.Tag("Config")<
Config,
{
readonly getConfig: Effect.Effect<ConfigData>
}
>() {}
export class Database extends Context.Tag("Database")<
Database,
{
readonly query: (sql: string) => Effect.Effect<unknown, DatabaseError>
}
>() {}
declare const connectToDatabase: (config: ConfigData) => Effect.Effect<Connection, DatabaseError>
declare const executeQuery: (connection: Connection, sql: string) => Effect.Effect<unknown, DatabaseError>
// Layer<Database, DatabaseError, Config>
export const DatabaseLive = Layer.scoped(
Database,
Effect.gen(function* () {
const config = yield* Config
const configData = yield* config.getConfig
// Acquire resource with automatic release
const connection = yield* Effect.acquireRelease(
connectToDatabase(configData),
(conn) => Effect.sync(() => conn.close()) // Cleanup
)
return Database.of({
query: (sql) => executeQuery(connection, sql)
})
})
)
Composing Layers: Merge vs Provide
Merge (Parallel Composition)
Combine independent layers:
import { Context, Layer } from "effect"
declare class Config extends Context.Tag("Config")<Config, {}> {}
declare class Logger extends Context.Tag("Logger")<Logger, {}> {}
declare const ConfigLive: Layer.Layer<Config, never, never>
declare const LoggerLive: Layer.Layer<Logger, never, Config>
// Layer<Config | Logger, never, Config>
// ▲ ▲ ▲
// │ │ └─ LoggerLive needs Config
// │ └─ No errors
// └─ Produces both Config and Logger
const AppConfigLive = Layer.merge(ConfigLive, LoggerLive)
Result combines:
- Requirements: Union (
never | Config = Config) - Outputs: Union (
Config | Logger)
Provide (Sequential Composition)
Chain dependent layers:
import { Context, Layer } from "effect"
declare class Config extends Context.Tag("Config")<Config, {}> {}
declare class Logger extends Context.Tag("Logger")<Logger, {}> {}
declare const ConfigLive: Layer.Layer<Config, never, never>
declare const LoggerLive: Layer.Layer<Logger, never, Config>
// Layer<Logger, never, never>
// ▲ ▲ ▲
// │ │ └─ ConfigLive satisfies LoggerLive's requirement
// │ └─ No errors
// └─ Only Logger in o
...
Repository Stats
Stars10
Forks4