Composition / @fjell/registry

Why does the registry exist?

Because an application has more than one Item instance. The registry gives Fjell a shared place to name, create, register, and find the right User, Address, Company, Team, and Membership services by coordinate and scope.

It is the framework's directory.

Imagine a large application with separate operations for Users, Companies, Addresses, Teams, and Team memberships—and sometimes multiple implementations of the same coordinate for different scopes or databases. Passing every instance manually through every constructor becomes fragile. The registry centralizes discovery.

Register

Make an instance available

A library instance is registered under its key-type path, such as ["user"] or ["address","user"], plus optional scopes.

Get

Find the matching instance

Callers ask for the coordinate and scope they need. The registry returns the matching instance or a clear miss.

Coordinate

Resolve the right level

User and Company are primary coordinates. Address and Membership coordinates include their parent type, preserving the graph's hierarchy.

Composition

Connect without hard-wiring

Lib, routers, and adapters can discover neighbors through the registry instead of importing concrete implementations everywhere.

What it looks up.

User
[user]
PItem operations
Company
[company]
PItem operations
Address
[address,user]
CItem operations
Membership
[membership,team]
role-aware child
The registry does not store Alex's data. It stores the services/instances that know how to operate on each Item coordinate.

Why not just import everything?

Direct imports couple the HTTP layer to a specific storage implementation, make alternate scopes awkward, and make circular dependencies more likely. A registry provides an explicit composition boundary. It also gives Fjell one place to instrument lookups and understand which coordinate is being requested.

const registry = createRegistry("application");

const users = registry.createInstance(
  ["user"],
  userCoordinate,
  userFactory,
  { scopes: ["postgres"] }
);

const addresses = registry.createInstance(
  ["address", "user"],
  addressCoordinate,
  addressFactory,
  { scopes: ["postgres"] }
);

const userService = registry.get(["user"], { scopes: ["postgres"] });

The registry's place in the story.

Core defines coordinates and operation contracts. The registry composes instances built on those contracts. Lib uses that composition point to build domain libraries, and routers/adapters consume the resulting instances.

Previous: core →Next: lib →Return to the graph →