ConceptsModule, edge & surface topology

Module, edge & surface topology

Flightdeck is one deployable application composed from three kinds of directory, each with a different boundary rule.

app/ — the composition root

app/src/modules.ts is the single source of what runs: an explicit, reviewed registry of every module. If a module isn’t in that file, it does not run — the conventions checker cross-validates the registry against the modules/ directory and fails on orphans in either direction. This is a deliberate defense against “built but not wired,” the defect class this repo is designed against.

app/src/main.ts is the composition root’s entry point: it wires the in-process event dispatcher, boots every module, collects each module’s maintenance sweeps into one namespaced list, and fails startup loudly on any missing required configuration — there is no empty-string secret default.

modules/ — the units of ownership

Each modules/<name>/ owns exactly one Postgres schema, exposes an in-process interface plus HTTP routes, and publishes/consumes events through the in-process dispatcher. A module never imports another module’s internals — only its published interface (modules/<name>/src/index.ts) and the shared @dscodotco/contracts package. This is enforced mechanically by the module-boundaries conventions rule.

See Reference -> Modules for the full list with a one-line purpose each.

edges/ — the only true network boundaries

Exactly two directories are allowed to speak to the outside network at the transport layer:

  • edges/webhooks-gateway/ — the universal inbound front door for every third-party callback. It verifies the provider signature, dedups on (provider, provider_event_id), stores the raw payload, and publishes exactly one internal event. There is no second receiver for a provider the gateway already ingests.
  • edges/outbound-webhooks/ — merchant deliveries and federation: the dispatch ledger, retry/DLQ/replay.

EventBridge and SQS exist only here. Everywhere else, an “event” is an in-process dispatch — see Event model.

surfaces/ — where a person or a request actually lands

Surfaces are Next.js apps (or similarly deployable frontends) that consume modules through in-process route mounting or HTTP, depending on the surface’s own architecture:

  • surfaces/storefront/ — the multi-tenant fleet renderer: one deployment serving every hosted store, Host -> tenant resolution, theme tokens and a typed section registry — no merchant code, no template engine. All storefront customization is data, never merchant code.
  • surfaces/portal/ — the merchant-facing portal.
  • surfaces/docs/ — this site: the public documentation surface.

Extraction is earned, not assumed

A module may eventually become its own deployed service, but only behind a written brief with load/isolation evidence — nobody extracts speculatively. Until that brief exists, “the module boundary” is an in-process contract enforced by lint rules, not a network call.