A senior-architect blueprint for rebuilding A-List on AWS as a small team optimizing for development speed and customer experience. The spine is one TypeScript monorepo, one modular-monolith backend, and one set of shared types flowing into web, portals and mobile. Designed against the current state revealed by the 13-repo audit, not on a blank page.
A-List is a small team running a UAE consumer marketplace. The correct shape is not microservices — it is a modular monolith with hard module boundaries, fronted by a few right-sized web/mobile apps, all sharing code through a single Turborepo + pnpm monorepo. Picking TypeScript end-to-end (Astro, React Router 7, Expo/React Native, NestJS) means one language, one type system, and one shared @alist/contracts package validating data from database to phone screen — the single biggest dev-velocity unlock for a team this size. Everything runs on AWS managed services so the org pays Amazon for reliability instead of hiring for it. We get there with a strangler-fig migration that keeps today's Laravel authoritative while new surfaces ship around it — value first, big-bang never.
@alist/contracts (Zod schemas + types), @alist/api-client, @alist/ui (shadcn/Tailwind), @alist/config. Web, portals, mobile and backend all import the same types. Change an API field once; every client fails to compile until it's fixed. That is how three platforms stay in lockstep without three teams.
You invited me to overrule where warranted. Five endorsed outright; two refined with reasoning.
| Your call | Verdict | Where it lands |
|---|---|---|
| Astro for the website | Endorse | Astro 5, static-first with React islands. Perfect for content/SEO; kills the current Vue/CRA SPA hydration cost. Also fold creators-website into it and move lead-capture server-side. |
| Remix for the portals | Endorse | Yes — with a 2026 footnote: Remix is now React Router 7 framework mode. Consolidate the 3 portals into one role-aware app. |
| Postgres / Elasticsearch / Redis | Refine | Postgres (Aurora) as the system-of-record; Redis (ElastiCache) yes. But Elasticsearch is a search index, not a primary database — fed from Postgres via an outbox. Using it as a store of record is an anti-pattern (no transactions/consistency). |
| AWS-first, cloud-first | Endorse | Fully. ECS Fargate + Aurora + ElastiCache + CloudFront, three isolated accounts, CDK as code. |
| Not microservices — a quality architecture | Endorse | Modular monolith is exactly right for this team. Microservices would add ops burden you can't staff. Module boundaries + EventBridge for the few async seams. |
| One way to build web + iOS + Android | Endorse | You're already most of the way there — alist-android is Expo and builds all three. Consolidate on Expo / React Native, retire the native Swift app, share code via the monorepo. |
| Backend framework (you specified DB, not framework) | Refine | Recommend an end-to-end-TypeScript NestJS modular monolith over keeping PHP/Laravel — so the backend shares the language, types and validation with every client. Reached via strangler-fig, not a rewrite (see the pivotal-decision box). |
What the audit found
dev-*.alist.ae URL.Where the rebuild lands
@alist/contracts, api-client, ui.This is the one call worth slowing down on. Two honest options for the backend:
Lower risk · keeps a working system
Recommended destination
@alist/contracts Zod schemas drive tRPC, REST, DB and forms from one source; tRPC gives the portals end-to-end type safety with zero codegen.| Layer | Today | Target | Why | Effort |
|---|---|---|---|---|
| Public web | Vue 3 SPA + self-hosted Strapi v4; CRA creators site with a leaked browser token | Astro 5 + React islands; managed CMS (Sanity); forms server-side on Lambda | Zero-JS-by-default speed & SEO; deletes the self-hosted Strapi attack surface; no provider token in the browser | M |
| Portals | Blade admin + Next.js vendors + partner — 3 apps, localStorage tokens, inline role checks | ONE React Router 7 app, role-aware; HttpOnly cookies; central CASL RBAC | One codebase for three audiences; fixes token-in-localStorage and the no-authz-framework systemic gap | XL |
| Mobile | Native Swift iOS + Expo app (already iOS+Android+web); committed keys; ATS off | Expo SDK 54 (RN New Arch) universal; retire Swift; SecureStore + cert pinning | One RN codebase for all platforms via the monorepo; fixes insecure storage & transport; OTA updates via EAS | L |
| Backend | Two Laravel 11 apps; SQLi, SSRF, OTP flaws; EOL PHP on some | NestJS 11 modular monolith on Fargate; tRPC + REST; HMAC webhooks; BullMQ | End-to-end types with the clients; explicit module boundaries; not microservices | XL |
| Data | One shared MySQL across partner/creator/portal + legacy forks | Aurora PostgreSQL SoR (per-domain schemas, least-privilege) + OpenSearch index + Redis | Kills the shared-DB trust boundary; real search without ES-as-primary; PITR backups | XL |
| AWS platform | cPanel/dev boxes; manual deploys; secrets in git; prod trusts dev | CDK; 3 accounts; Fargate + ALB + CloudFront/WAF; Secrets Manager; SQS/SES | Account isolation is the only hard boundary; ends the EOL-runtime treadmill | L |
| DevSecOps | No CI, no tests, no scanning; bus-factor-1 per repo | Turborepo + GitHub Actions gates (gitleaks/SAST/SCA/tests); module owners | Makes every other choice shippable by a small team; closes the systemic root causes | XL |
Astro 5 · React 19 islands · Tailwind v4
React Router 7 (Remix) · shadcn/ui · TanStack
Expo SDK 54 · React Native 0.81 · Expo Router
NestJS 11 · Node 22 · tRPC + REST · Zod
Aurora PostgreSQL 16 · OpenSearch · Valkey
CDK · ECS Fargate · CloudFront/WAF · Cognito
Sequenced to ship customer value fast while cutting the worst risk first. Laravel stays authoritative until each module is cut over — nothing breaks on day one.
| Systemic finding (audit) | Architectural fix |
|---|---|
| Secrets committed across repos; one Bitbucket key unlocks all | Secrets Manager + SSM, runtime injection, rotation; gitleaks merge gate + history purge |
| One shared production database across apps + legacy forks | Aurora with per-domain schemas & least-privilege users; retire demo/v2; separate partner/creator/portal |
| Prod clients trust a hardcoded dev API URL (7 repos) | 3 isolated AWS accounts; API base URL read from per-account SSM, never hardcoded |
| No CI/CD, no tests, no SCA; manual deploys | GitHub Actions gates (gitleaks/SAST/SCA/tests); OIDC blue/green deploys; reproducible Fargate images |
| No authorization framework — inline role strings, route-placement auth | Central CASL policy with a global default-deny guard; every endpoint declares its required ability |
| Tokens in localStorage; weak/echoed OTP; backdoor | Cognito JWTs, HttpOnly cookies for portals, SecureStore on mobile; OTP rate-limit/lockout |
| EOL runtimes (PHP 7.x, Strapi 4.14) never patched | Managed/containerized current runtimes; Dependabot/Renovate; managed CMS removes patching burden |
| SQLi / SSRF / XSS / mass-assignment classes | Prisma/parameterized queries, Zod validation everywhere, output encoding, allow-listed outbound fetch |