An architecture lint that fails the build
Aisolver (package taskwise-v2) is a rebuilt task/list manager — lists, nested
tasks, groups, drag-and-drop, a calendar, alarms, a trash bin, admin, and
invite-code registration. For a "just a to-do app," it carries a surprisingly
grown-up spine.
Boundaries you can't argue with
The centrepiece is discipline: an arch-check.ts lint that enforces the
intended module boundaries and fails the build when one is crossed. That's
the whole trick. "Please don't import the database from the UI layer" is a
code-review plea that erodes under deadline pressure; a script that turns the
same rule into a red CI check does not get tired, does not get talked out of it,
and does not forget.
The shape
It's a pnpm monorepo:
- web — React 19 + Vite + TypeScript + Tailwind v4,
- api — Fastify 5 +
node-pg+ Zod, - db — PostgreSQL 17,
- with WebSockets for live updates.
It ships real operational docs too — an ARCHITECTURE.md and a RUNBOOK.md —
so the boundaries the lint enforces are also written down for a human.
Why it matters on a small project
Architecture rot doesn't wait for scale; it starts on day two of any codebase with layers. Encoding the layering as an executable check is how a small tool stays refactorable — the structure defends itself instead of relying on everyone remembering the plan.