iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
You can use hexagonal architecture in Laravel by keeping use cases and domain rules independent of Laravel, defining ports only for meaningful application needs, and using Laravel service providers to connect those ports to infrastructure adapters. Laravel-specific controllers, Eloquent repositories, and provider bindings remain framework-dependent; the core does not have to be.
What hexagonal architecture means in a Laravel application
Hexagonal Architecture, also called Ports and Adapters, separates an application’s inside from its outside. A port describes an interaction the application needs or offers; an adapter translates a particular technology’s communication into that interaction. The hexagon is a diagrammatic convention, not a requirement for six ports or six layers. In his 2005 paper, Alistair Cockburn describes the goal as allowing an application to be driven by users, programs, automated tests, or batch scripts, and to be developed and tested apart from its eventual devices and databases. Read Cockburn’s original article.
In Laravel, the boundary is an architectural choice, not something the framework imposes. HTTP controllers, console commands, queue handlers, and scheduled entry points can act as inbound adapters. They translate framework-specific input into an application-level request and invoke a use case. The core contains application behavior and domain rules. Outbound adapters connect that core to infrastructure such as a database, mail service, queue, filesystem, or external API.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →How to keep Laravel out of the core
When framework independence matters, keep Laravel request objects, Eloquent models, facades, and vendor-specific types out of domain and use-case signatures. The core should express its own concepts and depend on capabilities it needs, rather than on how Laravel or a vendor provides them. This applies Cockburn’s inside/outside separation; it is not a Laravel requirement.
#1 Best Overall
For example, an order use case can accept an application-level command and depend on an application-owned OrderStore port. An Eloquent-backed adapter can implement that port and translate between the application’s order representation and persistence. The application service need not know that the adapter uses Eloquent.
This boundary is valuable when it isolates meaningful variability: a different storage mechanism, a useful test double, or a core that must remain usable outside Laravel. It is usually not valuable to create an interface for every class merely to mirror a single implementation. An abstraction that adds a file and binding without clarifying a responsibility, protecting a boundary, or enabling a real substitution is indirection without much architectural benefit.
Where Laravel’s container and service providers fit
Laravel’s service container resolves concrete classes automatically when they have no dependencies or only concrete-class dependencies. Explicit bindings are useful when a consumer type-hints an interface or when you need to select a particular implementation. Laravel’s 13.x documentation also describes supplying mock implementations for tests and contextual bindings for cases where different consumers need different implementations. See the Laravel 13.x service container documentation.
Service providers are the composition point for application services. Put container bindings in a provider’s register method; Laravel advises that this method should be used for bindings. In Laravel 13.x, user-defined providers are registered in bootstrap/providers.php. Consult the Laravel 13.x service provider documentation and check the Laravel major version installed in your project before copying paths or APIs.
Rank #3
Illustrative port and adapter wiring
The following sketch shows the roles; it is an example of the pattern, not a claim about executed code.
interface OrderStore
{
public function save(Order $order): void;
}
final class PlaceOrder
{
public function __construct(private OrderStore $orders) {}
public function handle(PlaceOrderCommand $command): void
{
$order = Order::place($command);
$this->orders->save($order);
}
}
final class EloquentOrderStore implements OrderStore
{
public function save(Order $order): void
{
// Translate the application order to persistence operations.
}
}
In a Laravel service provider, the interface-to-adapter mapping is explicit:
Rank #4
public function register(): void
{
$this->app->bind(OrderStore::class, EloquentOrderStore::class);
}
A controller can receive PlaceOrder through constructor injection or a method parameter, translate the HTTP input into a command, and invoke the use case. Laravel documents container injection for controllers, event listeners, middleware, queued jobs, and route closures. The core can be tested with a fake or mock OrderStore; the Laravel-facing adapter can be tested separately against its integration responsibilities.
Free tools Windows power users keep installed
One-click scans. No signup required.
Should you use Laravel contracts, facades, or concrete injection?
There is no blanket rule that all Laravel applications must avoid facades or that every dependency needs an interface. Laravel documents contracts and facades as supported options and says the choice is often a matter of team preference. The useful question is whether a dependency belongs to the core’s own boundary or is simply a Laravel service used by framework-facing code. See Laravel’s 13.x contracts documentation.
Best Value
| Choice | Best fit | Boundary and trade-off |
|---|---|---|
| Application-owned port | A capability the application needs, such as storing an order or sending a notification, where isolation, substitution, or testing has real value. | The application owns the abstraction, so the core can remain independent of Laravel. It does require an adapter and usually an explicit binding. |
| Laravel contract | A dependency on a Laravel service, or a package integration point intended to work with framework services. | It makes the Laravel relationship explicit, but a Laravel contract in a core signature means that core is not framework-agnostic. |
| Facade | Convenient use in Laravel-facing code where the team accepts the framework dependency. | Laravel supports facades; they are not inherently incompatible with robust, testable applications. For a strict core boundary, keep facade calls in adapters rather than business rules. |
| Concrete injection | A concrete class with no dependency ambiguity or a chain of concrete dependencies Laravel can resolve. | Usually the simplest option when there is no meaningful substitution or boundary to express; Laravel can resolve many such classes without an explicit binding. |
These approaches can coexist. A project might use application-owned ports for a few infrastructure boundaries, Laravel contracts when it deliberately relies on framework services, facades in controllers or adapters, and ordinary concrete injection for internal services. Use contextual bindings when genuinely different consumers need different implementations of one interface; if the consumers have distinct application needs, clearer port names may communicate the design better.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.What “framework agnostic” does—and does not—promise
The strongest framework-agnostic claim applies to the core and the ports it owns: their behavior and signatures need not rely on Laravel. Laravel-specific adapters, controller entry points, and service-provider composition remain tied to Laravel. Replacing Laravel would therefore mean replacing or rewiring those outer parts, not expecting the entire application to be portable without changes.
The right amount of separation depends on the project. If a core is intentionally small and the team values Laravel’s direct ergonomics over portability, using framework services in application code may be a reasonable choice. If the business rules need to be isolated, tested independently, or reused through different entry points, make that boundary explicit and keep framework types on the outside.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

