A service captures its construction dependencies

A service is a named interface. A Layer describes how to build a service and supply its dependencies. A context holds supplied service implementations.

Build a service once with the dependencies its implementation needs. Return methods that use those captured values. Callers then require only the service.

Name the job before the implementation

Context.Service names a service and describes its interface. Effect.Effect<FileStats, FileStatsError> declares the method’s success and expected failure36. Its omitted requirement parameter is never.

packages/core/src/file-inspector.ts · 65e9465f
export class FileInspector extends Context.Service<
  FileInspector,
  {
    readonly inspect: (
      path: string
    ) => Effect.Effect<FileStats, FileStatsError>;
  }
>()("@rat-stack/core/FileInspector", {
  make: Effect.gen(function* makeFileInspector() {
    const fileSystem = yield* FileSystem.FileSystem;

make describes service construction as an Effect. Layer.effect runs that construction when the service graph is built.

Compose work inside the method

Use Effect.gen for inline composition. Use Effect.fn("name") for reusable functions that need a tracing boundary. Use Effect.fnUntraced when tracing is not useful. Do not create a function that only returns another Effect.gen wrapper.

packages/core/src/file-inspector.ts · 65e9465f
    const inspect = Effect.fn("FileInspector.inspect")(function* inspect(
      path: string
    ) {
      const content = yield* fileSystem
        .readFile(path)
        .pipe(
          Effect.mapError(
            (error) => new FileStatsError({ path, reason: error.message })
          )
        );

      return summarizeBytes(path, content);
    });

    return { inspect } as const;
  }),
}) {
  static readonly layer = Layer.effect(this, this.make);
}

inspect retains the filesystem value captured during construction. It keeps read failures typed and returns a domain result.

The service method composes Effects. It does not run a separate runtime that executes the work immediately inside the computation.

Construction dependencies are not request identity

Methods can retain contextual requirements when the job needs them. A filesystem implementation usually belongs to construction. A current person may belong to a single request.

Pass request data explicitly, or keep that requirement visible at the request boundary. Do not capture one request’s identity in a service shared by later requests.

Issue #24 records the risk of treating dependency-free methods as universal law.

Common wrong approach

Resolving the filesystem inside every inspect call leaves FileSystem in that method’s requirements. The caller must now know the implementation dependency. Capturing it during construction removes that requirement from the method.

Rebuilding the service inside every call also loses the intended construction lifetime. Supply its Layer at the composition root13, the startup code that chooses and supplies implementations.

Effect idiom and house rule

See also: domain structure16, Layer composition13, the Layer constructor pattern12, and testing through services22.

Read why dependency injection matters14.

Sources

  1. Effect contributors. 2026. Context. Effect 4.0.0.. Effect-TS. Context.Service supports a typed interface and an effectful make operation. Accessed 2026-10-06.
  2. Effect contributors. 2026. Composing services with the Layer module.. Effect-TS. Construction captures dependencies before returning Effect.fn methods. Accessed 2026-10-06.
  3. Effect contributors. 2026. Effect. Effect 4.0.0.. Effect-TS. Effect.gen composes inline work; fn names traced functions; fnUntraced avoids tracing. Accessed 2026-10-06.
  4. rat-stack contributors. 2026. FileInspector.. rat-stack. The service captures FileSystem in make and exposes an inspect method without filesystem requirements. Accessed 2026-10-06.
  5. Langton, Kit. 2026. Services & Layers. Effect Solutions.. Kit Langton. Topic framing for service interfaces, dependency capture, and replaceable implementations; examples checked against Effect 4.0.0. Accessed 2026-10-06.
  6. Effect Solutions contributors. 2025. Clarify that there could be exceptions on methods having dependencies.. Effect Solutions. Reader concern about request-time context and universal dependency-free method advice. Accessed 2026-10-06.

Last changed 2026-10-07 in 989f258. Source on GitHub. Change log.

Linked from

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

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

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

    A service captures its construction dependencies — edited: fix(lore): re-pin the CLI failure excerpt and link glossary first uses

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

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

    Read services that capture dependencies.¶

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

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

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

    Handlers request the Flags service from core.

  8. Full agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.
  9. Layer composition makes dependencies explicit. Supply a service's dependencies, choose which services remain visible, and reuse Layer identity within a build.

    See also: service construction, domain structure, cartridges, and testing.¶

  10. Learn rat-stack from your own agent. Turn on learn mode: your own agent explains rat-stack ideas during real work and keeps progress local.

    The first path starts with context and requirements, then how a service captures its dependencies.

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

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

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

  13. Run Effect at the integration boundary. Build the runtime where a calling framework enters, reuse its services, and dispose it when that integration ends.

    Read services before extracting the job from its host.

  14. Schemas define the boundary. Define accepted data with Schema, decode unknown input before dispatch, and encode results through the declared contract.

    See also: one schema, three surfaces, OpenAPI refusals, services, and testing.¶

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

    A service captures its construction dependencies — edited: fix(lore): re-pin the CLI failure excerpt and link glossary first uses

  16. 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 service construction for the full pattern.¶

Unlinked mentions (49)