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-Assertionheader; 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_refreshedtimestamp 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
denotesbetween 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)
Arcadia method
Section titled “Arcadia method”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:
| Viewpoint | This 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.