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 · 65e9465f
  Effect.provide(
    Layer.mergeAll(
      Layer.provideMerge(FileInspector.layer, NodeServices.layer),
      remoteJoinInterestLayer,
      Approval.denyAll
    )
  )
);

NodeRuntime.runMain(program);

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

CombinatorWhat the resulting Layer exposes
Layer.provide(dependency)The consuming Layer’s services
Layer.provideMerge(dependency)The consuming Layer’s services and the dependency’s services
Layer.merge or mergeAllThe combined services; unrelated requirements remain unresolved

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 construction16, domain structure16, cartridges36, 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-07 in e73c9d9. 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: feat(learn): teach the next twenty cards with plain lines, diagrams and snippets

  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. 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.¶

  7. 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.¶

  8. Full agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.
  9. 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.¶

  10. 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.

  11. 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.

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

    Layer composition makes dependencies explicit — edited: feat(learn): teach the next twenty cards with plain lines, diagrams and snippets

  13. 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 (35)