Skip to content

Data Model

Both DPGs use PostgreSQL as their system of record (via Drizzle ORM) and Redis for caching, sessions and queues. They can run on the same PostgreSQL and Redis servers, but each DPG owns its own database and they do not share tables.

The Signals database is organised around the item/action/event primitives:

EntityWhat it holds
ItemsVersioned, schema-typed records keyed by item_type (e.g. profile_1.0). Item tables are partitioned so that high-volume reads can prune to relevant partitions.
Schema registryThe schemas that define domains (the code calls a domain a network) and item types. Schemas are fetched and cached at the inter-instance read layer.
Actions & eventsInteractions between items and their structured results.
OrganisationsCarries a type column (network_service | aggregator | voice) that gates what each org may do (for example, only network_service orgs may upsert aggregators).

How the item/action/event primitives relate:

An item-type schema types many items (1—n); items interact via actions (1—n); each action produces events as structured results (1—n)

Always query through the partition-aware helpers in @dpg/database; a query against the parent table without a partition key scans all partitions.

Schema files live in apps/api/db/postgres/schema/. Migrations are generated, never hand-edited — regenerate with pnpm db:generate:api. A bundled schema.sql is produced and verified with pnpm schema:bundle / pnpm schema:bundle:check.

The Aggregator database (Drizzle, owned by apps/api) holds:

EntityWhat it holds
AggregatorsRegistration state, approval status, profile data.
ParticipantsOnboarded individuals/enterprises destined to become Signals items.
Bulk jobsUpload batches and per-row processing state for the worker.
Registration links & metricsShareable links and their roll-ups.

db-schema is the single source of truth; apps import table definitions from it. Migrations are generated with pnpm --filter @aggregator-dpg/api db:generate and applied with db:migrate.

  • Schema-typed, never freeform. An item’s structure is governed by its item_type schema; clients cannot invent fields.
  • Backend-owned identity fields. item_instance_url and item_schema_url are generated server-side.
  • Configuration as code. No domain- or environment-specific value is hardcoded. Values load once at startup from config loaders or environment variables, and each domain’s schemas live in their own folder (see Configuration).