← BackArchitecture

The decisions worth talking about

Every pattern in this codebase has a rationale. These are the answers to the “why” questions — context, decision, tradeoffs, and the code that resulted.

Frontend architecture

Feature-Sliced Design (lite)

The layered model and one-way dependency rules from FSD, without the full specification overhead.

Transport layer

Next.js server actions via next-safe-action

Composable middleware, schema validation, and a single catch boundary via next-safe-action.

System design

Modular monolith over microservices

Service boundaries drawn too early are a bet on requirements you don't have yet.

Domain layer

Domain-Driven Design (lite)

A finance domain has real invariants worth modelling explicitly. A todo app wouldn't justify this.

Application layer

CQRS with a typed Command Bus

Commands and queries are separate concerns. The bus makes dispatch type-safe without boilerplate.

System design

CQRS read model

A separate read model is justified. A separate database is not, at least not yet.

Infrastructure

In-process event bus

Domain events decouple what happened from what happens next. The interface hides the delivery mechanism.

Infrastructure

Persist-first event dispatch with async processing

Every event hits Postgres before any handler runs. QStash handles async delivery.

Infrastructure

Event handler ordering

Registration order is implicit coupling. It works because processing is sequential. An explicit event chain would make the dependency visible.

Security

JWT authentication with refresh flow

Short-lived JWTs carry only userId. A session ID cookie enables token refresh without re-authentication.

Security

TOTP-based multi-factor authentication

Two-step login with challenge tokens, aggregate-owned events, and service-layer signing. No auth logic in handlers.

Infrastructure

Feature flags

Tier-gated, cache-aside, zero vendor. A database table and a Redis cache replace a SaaS subscription.

Infrastructure

OpenTelemetry + Grafana Cloud

Open standards for instrumentation. The backend is a config swap, not a rewrite.

Infrastructure

Two-tier health checks

A shallow probe keeps the orchestrator happy. A scheduled deep check catches silent dependency failures before users do.