Layer composition makes dependencies explicit

A service is a named interface. A Layer describes how to build a service from other services. Choose and supply the services and their dependencies before running the application. Keep dependencies private unless callers need them too.

Supply dependencies without changing the domain

FileInspector.layer needs a filesystem implementation. NodeServices.layer supplies platform services. The CLI combines them before execution.

apps/cli/src/cli.ts · b4d6b8f4
  Effect.provide(
    Layer.mergeAll(
      Layer.provideMerge(FileInspector.layer, NodeServices.layer),
      remoteJoinInterestLayer,
      localPromptLibraryLayer.pipe(Layer.provide(NodeServices.layer)),
      localCafeDirectoryLayer.pipe(Layer.provide(NodeServices.layer)),
      localLearnLayer,
      Flags.defaults(eventFlags),
      Approval.denyAll
    )
  )
);

NodeRuntime.runMain(program);

Choose the combinator by the services the resulting Layer should expose:

Merging services does not, by itself, feed one Layer into another Layer’s requirements.

The test suite retains platform services with provideMerge because tests also create temporary files. Production callers that need only the inspector can keep its implementation dependency private with provide.

Reuse Layer identity when sharing construction

Sharing follows Layer identity within a build context that remembers which Layer values it has already constructed. Equivalent construction code does not make two Layer values identical. Sharing is limited to that construction context.

The fixture gives two consumers a resource. One case shares a Layer value; the other calls the Layer factory again.

packages/core/test/layer-composition.test.ts · 72306d84
      const resource = Resource.layer();

      const graph = Layer.merge(
        ReadAccess.layer.pipe(Layer.provide(resource)),
        WriteAccess.layer.pipe(
          Layer.provide(shared ? resource : Resource.layer())
        )
      );

Resource.layer() creates a new Layer value. Reusing resource lets the two branches share construction.

The fixture counts constructions and compares service ids:

packages/core/test/layer-composition.test.ts · 72306d84
      const ids = yield* Effect.gen(function* readServiceIds() {
        return [yield* ReadAccess, yield* WriteAccess];
      }).pipe(Effect.provide(graph));

      const count = yield* ConstructionCount;

      expect(yield* Ref.get(count)).toBe(constructions);
      expect(ids[0] === ids[1]).toBe(shared);

Independent application runtimes do not share instances merely because they import the same Layer value. Their construction context and memo-map lifetime matter too.

Common wrong approach

Calling a Layer factory twice can create two pools, caches, or background workers when one shared instance was intended. Keep the constructed Layer value and provide that same value to both consumers.

Do not use provideMerge everywhere to silence missing-service errors. Read which services callers need. Retaining every dependency exposes implementation details and makes removal harder.

Effect idiom and house rule

See also: service construction26, domain structure17, cartridges38, and testing22.

Read why dependency injection matters14.

Sources

  1. Effect contributors. 2026. Layer. Effect 4.0.0.. Effect-TS. provide supplies dependencies; provideMerge retains their services; memoization uses Layer identity and a MemoMap. Accessed 2026-10-06.
  2. Effect contributors. 2026. Layer tests.. Effect-TS. Tests establish shared construction and resource lifetime within memoization contexts. Accessed 2026-10-06.
  3. rat-stack contributors. 2026. CLI composition root.. rat-stack. The CLI provides FileInspector and platform services before NodeRuntime.runMain. Accessed 2026-10-06.
  4. rat-stack contributors. 2026. Layer identity fixture.. rat-stack. A checked seam fixture counts one construction for shared identity and two for separate Layer instances. Accessed 2026-10-06.
  5. Langton, Kit. 2026. Services & Layers. Effect Solutions.. Kit Langton. Topic framing for provision and identity-based Layer sharing; rewritten around rat-stack's code. Accessed 2026-10-06.

Last changed 2026-10-24 in c525d23. Source on GitHub. Change log.

Linked from

  1. A service captures its construction dependencies. Build a service with its dependencies, then expose methods that callers can use without rebuilding the graph.

    Supply its Layer at the composition root, the startup code that chooses and supplies implementations.¶

  2. Agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.

    Layer composition makes dependencies explicit — Supply a service’s dependencies, choose which services remain visible, and reuse Layer identity within a build.

  3. Change log. What changed in the files served here, newest first.

    Layer composition makes dependencies explicit — edited: fix(site): replace published Markdown tables with lists and fence reader output

  4. Configuration is a supplied, validated dependency. Parse configuration when a Layer builds, reject invalid values, and supply controlled settings in tests.

    See also: services, Layer composition, failures, and tests.¶

  5. Dependency injection is the reason to choose Effect. Effect tracks service requirements in types and supplies replaceable implementations through Layers.

    Read explicit Layer composition.¶

  6. Durability. Seven durable Effect patterns, each with its seam, its fence, and the incident that taught it.

    Reference with a default, read at layer build, then Effect.timeoutOption.¶

  7. Durability starts with a recovery record. A reading order for storage, replay, worker ownership, overload, and recovery tests.

    Layers make dependencies explicit.¶

  8. Effect basics. An Effect describes work, expected failures, and required services before a runtime executes it.

    Read service construction, Layer composition, and context and requirements.¶

  9. Effect keeps the surfaces on one contract. Schema owns values, projections build interfaces, and Layers supply implementations.

    Start with Effect basics, then construct a service and compose its Layers.¶

  10. Feature flags. Typed feature flags with owners, removal dates, and stable subject rollouts.

    The operator layer adds the ledger DO and its KV write binding.¶

  11. Full agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.
  12. learn-rat-stack. Trace a capability through Effect, XState, five projections, the CLI, and Alchemy. Try hosted search and read.

    For implementation tasks, use services, Layer composition, and testing.¶

  13. Pure cores reduce timeline coupling. Move deterministic rules out of time-dependent actions and shared mutable state.

    Connect this to the existing rat-stack guidance.¶

  14. query-design. Design or review SQL reads, writes, scans, backfills, and slow database paths. Use before changing a store method or any loop that queries. Covers round-trip arithmetic, batching, keyset paging, index evidence, row budgets, PlanetScale Vitess limits, Hyperdrive connections, statement-count tests, and statement-template fences.

    A Layer builds services and supplies their construction dependencies.

  15. Rat Stack lore | rat-stack. Short, source-grounded notes on the ideas and decisions behind rat-stack.

    Layer composition makes dependencies explicit — Supply a service’s dependencies, choose which services remain visible, and reuse Layer identity within a build.

  16. Scopes release resources when work ends. Acquire resources lazily, keep their use inside the owner, and release them after success, failure, or interruption.

    Start with Layer composition when choosing the resource’s owner.

  17. Source change history (log.md). The generated change log as Markdown.

    Layer composition makes dependencies explicit — edited: fix(site): replace published Markdown tables with lists and fence reader output

  18. Structure an Effect codebase by domain. Start with folders for application work, services and Layers in the same file, and startup code that supplies implementations.

    See Layer composition.¶

Unlinked mentions (37)