Layer values that satisfy the port contracts defined in the service layer. Two full stacks are available: lightweight in-memory implementations used in most tests, and SQLite-backed SQL implementations for production and contract tests. Because both stacks implement the same port interfaces, swapping between them requires changing a single Layer at the composition root.
In-Memory Adapters
The in-memory adapters use plain JavaScriptMap objects and synchronous Effect.sync / Effect.succeed calls. They have no setup cost, need no migration, and can be constructed fresh for each test.
SQL Adapters
The SQL adapters use Effect’sunstable/sql module with an SQLite backend (@effect/sql-sqlite-bun for local runs, @effect/sql-d1 for Cloudflare Workers). They wrap all SQL errors in PersistenceError before returning, so use cases see a uniform error type regardless of the underlying driver.
PersistenceError.refail is a helper on the PersistenceError class that catches any failure or defect from a sub-effect and wraps it in a PersistenceError with a descriptive message. This prevents low-level SQL errors from leaking into the service layer’s error channel.Layer Pattern: Wiring Adapters to Ports
Each adapter file exports aLayer value typed as Layer.Layer<PortClass, never, Dependencies>. The service layer’s CoffeeOrderApp.layer requires MenuRepository, OrderRepository, and OrderIdGenerator — all three must be provided before the application can start.
live.ts: The Composition Root
backend/src/external/live.ts is the single place where individual adapter layers are merged into named stacks.
Both
InMemoryCoffeeAppLive and SqlCoffeeAppLive reuse InMemoryOrderIdGeneratorLive. The SQL stack does not have a database-backed ID generator — the in-memory counter is sufficient because IDs are monotonically increasing strings, not database sequences.Swapping Adapter Stacks
Tests
Use
InMemoryCoffeeAppLive. Provide it alongside CoffeeOrderApp.layer in the Effect test setup. Each test gets a fresh layer with an empty Map.Local HTTP server
The presentation layer’s
main.ts provides either InMemoryCoffeeAppLive or SqlCoffeeAppLive depending on a runtime flag or environment variable.