Software Architect · Module 06
Layers earn their keep when they protect the important logic from the details: HTTP, databases, queues, SDKs, and the current framework.
Clean Architecture · dependency inversion · ports and adapters
Business rules should depend on the language of the domain, not on how a request was delivered.
The details belong on the outside
The foundation of a house can't depend on the colour of the curtains. The curtains you can change. The foundation, hardly ever.
Clean Architecture, Hexagonal Architecture, and Onion Architecture describe one idea: the centre of the system holds domain rules and use cases, the outer layers hold details. The REST controller, the SQL query, the Redis cache, the Stripe SDK, the message broker — important details, but not the source of business rules.
The Dependency Inversion Principle keeps the arrows pointing the right way: high-level policy depends on abstractions, and implementations plug in from outside. In practice that looks like a PaymentGateway port and a StripePaymentGateway adapter.
A layer has to do work
An empty corridor between two rooms is useful if people walk through it. If it just adds distance, it's wasted floor space.
A layer is justified when it gives isolation, testability, or a separate rate of change. The application layer coordinates a use case. The domain layer holds the rules and invariants. The infrastructure layer talks to the outside world.
A layer that just shuffles parameters from one method into another isn't architecture, it's ceremony. The architect has to defend the system from both: missing boundaries and meaningless layering.
Testability shows you fast whether the dependencies point the right way.
Example: creating an order without a database in a unit test
To check a recipe, you don't open a restaurant. A kitchen and the ingredients are enough.
The CreateOrder use case receives InventoryPort, PaymentPort, and OrderRepository as dependencies. In a unit test they're swapped for fakes. The test checks one rule: an order can't be created without a fixed price and a successful payment intent.
The database and HTTP aren't part of this test. That means the business rule is separated from the infrastructure.
Anti-example: Active Record as the centre of the universe
If all the logic lives in the toolbox, every repair starts with hunting for the right screwdriver.
The ORM model validates business rules, makes network calls, sends email, writes the audit log, and decides permissions. It feels convenient while the system is small. Later, every test boots a database, every change drags side effects, and the domain becomes impossible to reuse.
The problem isn't the ORM as such. The problem is that the infrastructure model ended up owning the business policy.
- Can the core rule be verified without a database and without HTTP? - Which dependencies flow inward, which flow outward? - Where does the framework dictate the domain model? - Which adapters can be swapped without touching the use case?