I Wired the Boring 80% So You Can Build the Interesting 20%
Built for the AI era: a production-ready TypeScript modular monolith with guardrails for robot coworkers. Every new project starts the same way. You need auth, multi-tenancy, file uploads, notifications, emails, i18n, a web app, a mobile app, tests, observability… and three weeks later you're still wiring boilerplate instead of building the thing that actually matters. So I built the starter I always wished existed: a production-grade modular monolith in TypeScript — one repo, one deployable, zero premature microservices — with all the boring-but-critical stuff already done. It's open source and free: fork it, change it, make it yours → https://github.com/ihssmaheel-dev/modular-monolith-starter. Here's the tour. 🎢 Starting is easy. Surviving is hard. AI made it possible to build a working app in a weekend. That's amazing — but it's also where the trouble starts. Once real users, real data, and real money arrive, so do the questions nobody planned for: Where does this business rule even live? Why does changing one screen break three others? Who can access whose data? How do we delete a user's data when the law says we must? How do we ship mobile without rewriting everything? Speed without structure becomes pain. Every time. This starter exists so you can have both: move fast on day one, and still sleep well on day one thousand. The 10-second pitch Backend: NestJS 11 + Fastify + PostgreSQL 16 + Drizzle + Redis + BullMQ Web: TanStack Start (SSR) + TanStack Query + Zustand + Tailwind 4 + shadcn Mobile: Expo 57 + expo-router + NativeWind (mirrors the web features) Contracts: Zod 4 schemas shared end-to-end — the compiler catches API drift Already wired: fine-grained authorization (RBAC + ReBAC + ABAC), multi-tenancy, GDPR tools, notifications, file uploads, one-file reskin DX candy: one-command bootstrap, a rebrand script, a full-stack feature generator, interactive API docs, local observability stack AI-native: the repo ships machine-readable architecture laws, so AI assistants write code that actually fits The architecture: a modular monolith with lite DDD Big words, simple meaning: Monolith — one codebase, one deploy. No microservice maze when you have three developers and a dream. You can always split later, when (and if) you actually need to. Modular — inside that monolith, every feature (auth, notes, files, notifications…) is a strict module with walls around it. Modules never touch each other's database tables. They talk through clean commands, queries, and events — and CI blocks you if you cheat. Lite DDD — each module has small layers: presentation, application, domain, infrastructure. Just enough structure to keep business logic pure and testable, without the enterprise paperwork. Simple to start, safe to scale — easy for a new developer (or an AI agent) to grasp in an afternoon, and solid when the team grows to twenty. A real backend, not a todo-app demo Each domain module follows CQRS: thin controllers, single-responsibility commands and queries, pure domain entities, and Drizzle repositories. Application code never throws — it returns neverthrow Results. Boring? Yes. The kind of boring that sleeps through the night? Also yes. Already wired behind those modules: oRPC + REST with parity — type-safe RPC by default, REST fallback during migrations, interactive Scalar API docs at /api/docs Fine-grained authorization — one vocabulary (notes:create, team:invite…), ownership checks, tenant and department predicates, enforced via decorators and services Zero-trust multi-tenancy — single or multi-tenant via one flag, automatic row-level isolation, Postgres RLS as defense in depth Transactional outbox — domain events publish reliably, with retries and a replayable dead-letter queue Realtime two ways — WebSocket gateway plus user-scoped SSE, with shared fan-out so web and mobile update live Resilience boring stuff — rate limiting, WAF, circuit breakers, bulkheads, idempotency keys, CSRF, scheduled jobs, Piscina worker threads for CPU-heavy work, health probes via NestJS Terminus Auth done right — Argon2 password hashing, short-lived Bearer tokens held in memory, rotating refresh tokens and signing keys, SecureStore on mobile, secure server sessions File pipeline, not just uploads — presigned S3 URLs, generic upload-then-attach with slots for any parent record, and an orphan janitor so abandoned bytes never become a storage bill Request correlation everywhere — x-request-id flows through logs, error envelopes, and traces, so a user report maps to the exact request in seconds Transactional email — React Email templates over Resend or SMTP with circuit-breaker failover, previewed locally in Mailpit GDPR built in — data export, account erasure, organization erasure, immutable audit trail with retention worker Observability from day one — structured Pino logs, OpenTelemetry traces, Prometheus metrics, and a provider-neutral error sink i18n on both sides of the wire — backend errors localized through a shared dictionary service with Accept-Language negotiation, the same keys consumed by web and mobile, and CI parity checks so no language ever falls behind Web + mobile, speaking the same language The web app (SSR on srvx, file-based routes, dark mode, EN/ES/FR out of the box) and the Expo mobile app (SecureStore auth, push notifications, deep links, offline-tolerant queries) consume the same Zod contracts as the API. Change a schema, and TypeScript yells at every layer that disagrees. API drift becomes a compile error instead of a 2 a.m. mystery. Features already living in both clients: auth flows, notes CRUD with attachments, user management, tenant invitations, notification center with preferences and live updates, GDPR export and erasure, avatar + file uploads (presigned S3 URLs via local MinIO), toasts, paginated data tables, and empty/loading/error states that don't lie to users. The notes feature doubles as the reference slice: a complete, production-shaped example — contracts, module, permissions, translations, tests, web and mobile UI — that new developers read first and copy when building their own features. And when it's time to make it yours: change one theme file, run one command, and the whole product — web, mobile, even emails — wears your brand. The complete stack, and why each piece earned its place Backend Stack Why NestJS 11 + Fastify 5 Modules and DI that mirror the monolith; Fastify for raw speed PostgreSQL 16 JSON, full-text search, RLS, rock-solid transactions Drizzle SQL you can read, types you can trust, migrations you control Redis 7 + BullMQ Queues, retries, digests, and scheduled work that survives restarts Zod 4 + oRPC One contract language, end to end — drift is a compile error neverthrow Errors as values, not hidden GOTO statements Argon2 Modern password hashing, not legacy bcrypt defaults Pino + OpenTelemetry + prom-client Structured logs, traces, and metrics without extra wiring Nodemailer / Resend + React Email Transactional mail with failover and real templates Piscina + NestJS Schedule CPU-heavy work off the event loop; cron without a sidecar WebSockets + SSE Live updates for browsers and phones alike Frontend Stack Why TanStack Start + React 19 File-based routes and SSR without framework magic taking over — explicit, portable, and easy for AI tools to reason about. No lock-in, no deploy surprises TanStack Query + Zustand 5 Server state with caching; client state without boilerplate Tailwind 4 + shadcn + Base UI Utility styling plus accessible primitives, themed by tokens react-hook-form + zodResolver Forms validated by the same schemas as the API react-i18next + date-fns Every string and date localized, no hardcoding Vitest + Playwright Fast unit tests plus real browser journeys srvx + nginx Lean SSR server in front of a production-grade proxy Mobile Stack Why Expo 57 + expo-router Real native apps, file-based routes like the web NativeWind + Tailwind 3 Same design tokens, native rendering SecureStore + expo-notifications Hardware-backed secrets; push that actually arrives Same api-client + contracts One API language across web, mobile, and server Platform Stack Why Turborepo + pnpm 10 + TypeScript 6 Fast monorepo builds, one lockfile, strict types everywhere Docker + MinIO + Mailpit + pgAdmin Prod-like local infra: S3, inbox, and DB GUI included Prometheus + Loki + Jaeger + Grafana Metrics, logs, and traces locally before you need them in prod Husky + commitlint + Changesets Clean commits and versioned releases by default GitHub Actions CI/CD Typecheck, lint, tests, architecture rules — enforced, not suggested Nothing trendy-for-trendy's-sake. Everything chosen to make the next five years easier, not just the next five minutes. The developer experience is the feature pnpm bootstrap → deps, env, Docker services, migrations, build. Then pnpm dev. That's it. (An optional idempotent seed creates your first admin.) pnpm project:init rebrands the whole starter — name, slug, app titles, bundle IDs — so your fork stops looking like a template in minutes, with a dry-run plan before it touches anything. pnpm generate:feature scaffolds a full vertical slice: contracts, backend module, API client, web + mobile UI. A new feature stops being a two-day wiring exercise. Local infra with one command each: Postgres, Redis, MinIO, Mailpit (a fake inbox!), pgAdmin — plus Grafana, Prometheus, Loki, and Jaeger when you want to feel like a platform team. pnpm rules:check enforces architecture as code: file placement, no stray fetch, locale parity across languages, co-located tests for every data module. The rules bite — I know, they've bitten me. Production is planned, not improvised: nginx proxy, workers, and migration runners in compose, pg_dump backups with verified restores, secrets via _FILE mounts, traces and logs wired from the start. Testing without the lecture API: 119 Vitest files (unit + real-Postgres integration + e2e) Web: 87 unit and component tests + Playwright journeys Mobile: 91 logic tests with native modules mocked at one seam Coverage gates start low and ratchet up. No ceremony, no 3-hour suites. Built for the AI-assisted era 🤖 Here's the part I'm most excited about. The repo has an ai_instructions/ folder — mandatory, machine-readable architecture laws: locked stack, where every file belongs, how errors, i18n, and tests work. Combined with small single-responsibility files and end-to-end types, AI assistants stop hallucinating random patterns and start writing code that fits the codebase on the first try. It turned my own workflow from "review every line suspiciously" into "review, nod, merge." The feature generator plus strict contracts plus enforced rules basically function as guardrails for robot coworkers. How it makes life simpler, concretely Last week I needed a full feature with API, web UI, mobile UI, permissions, translations in three languages, and tests. Old me: two days. With this starter: scaffold the slice, fill in the business logic, run the gate (typecheck, lint, test:unit, rules:check, format:check, build), commit. The checklist does the worrying. It's still cooking — come help 👨🍳 An honest note: this is an active, early-stage project. Mobile component tests are now in place, coverage keeps ratcheting, docs keep growing — and next up is a Python-based intelligence layer, so AI features live in a dedicated service beside the NestJS API. There will still be rough edges and bugs we haven't met. But everything you need to start a serious project today is already here and wired. Get the code in 2 minutes git clone https://github.com/ihssmaheel-dev/modular-monolith-starter.git cd modular-monolith-starter pnpm bootstrap pnpm dev That's the whole install. If it helps you, a star on GitHub is the cheapest way to say thanks — and it helps other developers find it too. ⭐ If you try it and something breaks, confuses you, or just smells wrong — please open an issue on GitHub or drop it in the comments. Feature requests doubly welcome. The fastest way to make a starter great is people actually using it and complaining loudly. 🙂 Happy building — and may your monolith stay modular.
This is a summary aggregated from Dev.to. Read the complete article on the original site:
Read full article at Dev.to