Clean PostgreSQL core.
Decoupled TypeScript domain.
The architecture is designed around two non-negotiable constraints: immediate delivery speed for disaster recovery and strict separation of concerns so legacy database quirks never pollute the new codebase.
How Public Traffic, Clean PostgreSQL, and NetBird Legacy Admin Coexist
┌────────────────────────────────────────────────────────────────────────────────┐
│ PUBLIC INTERNET (Cloudflare WAF) │
└───────────────────────────────────────┬────────────────────────────────────────┘
│ HTTPS (highclerecastleshop.co.uk)
▼
┌────────────────────────────────────────────────────────────────────────────────┐
│ NEW APPLICATION SERVER (Debian / Docker / Node.js) │
│ │
│ ┌──────────────────────────────┐ ┌──────────────────────────────────┐ │
│ │ Astro SSR + Svelte Islands │───────▶│ Framework-Agnostic Domain Layer │ │
│ │ (Thin Presentation Views) │ │ (src/domain/*, Hono API, Zod) │ │
│ └──────────────────────────────┘ └─────────────────┬────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────┐ │
│ │ Clean PostgreSQL Database │ │
│ │ (Managed via Drizzle Migrations) │ │
│ └─────────────────▲────────────────┘ │
└────────────────────────────────────────────────────────────┼───────────────────┘
│
Idempotent ETL Sync │ (Every 1–2 mins:
(ON CONFLICT DO UPDATE) │ users, orders,
│ order_item, vouchers)
┌────────────────────────────────────────────────────────────┼───────────────────┐
│ PRIVATE NETBIRD VPN MESH (No Public Ingress) │ │
│ │ │
│ ┌──────────────────────────────┐ ┌─────────────────┴────────────────┐ │
│ │ Office Staff Browsers │───────▶│ Legacy PHP 7.4 (/admin only) │ │
│ │ (Order Lookup & Gate Lists) │ │ + Legacy MariaDB 10.11 │ │
│ └──────────────────────────────┘ └──────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────────────────────┘ Core Stack & Layer Responsibilities
Presentation Layer
Astro SSR (@astrojs/node) + Svelte Islands
Server-rendered HTML for fast ticket loading on mobile connections at the Castle gates, paired with Svelte islands for interactive calendars and admin tables in Components 2–4.
Domain & API Layer
Pure TypeScript + Hono + Zod
All business rules (src/domain/), barcode generation, Postmark email dispatch, and API routes live
outside .astro files—making UI retooling effortless if ever required.
Persistence & Migration
PostgreSQL + Drizzle ORM
Replaces legacy MyISAM tables with ACID transactions, foreign keys, TIMESTAMPTZ, row-level
locking for ticket capacity, and transactional schema migrations.
Distinguishing Ticketing & Commerce from Friends of Highclere
While sharing heritage typography (Cormorant Garamond + Jost) and sharp architectural
border-radius: 0 geometry with Friends of Highclere, this platform uses its own distinct
colour system so customers immediately recognise which Highclere property they are using:
Highclere Ticketing & Commerce (This Site)
Palette: Estate Forest Green (#1d3b2a), Burnished Brass (#9a7832),
and Warm Parchment (#f6f3ec).
Character: Parkland gates, formal printed admission tickets, clarity under outdoor mobile use.
Friends of Highclere Castle (Sibling Product)
Palette: Deep Crimson (#572130), Soft Teal (#A0D1CA), and Warm Stone
(#f7f4f0).
Character: Intimate drawing-room club, editorial video archive, and member community.
Recorded Decisions (docs/adr/)
ADR-0001: Clean PostgreSQL Schema with Idempotent Legacy MariaDB ETL Sync
Accepted
During disaster recovery, the legacy CodeIgniter 3 / MariaDB 10.11 application is restricted to internal staff
over NetBird VPN for back-office operations, while customer-facing functionality is rebuilt incrementally in
Node.js and Astro. Rather than coupling the new application to the legacy 118-table MariaDB schema or waiting
to rebuild the back office before launching, the new application will exclusively query a clean PostgreSQL
database managed via Drizzle migrations, populated from the legacy MariaDB database via a scheduled,
idempotent ETL sync (ON CONFLICT DO UPDATE). Once back-office workflows are migrated to the new
application, the ETL sync and legacy MariaDB instance will be retired with zero changes required in the new
application's domain or data-access code.
ADR-0002: Passwordless Customer Auth and Email-Based Account Consolidation
Accepted
The legacy users table stored passwords in plaintext and permitted multiple rows with the same
email address. Rather than migrating compromised plaintext passwords or perpetuating duplicate accounts, the
new PostgreSQL schema consolidates Customer records by normalised email address and authenticates
customers via Email OTP / Magic Link (or single-order lookup using Order Number, Email, and Billing Postcode).
Because the legacy Gate List barcode format ({order_number}-{order_item_id}-{user_id})
embeds the original MariaDB users.id, each migrated Order preserves its original
legacy_user_id so generated barcodes remain 100% compatible with on-site gate scanners.
ADR-0003: Astro SSR with Framework-Agnostic Domain Layer and Svelte Islands
Accepted
To maximise delivery speed during disaster recovery, the application will use Astro SSR
(@astrojs/node) with Svelte islands for interactive widgets, leveraging existing team familiarity
with Astro. Because Astro is content-first and may hit ergonomic limits as complex interactive workflows
(Coach Portal, Timed-Entry Checkout, Staff Back-Office) are added, all database schemas (src/db/),
ETL pipelines (src/etl/), business rules (src/domain/), and HTTP API handlers
(src/api/ via Hono) must be written in framework-agnostic TypeScript. .astro pages
must act strictly as thin view templates calling into src/domain/ or src/api/,
ensuring the presentation layer can be swapped to SvelteKit or another full-stack framework later without
rewriting domain or data logic.