ConceptsService architecture

Service Architecture

The Loop Platform is a collection of independently deployable services that share a common foundation. Each service owns a bounded context, a Postgres schema, and a set of published events.

Principles

  1. Service autonomy — each service owns its data and can deploy independently. No service reads or writes another service’s database schema.
  2. Contract-first — every service declares its API surface in openapi.yaml and its event contracts in service.yaml. CI enforces drift detection.
  3. Shared foundations, not shared code — common concerns (auth, observability, audit logging) live in @platform/* packages that services depend on. Services never import from each other.

Communication patterns

Synchronous (HTTP)

Services expose RESTful HTTP APIs via Hono. Calls between services use M2M tokens issued by the Identity service (see ADR-0018).

All inter-service HTTP calls go through the platform router, which handles TLS termination and subdomain routing (<service>.platform.loop.health).

Asynchronous (Events)

State changes are published as events to the platform event bus. Other services subscribe to the events they care about. This decouples producers from consumers and ensures eventual consistency across bounded contexts.

Events are defined in @platform/contracts and published via @platform/events.publishEvent(). Each event has a versioned schema (e.g., user.created.v1).

Service contract (service.yaml)

Every service declares a machine-readable contract at its root:

name: identity
display_name: "Identity"
owner: "@william"
team: platform
status: alpha
contracts:
  openapi: ./openapi.yaml
  events_published:
    - user.created.v1
  events_consumed: []
data:
  database: aurora
  schemas_owned: [identity]

CI validates these contracts against a JSON schema. The docs site auto-generates service detail pages from these files.

Deployment

Services deploy as Fargate containers (not Lambda) via SST. This allows persistent NATS connections, long-running background work, and consistent resource allocation. Each service gets its own ECS service definition, CloudWatch alarms, and subdomain.

Stages: devstaging (auto on merge to main) → prod (manual release).

Database

All services share an Aurora Serverless v2 Postgres cluster but own isolated schemas. Migrations use Drizzle Kit and are scoped per service. The ledger (Accounting) is append-only with immutability enforced by Postgres triggers.

Observability

Logs, traces, and metrics flow through OpenTelemetry to Axiom. Every service wires up OTel via @platform/observability. Audit logs are mandatory for all state-changing operations.