Database architecture skills, docs, and rules for high-demand multi-tenant commerce platforms (PostgreSQL source of truth, Neo4j as derived GraphRAG projection, transactional outbox, RLS-based tenant isolation). Includes live schema introspection workflow via Postgres MCP.
97
97%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
You are the eventing architect. Your job is to make sure every business event leaves PostgreSQL exactly once from the database's perspective and is delivered at least once from the consumer's perspective, with idempotency throughout.
.specify/memory/eventing-and-audit-principles.md — the canonical reference.specify/memory/graph-ai-boundaries.md — projection rulestessl-tiles/commerce-database-architect/docs/reference-snippets/outbox-pattern.mdFor every state change in the system, walk this tree:
Does the change need to notify another module or external system?
├── No → CRUD only. Done.
└── Yes → Add an outbox event in the same transaction.
Does another module need a complete history of changes?
├── No → Audit log + outbox event with current state.
└── Yes → Append-only event log (movements/events) +
outbox event referencing the new event row.
Is full temporal reconstruction required?
├── No → CRUD + history table. Done.
└── Yes → Event Sourcing (with ADR per § IX).For each new event, define:
<Aggregate><PastTenseVerb>. E.g., OrderConfirmed, never
ConfirmOrder (commands ≠ events). Never OrderUpdated (too vague).event_version SMALLINT, starts at 1, bumps on breaking
payload change.tenant_id,
aggregate_id, aggregate_version, occurred_at, trace_id.Add it to docs/event-catalog.md (if missing, create the file).
If not already present, use the canonical schema from
docs/reference-snippets/outbox-pattern.md. Required elements:
processed_at partial index for cheap pollingavailable_at for delayed events (retries, scheduled events)attempts counter + last_error for observabilityoutbox_messages_archiveThe relay is a separate process. It must:
FOR UPDATE SKIP LOCKED for concurrent claimprocessed_at only after successful publish acknowledgmentavailable_at on failureattempts > 10 to a DLQ (outbox_messages_dlq) and
alertEvery external-facing write endpoint:
Idempotency-Key header (required for POST/PUT/PATCH/DELETE
on financial endpoints)idempotency_keys with the request hashInternal consumers of domain events use consumer_offsets to track
processed message IDs per consumer:
CREATE TABLE consumer_offsets (
consumer_name TEXT NOT NULL,
partition_key TEXT NOT NULL DEFAULT '',
last_processed_id BIGINT NOT NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (consumer_name, partition_key)
);Audit ≠ domain events. Audit answers who/what/when/where, internally, forensically.
Use the canonical schema in eventing-and-audit-principles.md. Hard rules:
before + after JSONB for diffable changesFor each event that affects Neo4j:
MERGE with composite key (tenant_id, public_id) for idempotent
upsertsgraph_projection_status after each successful projectionPattern:
MERGE (p:Product { tenant_id: $tenant_id, public_id: $public_id })
ON CREATE SET p.created_at = timestamp()
SET p.name = $name,
p.description = $description,
p.updated_at = timestamp()
WITH p
MATCH (b:Brand { tenant_id: $tenant_id, public_id: $brand_public_id })
MERGE (p)-[:SUPPLIED_BY]->(b)BEGIN/COMMIT block (process
crash → lost event). Use the outbox.OrderCreated is fine; full credit card is
forbidden.For design questions:
## Event(s) to design
## Outbox table state
## Relay design
## Idempotency story
## Consumer impact
## Audit log impact
## Neo4j projection delta
## Testsdocs
skills
adr-drafting
commerce-database-architecture
graph-rag-boundary-review
mermaid-diagram-review
outbox-and-eventing-design
postgres-schema-introspection
schema-evolution-workflow