Backend

The backend is a NestJS application structured around Domain-Driven Design, CQRS and hexagonal architecture. The goal is a codebase where every piece has an obvious home, business logic is isolated from frameworks and I/O, and the important rules are enforced automatically rather than left to discipline.

The shape of a feature

Adding a behaviour to the system almost always means the same handful of files, in the same places:

  1. A value object or change to an aggregate in domain/ — the business rule itself.
  2. A command (write) or query (read) in application/, plus its handler.
  3. A controller method in interfaces/ that builds the command/query and dispatches it on the bus.
  4. Optionally, a domain-event handler that emits an integration event to the outbox for other contexts to react to.

Because the structure is so regular, you — or an AI agent — can navigate any context by analogy to any other.

The pillars

  • Domain-Driven Design — aggregates encapsulate state and rules; value objects make illegal states unrepresentable; domain events record what happened.
  • CQRS — commands and queries are separate, each with a base handler that adds authorize → validate → handle, plus logging, tracing and metrics for free.
  • Event-Driven Architecture — the transactional outbox and idempotent inbox give reliable, exactly-once messaging over Kafka.
  • Repositories — the domain defines a repository interface (a port); infrastructure provides the MongoDB adapter.
  • Authentication — JWT access/refresh tokens, a guard that also checks revocation, and role-based access.
  • Enforced Boundaries — custom ESLint rules that fail the build when an architectural rule is broken.

Why this much structure?

It pays off in three ways:

  • Legibility. Predictable layout and naming mean less time spent finding things and more spent changing them — for humans and for coding agents alike.
  • Safety. The layering and the lint rules stop accidental coupling (domain reaching into infrastructure, one context importing another's internals) before it ships.
  • Evolvability. Bounded contexts that communicate only through events can be split into separate services later with little ceremony.

Start with Domain-Driven Design.