The Architecture I Keep Coming Back To

Twenty-five years of backend services. I've shipped MVC, MVVM, full DDD, hexagonal, clean architecture. All of them work. None of them are wrong.

But when I start something new, I keep defaulting to the same structure. SOA with a thin application layer and domain modules. Not because it's trendy. Because it's the least amount of architecture that still holds up two years later when someone else is maintaining it.

Here's what that looks like, whether I'm using Nest.js, Django, Laravel, Node with Express:

Application Layer

The API surface. Routes, controllers, resources. It receives requests and returns responses. It does not think. All the business logic and rules lives elsewhere, injected in as services or DTOs. This is the receptionist. It greets, it routes, it does not perform surgery.

Modules

Where the domain lives. Each module is a vertical slice of business capability. One module owns its DTOs, models, repositories, contracts, services, events, handlers. Everything you need to understand that capability lives in one place. You should be able to delete a module and have the compiler tell you exactly what broke. That's how you know the boundaries are real.

Dependencies between modules stay limited to DTOs, services, and then of course domain events. Nothing else crosses. This is the load-bearing wall.

I still borrow from DDD. Aggregate roots when invariants matter. Rich models when logic starts spreading. Ubiquitous language when the team gets big enough that words stop meaning the same thing.

The goal isn't pattern purity. It's a codebase that doesn't fight you.

❤️
Jake

SONG