Skip to content

Architecture Decision Records

Architecture Decision Records (ADRs) capture the “why” behind choices. This practice aligns with the Arcadia method’s emphasis on “capitalisation” — retaining decisions and justifications for long-term traceability.

For ontological modeling decisions, see the companion Ontological Decision Records.

ADR-001: Cloudflare Access as sole auth plane

Section titled “ADR-001: Cloudflare Access as sole auth plane”

Context: The current system has two incompatible auth systems — Passport.js sessions (volatile, in-memory) and Supabase Auth (JWT, frontend only). Staff access is internal-only.

Decision: Use Cloudflare Access with Google SSO as the single authentication layer.

Consequences:

  • No auth UI in the app — Access gate handles login before the app loads
  • Every request carries Cf-Access-Jwt-Assertion header; Worker middleware validates and extracts identity
  • No RBAC in v1 — all authenticated staff (CCO Persons bearing BFO Roles) have equal access (deferred per SOW)
  • Service-to-service calls (cron, queues) use Access service tokens

ADR-002: GraphQL-first Shopify integration

Section titled “ADR-002: GraphQL-first Shopify integration”

Context: Shopify states the REST Admin API is “legacy” as of October 2024 and recommends GraphQL. The current codebase uses REST in 6 independent implementations.

Decision: Use GraphQL Admin API as the primary integration, with bulk operations for high-volume data sync.

Consequences:

  • Bulk operations are async (poll-based), requiring queue orchestration
  • Concurrency limits apply: one bulk query per shop on API version 2024-04 (five on 2026-01+)
  • REST retained only for endpoints without GraphQL equivalents, isolated behind the shared Shopify client

ADR-003: No stored procedures or materialized views

Section titled “ADR-003: No stored procedures or materialized views”

Context: Current system uses PostgreSQL RPCs (get_tag_categories(), refresh_tag_performance_aggregate(), etc.) and materialized views. The rewrite moves all business logic to application code for portability and testability.

Decision: Move all business logic into application code. Replace materialized views with explicit performance_measurement_dataset table writes triggered by cron/queue jobs.

Consequences:

  • All RPC logic migrated to TypeScript in Workers
  • Mart tables are regular tables, rebuilt by background jobs (PKO ProcedureExecutions)
  • last_refreshed timestamp tracks freshness
  • More app code, but fully testable and portable
  • Cross-reference: this aligns with ODR-002 — ontological grounding is documentation, not runtime enforcement

ADR-004: Foreign keys as documentation, not enforcement

Section titled “ADR-004: Foreign keys as documentation, not enforcement”

Context: PlanetScale supports foreign keys but recommends carefully weighing trade-offs — constraints increase locking and can create friction in high-concurrency workloads and online schema changes.

Decision: Define relationships in Drizzle schema (for type safety and documentation) but do not enforce FK constraints at the database level.

Consequences:

  • Application code enforces referential integrity
  • Background jobs include periodic orphan detection
  • Schema changes are simpler and non-blocking
  • Risk: orphaned rows possible if app bugs bypass checks
  • Cross-reference: parallels ODR-002 — ontological relations (like IAO denotes between assets and products) are documented in the schema naming and this specification, not enforced by database constraints

ADR-005: Queue-per-concern with typed discriminator

Section titled “ADR-005: Queue-per-concern with typed discriminator”

Context: The system has 8+ distinct background job types. Options: one queue per job type, or a single queue with typed messages.

Decision: Single queue (ingest-queue) with job_type discriminated union. Each message includes a job_type field that routes to the appropriate handler.

Consequences:

  • Simpler infrastructure (one queue binding)
  • Type-safe dispatch via Zod discriminated union
  • All jobs share retry/DLQ configuration
  • Trade-off: noisy-neighbor risk if one job type floods the queue (mitigated by batch size config)
  • Job types use ontological naming: PROCEDURE_EXECUTION_INITIATE, ICE_INGEST, DOCUMENT_ARTIFACT_GENERATE, MEASUREMENT_DATASET_REFRESH, etc. (see logical architecture job contracts)

This specification adapts the Arcadia (ARChitecture Analysis and Design Integrated Approach) method — a model-based systems engineering approach that defines viewpoints flowing from operational need to build strategy:

ViewpointThis spec’s mapping
Operational Analysis (OA)Scenarios — stakeholders, workflows, operational goals
System Need Analysis (SA)System Need Analysis — system boundary, external interfaces, non-functional constraints
Logical Architecture (LA)Logical Architecture — tech-neutral modules and contracts
Physical Architecture (PA)Physical Architecture — CF Workers, PlanetScale, R2 bindings
Building Strategy (BS)Delivery Plan — phases, verification, cutover

Arcadia’s core value for this rewrite is the discipline of maintaining a shared architectural reference across engineering levels — ensuring that operational scenarios trace to logical components, which trace to physical infrastructure, which trace to verification artifacts.

The Ontological Foundation adds a complementary discipline: ensuring that every entity, relation, and process traces to formal ontological categories, preventing naming drift and design erosion over time.