SP slotplate
Concept

The composition root

One file that wires everything. Grepable. Replaceable.

src/composition.ts is the only file in the project that says new Foo() for a service. Every other file receives its dependencies.

The shape

export async function compose(host, hud) {
  const container = new Container();

  container.register('stores', () => new RootStore());
  container.register('ticker', () => new GsapTicker());
  container.register('network', () => new MockNetworkManager(...));
  // ...

  await scene.init(host);
  const fsm = new FSM({ stores, ticker, network, reels, hud });
  fsm.register(new IdlePhase());
  fsm.register(new SpinPhase());
  // ...

  return { start: () => fsm.transition('idle'), dispose };
}

All new calls happen here. Phase handlers, presenters, and stores receive what they need via constructor arguments or the FSM context — they never reach for a global.

Anti-patterns this kills

  • "Let me just import the singleton here." — No singleton exists.
  • "I'll new MyService() inside this helper." — The helper takes it as a parameter.
  • "The service locator will find it." — The locator is only populated here; elsewhere it's read-only.