# United Tattoo – Backend Architecture Document Version: 1.0 Date: 2025-09-17 Output Template: .bmad-core/templates/architecture-tmpl.yaml (architecture-template-v2) Basis: docs/PRD.md, repo config (wrangler.toml, open-next.config.ts, next.config.mjs, package.json), lib/*, sql/schema.sql Introduction This document outlines the backend architecture for United Tattoo, including platform/runtime, data, integrations, security, operations, and non‑UI concerns. It is the blueprint for AI-driven and human development to implement the PRD. Relationship to Frontend Architecture: A separate Frontend Architecture document should cover UI state, routing, component patterns, and UX specifics. Core technology selections herein (Cloudflare, Next.js App Router, D1/R2, Auth.js, Zod) apply project-wide. Starter Template or Existing Project Decision: Existing project (this repository) on Next.js App Router with OpenNext for Cloudflare. - Pre-configured stack: Next 14.2.16, Tailwind, ShadCN, OpenNext Cloudflare adapter, Wrangler, Vitest. - Structure: app/ routes, components/, lib/, sql/, etc. - Built-in patterns: SSR with App Router, route handlers under app/api, middleware guards, Zod validations, D1/R2 bindings. - Constraints: Cloudflare Workers runtime; D1 (SQLite semantics), R2 for media and ISR cache. Images unoptimized (Next images disabled by config). Change Log | Date | Version | Description | Author | |------------|---------|---------------------------------------------|----------| | 2025-09-17 | 1.0 | Initial backend architecture document | Architect | High Level Architecture Technical Summary United Tattoo runs as a serverless, modular monolith on Cloudflare Pages + Workers using the OpenNext adapter. Next.js App Router handles SSR/ISR and routing; Cloudflare D1 stores structured data (users, artists, appointments, settings) and R2 stores media plus incremental cache. Back-end concerns (auth, RBAC, validations, uploads, booking, payments, notifications, calendar sync) are implemented via Next.js route handlers and server actions with strict Zod validation and middleware-based RBAC. The architecture prioritizes image-forward delivery performance, reliability, and maintainability aligned to the PRD. High Level Overview 1) Style: Serverless modular monolith (single app, bounded modules by domain). 2) Repo: Single repository (this project). 3) Services: Single deployable (OpenNext worker) with internal modules (auth, artists, portfolio, booking, payments, notifications, calendar). 4) Primary flows: - Public SSR pages, search/discovery (later), booking/consultation routing, client portal (later). - Admin onboarding, artist/portfolio CRUD, uploads to R2, moderation queue (later), activity logs (later). - Payments via Stripe for deposits; later add PayPal. - Two-way Google Calendar sync for artists (planned now). 5) Key decisions: - Cloudflare-first stack (D1, R2, Pages/Workers) with OpenNext. - REST via route handlers + Server Actions for same-origin mutations. - Zod across edges; strict RBAC via middleware. - Sentry + OpenTelemetry for observability; Upstash Redis for rate limiting. High Level Project Diagram ```mermaid graph TD subgraph Users V[Visitor] C[Client] A[Admin/Artist] end V -->|HTTP(S)| CDN[Cloudflare CDN] C -->|HTTP(S)| CDN A -->|HTTP(S)| CDN CDN --> W[OpenNext Worker (Next.js App)] W --> D1[(Cloudflare D1)] W --> R2[(Cloudflare R2 - Media)] W --> R2INC[(R2 - ISR/Incremental Cache)] W --> STRIPE[Stripe API] W --> RESEND[Resend Email] W --> TWILIO[Twilio SMS] W --> GCAL[Google Calendar API] style W fill:#222,stroke:#999,color:#fff style D1 fill:#114,stroke:#99f,color:#fff style R2 fill:#141,stroke:#9f9,color:#fff style R2INC fill:#141,stroke:#9f9,color:#fff,stroke-dasharray: 5 5 ``` Architectural and Design Patterns - Serverless Modular Monolith (chosen): Single worker with domain modules. Rationale: Simpler ops, Cloudflare-native, low latency, satisfies PRD scope; avoids premature microservices. - Communication: RESTful route handlers + Server Actions. Alternative: GraphQL (deferred). Rationale: Simplicity, aligns with Next App Router, easy cache/policy control. - Data Access: Thin repository-like helpers (lib/db.ts) with prepared SQL on D1. Alternative: Kysely/Prisma (N/A on Workers) → stay with direct SQL for D1. Rationale: Control, performance, D1 fit. - Validation: Zod at boundaries (API/server actions/forms). Rationale: Single schema source; consistent erroring. - AuthZ: Middleware-based RBAC on path prefixes + helper functions. Rationale: Centralized enforcement. - Caching: R2-based ISR via OpenNext override; CDN caching for assets/API where applicable. Rationale: Cost/perf balance. - Rate limiting: Redis (Upstash). Alternative: KV tokens. Rationale: Simplicity and global availability. Tech Stack Cloud Infrastructure - Provider: Cloudflare - Key Services: Pages + Workers (OpenNext), D1 (SQL), R2 (object storage + ISR cache) - Regions: Global edge (Cloudflare network), D1 region as configured by account Technology Stack Table | Category | Technology | Version | Purpose | Rationale | |------------------|---------------------------------|-------------------------|----------------------------------------------|--------------------------------------------------| | Language | TypeScript | 5.x (lockfile-resolved) | Primary backend language | Strong typing, tooling, Next.js alignment | | Framework | Next.js (App Router) | 14.2.16 | SSR/ISR, routing, APIs, server actions | Matches UI, supports OpenNext | | Platform | OpenNext (Cloudflare) | ^1.8.2 (lockfile pin) | Next->Workers build, ISR in R2 | Official adapter, ISR override support | | Runtime | Cloudflare Workers | nodejs_compat enabled | Serverless execution | Edge-native, low latency | | Config/Deploy | Wrangler | ^4.37.1 (pin in lock) | Build/preview/deploy worker + bindings | Cloudflare-native tooling | | DB | Cloudflare D1 | Managed | Relational store for core data | Simple, sufficient for scope, low-ops | | Storage | Cloudflare R2 | Managed | Media assets; ISR cache bucket | Cost-effective, global access | | Auth | NextAuth (Auth.js) | ^4.24.11 (pin in lock) | JWT sessions + providers | Standard pattern with Next | | Validation | Zod | 3.25.67 | Input/env validation | Ubiquitous + types inference | | Testing | Vitest + RTL | ^3.2.4 + latest RTL | Unit/component testing | Fast, TS-native | | Observability | Sentry + OpenTelemetry | Latest stable | Errors, traces, metrics | Full visibility across Workers + Next | | Rate Limiting | Upstash Redis | Managed | Rate limit auth/forms/APIs | Simplicity, global | | Payments | Stripe (primary); PayPal later | Latest SDK | Deposits, refunds | Start with Stripe per customization | | Email | Resend | Latest SDK | Transactional emails | Simple, modern API | | SMS | Twilio | Latest SDK | Notifications (reminders, etc.) | Reliable, well-documented | | Calendar | Google Calendar API | v3 | Two-way artist sync | Per customization → implement now | Note: Exact semver pins should be recorded from the lockfile in CI. Next 14.2.16 and zod 3.25.67 are exact; others are resolved via lock. Data Models Core entities mapped from PRD and schema: - User: id, email, name, role (CLIENT|ARTIST|SHOP_ADMIN|SUPER_ADMIN), avatar. - Artist: id, userId, name, bio, specialties[], instagramHandle, isActive, hourlyRate. - PortfolioImage: id, artistId, url, caption, tags[], orderIndex, isPublic, createdAt. - Appointment: id, artistId, clientId, title, description, startTime, endTime, status, depositAmount, totalAmount, notes. - Availability: id, artistId, dayOfWeek, startTime, endTime, isActive. - SiteSettings: id, studioName, description, address, phone, email, socialMedia{}, businessHours[], heroImage, logoUrl. - FileUpload: id, filename, originalName, mimeType, size, url, uploadedBy. Relationships: - User 1‑to‑1/0‑1 Artist (user_id). - Artist 1‑to‑many PortfolioImage. - User (client) 1‑to‑many Appointment; Artist 1‑to‑many Appointment. - Artist 1‑to‑many Availability. Components - Auth & RBAC - Responsibility: JWT sessions; role enforcement. - Interfaces: /auth routes (NextAuth), middleware guards, helpers isAdmin/hasRole. - Dependencies: NextAuth, middleware.ts. - Tech: NextAuth with Credentials (dev), Google/GitHub optional. - Artist & Portfolio Service - Responsibility: CRUD artists, portfolio images, ordering, visibility. - Interfaces: app/api/artists/*, app/api/portfolio/*, server actions for admin UI. - Dependencies: D1 tables, R2 uploads. - Tech: lib/db.ts helpers; lib/r2-upload.ts. - Upload Service - Responsibility: R2 uploads (single/bulk), file validation, delete. - Interfaces: app/api/upload, admin UI dropzones. - Tech: R2 bucket binding; R2_PUBLIC_URL used to compose public URLs. - Booking & Scheduling - Responsibility: Booking/consultation routing, appointment CRUD, availability. - Interfaces: app/api/appointments/*; server actions for stepper. - Dependencies: D1, Notifications, Stripe. - Tech: Zod forms; quote estimation (to be implemented). - Payments - Responsibility: Deposit intents (Stripe), receipts, refunds; PayPal later. - Interfaces: app/api/payments/* webhooks; server actions for checkout init. - Dependencies: Stripe SDK, D1 to persist intents/receipts. - Tech: Stripe Checkout/PaymentIntents; signed webhooks; idempotency keys. - Notifications - Responsibility: Email (Resend) + SMS (Twilio), templates, preferences. - Interfaces: internal functions invoked from flows; background execution pattern if needed. - Dependencies: D1 preferences, external providers. - Calendar Integration - Responsibility: Google Calendar two-way sync for artists (now). - Interfaces: OAuth connect, webhook/push, polling fallback, reconciliation. - Dependencies: Google API, D1 for tokens, Appointments. Component Diagram ```mermaid graph LR AUTH[Auth & RBAC] --> API[Route Handlers / Server Actions] ART[Artists/Portfolio] --> API UP[Upload Service] --> API BK[Booking/Scheduling] --> API PAY[Payments (Stripe)] --> API NOTIF[Notifications] --> API CAL[Calendar Sync] --> API API --> D1[(D1)] API --> R2[(R2)] PAY --> STRIPE[Stripe] NOTIF --> RESEND[Resend] NOTIF --> TWILIO[Twilio] CAL --> GCAL[Google Calendar] ``` External APIs - Stripe API - Purpose: Deposits, refunds; store payment intents + receipts. - Auth: API keys (Wrangler secret); webhook signing secret. - Rate limits: Stripe-managed; implement retries with backoff; idempotency keys. - Endpoints: PaymentIntents, Checkout, Refunds, Webhooks. - Resend - Purpose: Transactional emails (booking confirmations, receipts). - Auth: API key (secret). - Integration: templated emails, error handling. - Twilio - Purpose: SMS notifications (confirmations/reminders). - Auth: Account SID/Token (secret). - Rate limits: provider-specific; implement basic backoff. - Google Calendar v3 - Purpose: Two-way artist sync (now). - Auth: OAuth 2.0 per artist; token storage in D1; refresh handling. - Endpoints: Events, Watch (push), Channels (stop), CalendarList. Core Workflows Booking with Deposit (sequence) ```mermaid sequenceDiagram participant U as User participant W as Worker(App) participant D as D1 participant S as Stripe U->>W: Submit booking form (zod-validated) W->>D: Create pending Appointment (PENDING) W->>S: Create PaymentIntent (deposit) with idempotency S-->>W: Client secret W-->>U: Return client secret U->>S: Confirm payment S-->>W: Webhook event (payment_succeeded) W->>D: Update Appointment status + store receipt reference W-->>U: Confirmation (email/SMS) ``` Portfolio Upload (sequence) ```mermaid sequenceDiagram participant A as Admin/Artist participant W as Worker(App) participant R as R2 participant D as D1 A->>W: Upload image(s) (validated) W->>R: Put object(s) W->>D: Insert portfolio_images rows W-->>A: URLs + metadata ``` REST API Spec (sketch) openapi: 3.0.0 info: title: United Tattoo Backend API version: 1.0.0 description: REST endpoints backing booking, admin, and integrations servers: - url: https://your-preview-domain.pages.dev description: Preview - url: https://your-domain.com description: Production paths: /api/appointments: get: summary: List appointments post: summary: Create appointment (server action preferred) /api/portfolio: post: summary: Upload portfolio image (server action preferred) /api/payments/deposit-intent: post: summary: Create Stripe PaymentIntent for deposit /api/webhooks/stripe: post: summary: Stripe webhook receiver Database Schema - Source of truth: sql/schema.sql (D1). Entities and indexes already implemented. - Note: Fix comment header mismatch (“united-tattoo-db” vs scripts using “united-tattoo”). - Migrations: wrangler d1 execute … --file=./sql/schema.sql (local/prod variants). Source Tree ``` project-root/ ├─ app/ # App Router (public pages, admin area, api/) │ ├─ api/ # Route handlers │ ├─ admin/ # Admin UI sections │ └─ ... # Site pages ├─ components/ # UI + composed components ├─ lib/ │ ├─ db.ts # D1 & R2 binding access + helpers │ ├─ r2-upload.ts # R2 upload manager │ ├─ auth.ts # NextAuth config + RBAC helpers │ ├─ env.ts # Zod env validation (see alignment note) │ └─ validations.ts # Zod schemas for domain + forms ├─ sql/ │ └─ schema.sql # D1 schema SSoT ├─ wrangler.toml # Worker config + bindings ├─ open-next.config.ts # ISR cache override to R2 ├─ next.config.mjs # Next config (standalone, images unoptimized) └─ package.json # Scripts (build/preview/deploy/db/test) ``` Infrastructure and Deployment Infrastructure as Code - Tool: Wrangler 4.x - Location: wrangler.toml - Approach: Wrangler-only “config of record” now; Terraform later if needed. Deployment Strategy - Strategy: OpenNext build → Cloudflare Pages (Worker + assets). - CI/CD Platform: Gitea (planned) with required pipeline gates. - Pipeline Config: .gitea/workflows/* (to be added) or equivalent CI config. Environments - dev: Local dev server (next dev) and/or Workers preview (OpenNext preview). - preview: Cloudflare Pages preview (pull requests). - production: Cloudflare Pages live. Environment Promotion Flow ``` feature → PR → CF Pages preview → E2E/quality gates → merge → production deploy ``` Rollback Strategy - Primary Method: Re-deploy previous artifact via CF Pages rollback / previous commit. - Triggers: Elevated errors, failed SLOs, payment or booking failures. - RTO: < 30 minutes for deploy rollback; data roll-forward with compensating ops. Error Handling Strategy General Approach - Error Model: Domain errors (validation, auth, business) vs operational (transient, provider). - Propagation: Translate to typed JSON errors at API boundary; avoid leaking internals. Logging Standards - Library: Console structured JSON (Workers) + Sentry/Otel exporters. - Format: JSON with fields: timestamp, level, correlationId, route, userId (if any). - Levels: debug/info/warn/error; No PII in logs. Error Handling Patterns - External API Errors: - Retry: Exponential backoff (bounded), idempotency keys (Stripe). - Circuit Breaker: Soft disable non-critical providers on repeated failures. - Timeouts: Tight timeouts; fail fast, surface friendly errors. - Business Logic Errors: - Custom error types; map to 4xx with helpful messages; do not expose details. - Data Consistency: - D1 is transactional per statement; use idempotency on effects; write-ahead logic for webhooks. Coding Standards (Backend) Core Standards - Languages & Runtimes: TypeScript (Workers-compatible), Next.js App Router. - Style & Linting: ESLint + Prettier (enable in CI, even if build ignores failures). - Tests: Vitest for unit; RTL for component; Playwright for e2e (planned). Critical Rules - Validate all external inputs with Zod at API boundary. - Use lib/db.ts helpers; do not inline raw env binding plucking in routes. - Payments: Always use idempotency keys for Stripe and verify signed webhooks. - No secrets in logs; use Wrangler secrets for all provider keys. - RBAC: All /admin and /api/admin routes must enforce SHOP_ADMIN or SUPER_ADMIN. - Rate limiting must wrap auth/forms/API entrypoints once Upstash is configured. Test Strategy and Standards Testing Philosophy - Approach: Pragmatic test-after with critical flows prioritized; expand coverage over time. - Coverage Goals: Unit 60–70% rising; critical domain paths higher. Test Types and Organization - Unit: Vitest; files *.test.ts under __tests__/ or next to modules. - Integration: D1 local (wrangler d1 execute) for repository tests; mock providers. - E2E: Playwright on CF Pages preview (booking flow, uploads, admin critical). Test Data Management - Fixtures: __tests__/fixtures; factories for domain objects. - Cleanup: Explicit clean after each suite (D1 truncate helpers for local env). Continuous Testing - CI Integration: Lint/typecheck → unit → build → migration dry-run → e2e on preview. Security Input Validation - Library: Zod - Location: Route handlers/server actions before processing - Required Rules: - Validate all inputs (query/body/params). - Whitelist approach; reject extras. Authentication & Authorization - Auth Method: NextAuth JWT (Credentials for dev; OAuth optional). - Session: JWT; role included in token; maxAge defaults; future 2FA. - Required: - Enforce RBAC in middleware + route-level checks for admin endpoints. - Implement invites and 2FA per PRD in admin flows. Secrets Management - Development: Wrangler secrets; .env only for local dev of non-secrets. - Production: Wrangler secrets; never commit secrets. - Code Rules: Never log secrets; never embed keys. API Security - Rate Limiting: Upstash Redis (global) on auth/forms/APIs. - CORS: Strict same-origin for server actions; minimal cross-origin where necessary. - Security Headers: Enforce via centralized headers helper (CSP, X-Frame-Options DENY, Referrer-Policy strict-origin-when-cross-origin, Permissions-Policy). Data Protection - At Rest: Provider-managed (D1/R2). Encrypt sensitive app-level data if needed before storage. - In Transit: HTTPS only. - PII: Store minimal; redact logs. Dependency Security - Scanner: Enable dep audit in CI; weekly review cadence. - New Deps: Require review; follow Context7 MCP check (per repo rules). Security Testing - SAST: ESLint/security rules; consider additional scanners. - DAST: E2E includes basic auth + booking hardening; expand later. Backend Customization Choices (Confirmed) - Payments: Stripe first (PayPal later). - Email: Resend. - SMS: Twilio. - Rate Limiting: Upstash Redis. - Observability: Sentry + OpenTelemetry. - Calendar Integration: Google two‑way sync now. - API Style: Next.js Route Handlers (REST) + Server Actions. - IaC/Config: Wrangler-only config of record (Terraform later). - Env/Secrets: Align env.ts to Cloudflare bindings (remove unused AWS_* + DATABASE_URL now). Note: the current names in env.ts are labeled AWS_ but map to Cloudflare R2 usage—document and align. - Security Headers: Centralized middleware/edge headers helper. Next Steps - Frontend Architecture Mode: Generate a separate frontend architecture doc leveraging this backend document and PRD UI requirements (ShadCN, search/filters, parallax/split). - Product Owner Review: Validate scope and sequencing (Phases 1–4). - Dev Agent: Implement stories for Phase 1 (Admin invites, onboarding wizard stub, artist CRUD/portfolio upload MVP, D1/R2 confirmations). - DevOps: Add CI pipeline, lock exact versions from lockfile into doc, add .env.example listing required secrets (NEXTAUTH_URL, NEXTAUTH_SECRET, R2_PUBLIC_URL, STRIPE_KEYS, RESEND_API_KEY, TWILIO_KEYS, GOOGLE_OAUTH, UPSTASH). Appendix Bindings (wrangler.toml) - DB (D1), R2_BUCKET (media), NEXT_INC_CACHE_R2_BUCKET (ISR cache), WORKER_SELF_REFERENCE (service binding). Runbooks - Preview: npm run pages:build && npm run preview - Deploy: npm run pages:build && npm run deploy - DB: npm run db:create; npm run db:migrate[:local] References - PRD: docs/PRD.md - Schema: sql/schema.sql - Config: wrangler.toml, open-next.config.ts, next.config.mjs