Skip to main content

A DDD Architecture in Laravel — The 4-Layer Structure and Dependency Inversion

From here, as Part 2, we explain how to implement the DDD concepts you learned in Part 1 with Laravel.

For those who want to review the basics of Laravel

From this chapter on, Laravel-specific features (controllers, Eloquent, service providers, and so on) appear. Their basics are explained in Chapter 2, "Basics of Laravel API development", so refer to it as needed.

A comparison of architectures

The traditional Laravel structure

Business logic is scattered across the controller and the model.

DDD + clean architecture

Clean architecture is a design philosophy proposed by Robert C. Martin (commonly known as "Uncle Bob"). It puts domain logic at the center and aims to make it independent of external technologies such as frameworks and databases.

What is clean architecture?

It is an architectural pattern proposed in Robert C. Martin's book "Clean Architecture." The essential idea is to put business logic (the domain layer) at the center and place the technical details (framework, DB, UI) on the outside. This keeps business rules unaffected by changes in technology and lets you build a system that is maintainable in the long term.

Reference: The Clean Architecture

About the terminology (Clean Architecture and Onion Architecture)

The 4-layer structure this book adopts (placing the domain at the center) is, strictly speaking, closer to Jeffrey Palermo's Onion Architecture. Robert C. Martin's Clean Architecture is originally expressed as four concentric rings (Entities / Use Cases / Interface Adapters / Frameworks & Drivers). Both share the core idea of "pointing dependencies toward the inside (the domain)." In practice they are often referred to collectively as "clean architecture" without distinction, so this book follows that convention.

The responsibilities of each layer

Each layer has a clearly separated role, and this separation localizes the scope of changes and enables testable code.

LayerResponsibilityWhat it containsWhy it matters
Presentation layerHandling HTTP requests/responsesController, Request, ResourceResilient to changes in input format. A change in the interface, such as migrating from a REST API to GraphQL, does not affect the domain
Application layerCoordinating use casesUseCase, CommandClarifies the business flow. You can take an overall view of the business process and coordinate multiple domain objects
Domain layerExpressing business rulesEntity, Value Object, Repository InterfaceProtects business logic. Completely independent of technical details, holding only pure business rules
Infrastructure layerTechnical implementationRepository Implementation, Eloquent ModelFlexibility in technology choices. A change in persistence method, such as migrating from Eloquent to Doctrine ORM, does not affect the domain

Why this separation matters

  1. Limiting the scope of changes

    • UI changes do not affect the business logic
    • Database changes do not affect the business logic
    • Framework changes do not affect the business logic
  2. Improved testability

    • The domain layer can be tested without external dependencies
    • Fast unit tests using mocks
    • Testing of business logic that needs no database
  3. Easy-to-understand code

    • Each layer's responsibility is clear, so you know where to write what
    • Business logic is collected in the domain layer
    • Even new members can develop without confusion

The direction of dependencies

The dependency inversion principle (DIP)

Important: the infrastructure layer depends on the domain layer (the dependency inversion principle). The domain layer never depends on the infrastructure layer.

What is the dependency inversion principle (DIP)?

It is the "D" of the SOLID principles, an important principle of object-oriented design.

What the principle says:

  1. High-level modules (the domain layer) must not depend on low-level modules (the infrastructure layer). Both should depend on abstractions (interfaces)
  2. Abstractions must not depend on details. Details (implementations) should depend on abstractions

In plain terms: It means "depend on interfaces (contracts), not on implementations."

Why does the infrastructure layer depend on the domain layer?

In a normal layered structure, dependencies go "from top to bottom" (Controller → Service → Repository implementation). But clean architecture puts the domain layer at the center, so it "inverts" the infrastructure layer's dependency.

Understanding with a concrete example

// Domain layer: define an interface (the abstraction)
namespace App\Domain\Order;

