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 -> tenantresolution, 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.