← BACK TO BLOGENTRY 03 / 08
ENGINEERING/8 MIN

Shipping a Laravel + Angular Monolith Without Losing Your Mind

How I structure a single repo that ships a Laravel API, an Angular SPA, and a shared TypeScript contract — without resorting to a microservices manifesto. Real folder layout, real CI, real tradeoffs.

KS
Kusal Salpura
Full-Stack Developer & Software Engineer

Shipping a Laravel + Angular Monolith

Most teams reach for microservices the moment their second frontend shows up. After shipping three Laravel + Angular projects this year, I am here to tell you that the boring monolith — done deliberately — will outperform a premature microservices setup on every axis that matters: developer velocity, deployment risk, observability, and cost.

The folder layout that actually works

I keep one git repository per product. Inside it:

/apps
  /api          -> Laravel 11 (REST + queues)
  /web          -> Angular 17 standalone components
  /admin        -> optional Angular shell
/packages
  /contracts    -> TypeScript types shared with the API
  /ui           -> shared presentational components
/docker
/infra

The packages/contracts directory is the secret. Laravel exposes its API shape via Scribe or a hand-rolled OpenAPI spec, and a tiny pnpm script regenerates TypeScript types into packages/contracts on every push to main. The Angular app imports those types directly. No drift. No any. No "the API changed and nobody told the frontend" incidents.

The three rules I refuse to break

  1. One deploy target per product. Laravel serves the Angular build from public/ in production. Two apps, one deploy. No CORS. No separate origin story.
  2. Queues are not optional. Anything that touches a third party (email, payments, webhooks) goes on a Laravel queue. The HTTP request returns 200 in under 200ms, period.
  3. The database is the contract. Migrations are reviewed like application code. No "quick ALTER in prod" — ever.

CI, in 90 seconds

GitHub Actions runs three jobs in parallel on every push: phpunit, jest (Angular), and phpstan. A fourth job regenerates the contracts package and fails if the diff is non-empty — that catches forgotten spec updates before they ship.

When all four are green, a single Docker image is built and pushed. No "frontend pipeline" and "backend pipeline" — one artifact, one deploy.

When this stops working

I would reach for a split the day one of these becomes true: a second team needs to ship backend changes independently, the deploy image crosses ~15 minutes, or the queue backlog starts affecting web requests through shared database connections. None of that has happened yet.

Until then, the monolith ships faster, debugs easier, and lets me close the laptop at 6pm.

    Shipping a Laravel + Angular Monolith Without Losing Your Mind — Kusal Salpura