Software Architecture
The Danger of Framework-Driven Development
Many software projects start as a manageable, fast-itching project built on a popular framework (such as Laravel, Spring Boot, or Django). As the application grows and years pass, something insidious happens: the core of the application—the unique business logic that creates value—becomes inextricably intertwined with the database ORM, the HTTP controllers, and external API clients. When a company decides after five years to switch from a MySQL database to MongoDB, or from a REST API to a GraphQL API, this proves to be a virtually impossible, costly rewrite operation. This is known as ‘vendor lock-in’ caused by framework entanglement.
To build applications that remain maintainable for decades, senior architects fall back on design patterns such as Clean Architecture (Robert C. Martin) or the Hexagonal Architecture (Ports and Adapters by Alistair Cockburn). These architectures share one fundamental goal: isolating the beating heart of the application.
Dependency Inversion and Inner Circles
In a traditional ‘layered’ architecture, the business layer (the business logic) depends directly on the data layer (the database). This means that changes in the database directly break the core logic. Clean Architecture reverses this via the ‘Dependency Inversion Principle’.
You structure the application like an onion, with several concentric circles. The core circle contains exclusively your ‘Entities’ (the domain model) and ‘Use Cases’ (the actions your application can perform). The golden rule of Clean Architecture states: Dependencies always point inward. The inner layers must have absolutely no notion of the outer layers. A ‘Use Case’ for processing a payment knows in the code absolutely not whether it is processing an HTTP request, a CLI command, or a Kafka event, and it knows even less whether the payment data is stored in PostgreSQL, Firebase, or in memory.
Ports and Adapters (Hexagonal Architecture)
How do you store data, then, if the core must know nothing about databases? This is where the Hexagonal Architecture introduces ‘Ports’ and ‘Adapters’. A Port is simply an abstract Interface (for example IPaymentRepository) defined in the inner core. The core dictates: “I need something that can satisfy this interface.”
On the outside of the application (the infrastructure layer), the developer writes an ‘Adapter’. This is the actual, ugly code that communicates with the external world, for example a PostgresPaymentAdapter who the IPaymentRepository implements interface. Via Dependency Injection, the adapter is injected into the Use Case during the startup of the application. This makes the architecture extremely flexible (pluggable).
The Profit for Test-Driven Development (TDD)
You will notice the greatest and most direct benefit of this extreme decoupling in your test suite. Unit testing of business logic in a traditional application often requires complex mock databases, network stubs, and web server emulation, making tests slow and brittle (flaky). In Clean Architecture, you can InMemoryPaymentAdapter write it, inject it into your Use Case, and test your entire business logic within a fraction of a millisecond in complete isolation (without a database or server).
Although Clean Architecture initially requires a lot of abstraction and boilerplate code (and is therefore too heavy for small projects), it is the ultimate architecture for critical enterprise systems. Read more about architecture considerations in this guide at AG Connect.
Next: Test-Driven Development (TDD) and E2E Automation with Playwright
