Core Concepts
Three concepts, two namespaces.
Tag
A typed key. Its nominal identity (the class + a literal Id) is what appears in the requirement union R; the second type parameter is the service shape. Two structurally identical services never collide.
Define a service by inlining its shape — the class is the tag:
import { Tag } from "demesne";
class Logger extends Tag("Logger")<Logger, {
readonly log: (msg: string) => void;
}>() {}The identifier now names the tag (its nominal identity in R), not the service shape — the tag type is deliberately distinct so two structurally identical services never collide. When a signature needs the shape by name (a constructor parameter, a port type), recover it with the exported ServiceOf helper — it accepts the tag instance type or typeof tag:
import { type ServiceOf } from "demesne";
type LoggerService = ServiceOf<Logger>; // { readonly log: (msg: string) => void }Domain entities stay named
A Tag is for a service (a port). A domain entity like Order is just a type — don't wrap it in a tag.
Context
An immutable map from tag to service. get only accepts a tag whose identity is in R (reading an absent service is a compile error). It is contravariant in R: a Context<A | B> works wherever a Context<A> is expected — a consumer asking for fewer services accepts a richer context.
declare const ctx: Context<Logger>;
ctx.get(Logger); // ok
// ctx.get(Database) // compile error — Database is not in RLayer
A recipe that builds the services in Provides, possibly requiring Needs and possibly failing with E:
Layer<Provides, E, Needs>;Both Needs and E accumulate as unions: Layer.merge widens them, Layer.provideTo subtracts from Needs. You can Layer.build only once Needs is never. See Layers & Wiring.
The two namespaces
Operations are grouped so call sites read unambiguously:
Layer.*— constructors (value/factory/make), combinators (merge/provideTo), and the terminalbuild.Context.*—empty(reading a service is the instance methodctx.get(tag)).
Context and Layer are each both a type and a value — Context<R> / Context.empty(), Layer<P, E, N> / Layer.make(...). Tag stays top-level.