interface OrderRepositoryInterface
{
public function save(Order $order): void;
public function findById(OrderId $id): ?Order;
}
// Infrastructure layer: implement the interface (the detail)
namespace App\Infrastructure\Repository;

use App\Domain\Order\OrderRepositoryInterface; // ← depends on the domain layer

final class EloquentOrderRepository implements OrderRepositoryInterface
{
public function save(Order $order): void
{
// An implementation using Eloquent
}
}
// Domain layer: the entity
namespace App\Domain\Order;

// The domain layer knows only the "interface"
// It does not know the existence of Eloquent (it does not depend on it)
final class Order
{
// Business logic
}

The benefits of this design

  1. The domain layer is independent of technical details

    // Testing the domain layer: testable quickly without a DB
    $order = Order::create($orderId, $address);
    $order->addItem(...);
    // Test only the business logic
  2. Easy to swap implementations

    // Even if you change from Eloquent to another ORM, the domain layer is unchanged
    final class DoctrineOrderRepository implements OrderRepositoryInterface
    {
    // An implementation using Doctrine ORM
    }
  3. The flow of dependencies The traditional design (bad example) — the business logic strongly depends on Eloquent.

After applying DIP (good example) — the domain is completely independent of the infrastructure.

The directory layout

app/
├── Domain/ # Domain layer
│ ├── Order/
│ │ ├── Order.php # Aggregate root (entity)
│ │ ├── OrderId.php # Identifier (value object)
│ │ ├── OrderLine.php # Entity
│ │ ├── OrderLineId.php # Identifier (value object)
│ │ ├── OrderStatus.php # Enum
│ │ ├── ProductId.php # Identifier (value object)
│ │ ├── ShippingAddress.php # Value object
│ │ └── OrderRepositoryInterface.php # Repository interface
│ │
│ └── Shared/
│ └── Money.php # Shared value object

├── Application/ # Application layer
│ └── UseCase/
│ └── Order/
│ ├── CreateOrderUseCase.php
│ ├── CreateOrderCommand.php
│ └── ConfirmOrderUseCase.php

├── Infrastructure/ # Infrastructure layer
│ ├── Eloquent/
│ │ ├── OrderModel.php # Eloquent Model
│ │ └── OrderLineModel.php # Eloquent Model
│ ├── Repository/
│ │ └── EloquentOrderRepository.php
│ └── Provider/
│ └── RepositoryServiceProvider.php

└── Http/ # Presentation layer
├── Controllers/
│ └── OrderController.php
└── Requests/
└── CreateOrderRequest.php

The knowledge and responsibilities of each layer

LayerWhat it knowsWhat it does not know
ControllerThe HTTP request/response formatBusiness rules, table structure
UseCaseHow to use domain objectsThe HTTP request format, table structure
Domain EntityBusiness rulesThe persistence method, the framework
RepositoryThe table structure, EloquentThe HTTP request format

The difference between an Eloquent model and a domain entity

CharacteristicDomain EntityEloquent Model
ResponsibilityExpressing business rulesMapping to the database
Business rulesHolds themDoes not hold them
DB operationsDoes not hold themHolds them (save, find, etc.)
TestabilityHigh (testable without a DB)Low (needs a DB)

Why separate them?

  1. Separation of concerns: separate business logic from persistence logic
  2. Testability: the domain entity can be tested without a DB
  3. Flexibility: changing the persistence method does not affect the domain
  4. Clarifying business rules: business rules are collected in the domain entity

Further reading

If you want to learn more about clean architecture and DDD, see the following resources.

Clean architecture

  • Robert C. Martin, "Clean Architecture" (book)
    • The original text of clean architecture. You can comprehensively learn the principles of architectural design.
  • The Clean Architecture (article)
    • Uncle Bob's article explaining clean architecture

The SOLID principles

  • The dependency inversion principle (DIP) is the "D" of the SOLID principles
  • Understanding the SOLID principles is essential for understanding clean architecture

In the next chapter, we look in detail at the design of the application layer (the use case layer).