Skip to content
GroundworkGroundwork
Esc
↑↓navigate↵open⌘Jpreview
On this page

Services

A system's data and actions behind one Effect service, with a layer for each backend, run from any target.

A system’s data and actions sit behind one service. Its interface is what every target calls; its layer decides where the data comes from. Swapping the layer moves every target from fixtures to the real system at once.

The demo’s Items

@timmo001/groundwork-demo defines the service every demo runs on:

export class Items extends Context.Service<
  Items,
  {
    readonly list: () => Effect.Effect<readonly Item[], ItemsError>;
    readonly apply: (id: string) => Effect.Effect<Item, ItemsError>;
    readonly restart: () => Effect.Effect<void, ItemsError>;
  }
>()("groundwork-demo/Items") {
  static readonly fixture = Layer.sync(Items, () => /* in-memory items */);
}

Items.fixture keeps the items in memory and resets each time it’s built. A real tool replaces it with a layer that calls the system’s SDK, and nothing else changes.

Running it from a target

Target How it runs the service
CLI and MCP Each command and tool yield*s Items, with the layer provided once at the root
TUI A ManagedRuntime built from the layer, wrapped as promises for the views
Web A ManagedRuntime in the element, read with runPromiseExit so failures become messages
const runtime = ManagedRuntime.make(Items.fixture);
const exit = await runtime.runPromiseExit(
  Effect.flatMap(Items, (items) => items.list()),
);

Guidelines

  • One service per system, named for what it controls. Keep it small: the operations people and agents use.
  • Wrap each method in Effect.fn("Service.method") so traces name it.
  • Fail with a tagged error class carrying a message a person can read, never a thrown Error.
  • Fixtures live next to the service, so tests, demos and docs share them.
  • Build on Effect’s built-in services, such as ChildProcess, HttpClient or Socket, rather than wrapping Node APIs yourself.

The effect-service-design skill goes deeper on shaping a service and its layers. Where a system doesn’t use Effect, effect-principles keeps the same boundaries in plain code.

Last updated on