Architecture Decision Records (ADR)
1. Overview & Decision Framework
Architecture Decision Records (ADRs) capture critical structural decisions made throughout the evolution of the Debelu marketplace. Every record articulates the business context, engineering problem, evaluated alternatives, chosen decision, and resulting trade-offs.
All architectural modifications impacting data security, payment settlement, monorepo topology, or multi-cloud boundaries require a formal ADR signed off by engineering leadership.
ADR-001: npm Workspaces Monorepo with Turborepo Caching
- Status: Accepted
- Date: 2026-01-15
- Deciders: Engineering Lead, Tech Architecture Team
Context
Debelu comprises multiple customer-facing and internal applications (apps/storefront, debelu-marketing, debelu-backend, debelu-admin) that share common TypeScript types, financial schemas, UI component primitives, and utility services. Maintaining separate repositories led to severe schema desynchronization, duplicate validation logic, and slow cross-surface feature delivery.
Decision
Adopt an npm workspaces monorepo orchestrated with Turborepo (turbo.json) for pipeline execution and distributed task caching. Extract reusable logic into @debelu/core and design tokens into @debelu/ui.
Alternatives Considered
- Polyrepo (Multiple GitHub Repositories): High maintenance burden for shared types; required publishing private npm packages on every minor API tweak.
- Nx Monorepo: High configuration complexity and heavy abstraction layers compared to Turborepo's lightweight pipeline model.
- pnpm / Yarn v4 Workspaces: npm workspaces provided zero-friction compatibility with existing CI/CD runners without requiring specialized package manager bootstrapping.
Consequences
- Positive: Atomic multi-package pull requests; instant TypeScript type sharing across frontend and backend; cached CI test execution saving $> 65%$ build time.
- Negative: Monorepo repository size grows faster; requires disciplined root
package.jsondependency management to prevent version drift.
ADR-002: Supabase as Managed Auth, Database & Edge Layer
- Status: Accepted
- Date: 2026-01-20
- Deciders: Tech Architecture Team, Security Lead
Context
The platform requires a highly scalable, transactional relational database with native support for Row-Level Security (RLS), multi-factor authentication (MFA/TOTP), real-time change data capture, and low-latency serverless edge workers.
Decision
Use Supabase as the primary backend data and identity foundation:
- PostgreSQL 16: Enterprise relational database with ACID transactions, JSONB document querying, and row-level security.
- GoTrue Auth: Native multi-factor authentication (AAL1 password/magic link, AAL2 TOTP).
- Supabase Edge Runtime (Deno): Ultra-low-latency edge handlers for inbound payment webhooks.
Alternatives Considered
- Raw AWS RDS (Aurora PostgreSQL) + Auth0: Significantly higher operational and integration overhead; Auth0 licensing costs scale poorly for university student populations.
- Firebase / Firestore: Document-based NoSQL lacks ACID transactions necessary for multi-table double-entry escrow ledgers and strict check constraints.
Consequences
- Positive: Robust database-level authorization via Postgres RLS; simplified auth lifecycle; instant edge function scaling for webhooks.
- Negative: Vendor lock-in to Supabase extensions; requires careful connection pool tuning (PgBouncer) for persistent backend processes.
ADR-003: Persistent Express Backend on Railway (Not Serverless)
- Status: Accepted
- Date: 2026-02-01
- Deciders: Backend Platform Squad
Context
While serverless functions excel at stateless HTTP requests, Debelu requires long-lived stateful daemons: persistent BullMQ queue workers, Redis connection pooling, automated payout reconciliation crons, and streaming SSE connections for the Gemini AI assistant.
Decision
Deploy Express.js with TypeScript as a continuous Docker container service hosted on Railway, reserving serverless edge functions strictly for webhooks.
Alternatives Considered
- Pure Serverless (AWS Lambda / Vercel Serverless Functions): Serverless cold starts severely degrade real-time AI streaming and webhook timeouts; managing distributed background queue workers across serverless invocations is fragile and expensive.
- Kubernetes (EKS / GKE): Premature operational complexity for current scale; excessive DevOps maintenance overhead.
Consequences
- Positive: Predictable sub-millisecond local Redis connection reuse; stable background queue consumers; zero cold starts on API routes; native SSE streaming support.
- Negative: Requires ongoing container resource monitoring (CPU/RAM sizing); manual scaling configuration compared to automatic serverless concurrency.
ADR-004: Paystack as Primary Payment Gateway & Escrow Settlement Partner
- Status: Accepted
- Date: 2026-02-10
- Deciders: Executive Team, Finance Lead, Engineering Lead
Context
Debelu operates within Nigerian tertiary educational institutions, requiring high-success payment channels: Naira-denominated Mastercard/Visa/Verve cards, USSD, bank account transfers, and automated interbank payouts (NIP) to 25+ Nigerian financial institutions.
Decision
Integrate Paystack as the exclusive payment processor for customer checkout captures and automated vendor payouts. All funds settle into Debelu's closed-loop platform escrow balance before automated release.
Alternatives Considered
- Flutterwave: Experienced intermittent API availability and settlement latency spikes during testing in campus regions.
- Monnify: Strong virtual account infrastructure, but lacks Paystack's mature developer tooling, rich webhook reliability, and automated recipient transfer APIs.
Consequences
- Positive: Market-leading authorization success rates on Nigerian student bank cards; native NUBAN bank account verification; robust transfer recipient APIs for vendor payouts.
- Negative: Single point of failure for payment processing; requires proactive circuit breakers and transaction queue catch-up tooling during upstream Paystack incidents.
ADR-005: 4-Digit Delivery PIN Confirmation for Hyperlocal Commerce
- Status: Accepted
- Date: 2026-02-18
- Deciders: Product Lead, Operations Lead, Security Squad
Context
Campus commerce is inherently hyperlocal: 95% of orders are fulfilled in person via handoffs at hostel gates, faculty halls, or campus transit hubs. Traditional courier tracking (waybills, GPS tracking) is completely inapplicable to student peer-to-peer delivery.
Decision
Implement a cryptographic 4-digit Delivery PIN protocol:
- At checkout, the platform generates an unpredictable 4-digit PIN stored in
orders.delivery_pin. - The PIN is visible only to the buyer in their active order view.
- Upon physical handoff and inspection, the buyer gives the PIN to the vendor.
- The vendor submits the PIN via mobile app (
POST /api/orders/:id/verify-delivery). - Matching of the PIN releases escrow funds instantly from escrow hold to the vendor's wallet balance.
Alternatives Considered
- QR Code Scanning: Required working cameras and high screen brightness; failed under direct Nigerian sunlight or on damaged student smartphone screens.
- Buyer Self-Confirmation Button: Buyers frequently forgot to tap "Confirm Received" after taking physical possession, leaving vendor capital unfairly trapped in escrow for days.
Consequences
- Positive: Zero hardware requirements; works offline or over verbal communication; mathematically eliminates disputes regarding whether physical handoff occurred.
- Negative: Requires buyer and vendor awareness during handoff; requires brute-force rate-limiting on vendor PIN submission (locked after 3 failed attempts).
ADR-006: Capacitor for Mobile Hybrid Packaging (Not React Native)
- Status: Accepted
- Date: 2026-03-01
- Deciders: Mobile Squad, Frontend Architecture
Context
Debelu needs native Android and iOS mobile app presence to leverage push notifications, biometric auth, and offline caching, with a lean engineering squad.
Decision
Package the flagship React 19 / Vite SPA (apps/storefront) using Capacitor 8.5 (@capacitor/core, @capacitor/android, @capacitor/ios) under application ID com.debelu.app.
Alternatives Considered
- React Native / Expo: Would require maintaining two separate UI component codebases (web DOM vs native components), doubling design system overhead for a small team.
- Flutter: Complete language and framework fork (Dart); incapable of sharing the existing
@debelu/uicomponent library.
Consequences
- Positive: 98% code reuse between web and mobile; instant feature parity across web and app stores; seamless native plugin access (FCM push tokens, Haptics, Device info).
- Negative: Slightly higher initial cold-start webview rendering latency compared to raw native views; requires careful touch event and viewport tuning.
ADR-007: Google Gemini 1.5 Flash for Nduzi AI Assistant
- Status: Accepted
- Date: 2026-03-15
- Deciders: AI Squad, Product Lead
Context
Students require a natural language shopping assistant ("Nduzi") capable of finding campus products, negotiating deals, checking order statuses, and explaining return policies within tight sub-second latency budgets.
Decision
Select Google Gemini 1.5 Flash (gemini-1.5-flash) via the official @google/genai SDK with custom backend tool-calling orchestration and Server-Sent Events (SSE) streaming.
Alternatives Considered
- OpenAI GPT-4o-mini: Higher inference cost per million tokens; slower streaming TTFB in West African regions compared to Google Cloud edge POPs.
- Self-Hosted Llama-3-8B: Unacceptable GPU infrastructure hosting costs and operational maintenance overhead.
Consequences
- Positive: Blazing fast TTFB ($< 350\text{ ms}$); massive 1M token context window allows rich campus context and product catalogs; native function calling for deterministic DB queries.
- Negative: Dependent on Google Cloud API uptime and regional quota limits; requires strict prompt guardrails against prompt injection and jailbreaks.
ADR-008: Multi-Cloud Edge Hosting Topology
- Status: Accepted
- Date: 2026-03-20
- Deciders: Infrastructure Squad, Security Lead
Context
Different monorepo surfaces have distinct architectural and rendering requirements that cannot be optimally served by a single cloud hosting provider.
Decision
Implement a specialized multi-cloud hosting strategy:
- Cloudflare Pages: Hosts static React SPAs (
storefront,admin) with global edge asset caching and zero egress charges. - Vercel: Hosts the Next.js marketing site (
debelu-marketing) to maximize SSR performance, dynamic OG image generation, and SEO edge indexing. - Railway: Hosts the containerized Express backend (
debelu-backend) with co-located Redis and high-throughput TCP connections. - Supabase Cloud: Hosts PostgreSQL and serverless Deno edge workers.
Alternatives Considered
- Single-Cloud AWS (S3 + CloudFront + ECS + RDS): Massive configuration sprawl, complex Terraform manifests, and exorbitant CloudFront data egress billing.
Consequences
- Positive: Best-of-breed developer experience per surface; zero egress fees on storefront assets; instant preview branches.
- Negative: Requires managing DNS, TLS certificates, and secrets across three cloud provider dashboards.
ADR-009: Cloudflare R2 for Product Media & Document Storage
- Status: Accepted
- Date: 2026-04-01
- Deciders: Storage Squad, Finance Lead
Context
Product listings, vendor identity documents (NIN cards, student IDs), and dispute attachments require terabytes of durable object storage. Traditional cloud storage providers (AWS S3) impose punishing data egress fees when millions of image requests hit the CDN.
Decision
Utilize Cloudflare R2 via the S3-compatible API for all user uploads, fronted by custom domain cdn.debelu.com.
Alternatives Considered
- Supabase Storage: Under-the-hood pricing scales poorly for heavy image assets; lacks fine-grained custom CDN transformation controls.
- AWS S3: Prohibitive egress charges when images are viewed repeatedly by mobile app users.
Consequences
- Positive: Zero data egress fees regardless of bandwidth consumed; native integration with Cloudflare CDN edge caching; 99.999999999% (11 9s) durability.
- Negative: Lacks native image resizing transforms out of the box (handled via client-side Canvas compression before upload).
ADR-010: Maker-Checker Dual Authorization for High-Risk Operations
- Status: Accepted
- Date: 2026-04-15
- Deciders: Compliance Lead, Security Squad, Tech Lead
Context
High-consequence administrative actions—such as dispatching millions in vendor payout batches, manually adjusting user wallet balances, or altering platform commission rates—cannot be entrusted to a single operator due to insider fraud risks and accidental keystroke errors.
Decision
Enforce the 4-Eyes Principle (Maker-Checker) across all high-risk administrative mutations:
- Operator A (The Maker) drafts a structured proposal.
- Operator B (The Checker) reviews and executes or rejects the proposal.
- The system enforces database-level constraints:
proposal.created_by != proposal.reviewed_by.
Alternatives Considered
- Single Superadmin Role: Vulnerable to credential phishing and unilateral insider misconduct.
- Out-of-Band Email Approvals: Unauditable and disconnected from atomic database transaction lifecycles.
Consequences
- Positive: Complete elimination of unilateral financial embezzlement; ironclad compliance with CBN financial controls; fully auditable proposal history.
- Negative: Introduces minor operational delay while waiting for peer review.
ADR-011: Zustand for Lightweight Client State Management
- Status: Accepted
- Date: 2026-05-01
- Deciders: Frontend Architecture Squad
Context
Client applications require global state management for user authentication sessions, active shopping carts, campus location context, and UI drawer states without unnecessary boilerplate or rendering cascades.
Decision
Standardize on Zustand across all React applications, using persistent middleware for local storage syncing and React Context for dependency injection.
Alternatives Considered
- Redux Toolkit (RTK): Excessive boilerplate actions, reducers, and selectors for straightforward campus commerce state.
- React Context Alone: Triggers severe re-rendering cascades across the DOM tree whenever any state slice updates.
Consequences
- Positive: Minimal bundle footprint ($< 1.2\text{ KB}$); zero boilerplate; precise selector subscriptions preventing unnecessary component re-renders.
- Negative: Requires discipline to prevent storing server-cached data in Zustand (server data belongs in TanStack Query).
ADR-012: Universal Zod Schema Validation Across Frontend and Backend
- Status: Accepted
- Date: 2026-05-15
- Deciders: Architecture Squad
Context
Payload validation discrepancies between frontend form handling and backend API routes were a frequent source of bugs, unhandled 500 errors, and security bypasses.
Decision
Standardize on Zod as the single schema definition library across the monorepo:
- Shared schemas reside in [
packages/core/src/schemas/](file:///c:/Users/frank/OneDrive/Desktop/Chisom/Debelu/New%20Debelu%20Marketplace/packages/core/src/schemas/). - Backend routes use Zod middleware to validate
req.body,req.query, andreq.params. - Frontend forms use
react-hook-formwith@hookform/resolvers/zod. - TypeScript types are inferred directly via
z.infer<typeof Schema>.
Alternatives Considered
- Yup / Joi: Weaker TypeScript type inference; inconsistent browser vs Node.js bundle footprints.
- TypeBox: Faster JSON schema parsing, but steeper learning curve and poorer integration with React form ecosystems.
Consequences
- Positive: 100% type parity between frontend forms and backend validation; compile-time errors when API contracts change.
- Negative: Minor runtime parsing overhead on extremely large JSON payloads (negligible for typical REST requests).
ADR-013: Next.js for Public Marketing, Vite for Authenticated Applications
- Status: Accepted
- Date: 2026-06-01
- Deciders: Frontend Squad, Growth Lead
Context
The marketing website requires exceptional Server-Side Rendering (SSR), automatic OpenGraph image generation, and search engine optimization (SEO) for campus discovery. Conversely, the storefront and admin console are rich, authenticated, interactive web applications that demand ultra-fast client-side navigation.
Decision
Divide frontend architectural stacks:
debelu-marketing: Next.js 16 App Router on Vercel for public discovery and SEO.apps/storefront&debelu-admin: React 19 + Vite 6 Single Page Applications (SPAs) on Cloudflare Pages.
Alternatives Considered
- Single Massive Next.js App: Exposing authenticated portals inside Next.js introduced heavy SSR caching complexities and compromised the zero-egress static hosting model for the storefront.
Consequences
- Positive: Best-of-both-worlds: 100 Lighthouse SEO on marketing, instant sub-50ms tab transitions in the storefront.
- Negative: Requires cross-domain session handoff via signed short-lived URL tokens when users click "Sign In" on marketing.
ADR-014: Content Security Policy (CSP) with Build-Time Inline Script Hashing
- Status: Accepted
- Date: 2026-06-15
- Deciders: Security Squad, Frontend Lead
Context
Static hosting on Cloudflare Pages prevents dynamically generating per-request cryptographic nonces in HTTP response headers. However, strict Content Security Policy (CSP) is required to block Cross-Site Scripting (XSS) and token theft.
Decision
Implement build-time inline script hashing via [apps/storefront/vite.csp-nonce.ts](file:///c:/Users/frank/OneDrive/Desktop/Chisom/Debelu/New%20Debelu%20Marketplace/apps/storefront/vite.csp-nonce.ts):
- During
vite build, a custom plugin scans all injected inline scripts (theme initializers, analytics). - Computes the SHA-256 base64 hash of every script content.
- Injects the hashes into the CSP header in
_headers:script-src 'self' 'sha256-abc...' 'sha256-xyz...'.
Alternatives Considered
- Using
'unsafe-inline': Completely defeats XSS protection; unacceptable for financial applications. - Moving to SSR Server for Dynamic Nonces: Significant increase in hosting costs and infrastructure complexity just to serve header nonces.
Consequences
- Positive: Enterprise-grade XSS protection on completely static, zero-cost edge hosting.
- Negative: Modifying any inline bootstrap script requires rebuilding the frontend bundle to update the cryptographic hash.
ADR-015: WhatsApp Business Cloud API for Real-Time Campus Notifications
- Status: Accepted
- Date: 2026-07-01
- Deciders: Product Lead, Growth Lead, Engineering Lead
Context
Nigerian university students rarely monitor traditional email inboxes. SMS delivery rates across Nigerian telecom networks (MTN, Airtel, Glo, 9mobile) suffer from frequent 15–30 minute gateway queues and DND (Do-Not-Disturb) filtering. Conversely, WhatsApp is actively open on student devices $> 6\text{ hours/day}$.
Decision
Integrate the Meta WhatsApp Business Cloud API as a tier-1 transactional notification channel alongside mobile push notifications for order confirmations, delivery PIN dispatch, and dispute updates.
Alternatives Considered
- Aggregated SMS Gateways (Termii / Twilio): High per-message cost ($~₦4.00/\text{SMS}$) with unpredictable carrier delivery delays.
- Push Notifications Only: Fails when students are off-campus, when battery-saver kills background processes, or when mobile apps are closed.
Consequences
- Positive: $> 98%$ delivery within 5 seconds; students receive delivery PINs directly in their most active chat app; supports rich interactive CTA buttons.
- Negative: Requires strict template pre-approval by Meta; imposes Meta messaging window fees for outbound business-initiated messages.
ADR-016: Strict Origin Segregation for Administrative Operations (admin.debelu.com)
- Status: Accepted
- Date: 2026-07-15
- Deciders: Security Squad, Compliance Lead
Context
Bundling administrative capabilities or admin route components within the consumer storefront application exposes staff authorization surfaces to client-side reverse engineering, accidental role leaks, and Cross-Site Scripting (XSS) privilege escalation vectors.
Decision
Enforce strict origin isolation:
- All staff administration lives in a dedicated repository package (
debelu-admin) deployed exclusively tohttps://admin.debelu.com. - Any administrative navigation attempt within the consumer storefront triggers an immediate hard browser redirect via
AdminExternalRedirect(window.location.replace('https://admin.debelu.com')). - Storefront production build trees completely exclude administrative components, hooks, and service clients.
Alternatives Considered
- Role-Based Route Rendering in a Single App: Leaving
/adminroutes in the storefront protected only by client-side auth guards exposes administrative UI code to bundle inspection.
Consequences
- Positive: Total elimination of client-side privilege escalation; zero administrative code in public consumer bundles; allows independent deployment and stricter CSP policies on the admin origin.
- Negative: Requires staff members to maintain separate browser sessions across domains.
ADR-017: Unleash Edge Proxy for Dynamic Campus Feature Flags
- Status: Accepted
- Date: 2026-08-01
- Deciders: Infrastructure Squad, Operations Lead
Context
Features must be rolled out incrementally across universities (e.g. launching Flash Sales at UNILAG before enabling at UI or OAU). Hardcoded deployments or manual database switches lack fine-grained user targeting and instantaneous kill-switch capabilities.
Decision
Integrate Unleash via a lightweight Edge Proxy, backed by an in-memory typed fallback dictionary ([packages/core/src/services/featureFlags.ts](file:///c:/Users/frank/OneDrive/Desktop/Chisom/Debelu/New%20Debelu%20Marketplace/packages/core/src/services/featureFlags.ts)).
Alternatives Considered
- LaunchDarkly: Enterprise SaaS pricing exceeds current budget constraints.
- Custom Database Flags Table: High database query overhead on high-frequency frontend renders; lacks sophisticated percentage rollout strategies.
Consequences
- Positive: Sub-millisecond flag evaluation; instant emergency feature shutdown without code redeployment; campus-specific targeting strategies.
- Negative: Adds a downstream proxy dependency (mitigated by hardcoded local fallback defaults if proxy is unreachable).
2. Template for New ADRs
When authoring a new ADR, copy the following structure and submit as a pull request to docs/architecture/adr.md:
## ADR-NNN: [Title of Decision]
* **Status**: Proposed | Accepted | Superseded | Deprecated
* **Date**: YYYY-MM-DD
* **Deciders**: [List of Authors & Reviewers]
### Context
[What is the context, architectural issue, or business constraint motivating this decision?]
### Decision
[What is the architectural change or pattern we are establishing?]
### Alternatives Considered
* *[Alternative 1]*: [Why it was rejected]
* *[Alternative 2]*: [Why it was rejected]
### Consequences
* **Positive**: [Benefits, improvements, unlocked capabilities]
* **Negative**: [Trade-offs, overhead, operational burdens introduced]