Hasina Razafintsalama

Hasina RAZAFINTSALAMA

← Back to Blog
Architecture

Clean Architecture: Building Software That Outlives Its Framework

Clean Architecture is not about folders or diagrams,it is about one rule: dependencies point inward. Here is what that actually buys you in a real codebase.

2026-07-10·8 min

Robert C. Martin's Clean Architecture popularized a diagram of concentric circles, but the diagram is not the point. The point is one rule that, applied consistently, keeps your business logic usable long after the framework you built it in has been replaced.

The dependency rule

Source code dependencies can only point inward. Outer layers,web framework, database, UI,depend on inner layers: use cases and entities. The reverse is never allowed. A use case must not import an Eloquent model, a Symfony controller, or an HTTP client directly.

The layers, from the inside out

  • Entities: enterprise-wide business rules that would exist even without this specific application,framework-agnostic, no imports outside plain language code.
  • Use Cases: application-specific business rules that orchestrate entities to fulfill a request,still no framework dependency.
  • Interface Adapters: controllers, presenters, and gateways that convert data between the use case format and whatever the outer layer needs (HTTP, SQL, JSON).
  • Frameworks & Drivers: the database, the web framework, the UI,the most volatile layer, and the one that should be easiest to replace.

A concrete example: decoupling a use case from Laravel

A use case should depend on an interface, not on Eloquent. The concrete implementation lives in the outer layer and gets bound at runtime.

php
// Use case: pure PHP, no Eloquent, no framework imports
final class PlaceOrder
{
    public function __construct(
        private OrderRepository $orders, // interface, not Eloquent
        private PaymentGateway $payments, // interface
    ) {}

    public function execute(PlaceOrderRequest $request): Order
    {
        $order = Order::create($request->items, $request->customerId);
        $this->payments->charge($order->total(), $request->paymentToken);
        $this->orders->save($order);

        return $order;
    }
}
php
// Domain layer: defines the contract
interface OrderRepository
{
    public function save(Order $order): void;
    public function find(string $id): ?Order;
}

// Infrastructure layer: implements it with Eloquent
final class EloquentOrderRepository implements OrderRepository
{
    public function save(Order $order): void
    {
        OrderModel::updateOrCreate(['id' => $order->id()], $order->toArray());
    }

    public function find(string $id): ?Order
    {
        $model = OrderModel::find($id);
        return $model ? Order::fromModel($model) : null;
    }
}

// Bound in the service container, never referenced directly by the use case
$this->app->bind(OrderRepository::class, EloquentOrderRepository::class);

What you gain, and what it costs

  • Testability: the use case can be tested with an in-memory repository, no database or HTTP server required.
  • Swappable infrastructure: change ORM, database, or even framework without touching a single business rule.
  • A real cost: more files, more indirection, and a learning curve for the team.

Do not apply Clean Architecture to every project. It pays off when business logic is complex, long-lived, or needs to be tested and swapped independently of infrastructure. A basic CRUD admin panel does not need four layers and an interface for every repository.

Even without adopting the full folder structure, the one rule worth keeping is simple: do not let framework classes leak into your business logic. That single discipline prevents most of the pain Clean Architecture is designed to solve.

Need help with this topic? Full Stack Development

Discover this service