Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Hexagonal Maker Bundle Laravel Package

ahmed-bhs/hexagonal-maker-bundle

View on GitHub
Deep Wiki
Context7

layout: default

Why Hexagonal Architecture

Table of Contents


2.1 The Fundamental Question

Everything is coupled anyway, so why bother?

Your code will always call other code. Repositories, services, databases—everything is connected. The question is not about eliminating coupling (impossible), but about controlling the direction of coupling.


2.2 Traditional Layered Architecture: The Critical Problems

In a traditional layered architecture, business logic (Services) depends directly on infrastructure (Database, Framework, Libraries). This creates a dangerous dependency chain with the following critical problems:

2.2.1 Framework Prison

  • Business logic is tightly coupled to Doctrine, Symfony, or any framework
  • Switching from MySQL to MongoDB requires rewriting everything
  • Framework update breaking APIs stops the entire application
  • Real cost: Weeks of rewriting for technology changes

2.2.2 Testing Complexity

  • Every test requires a real database, containers, and fixtures
  • Test suite takes 10 minutes instead of 10 seconds
  • Cannot test business rules without booting the entire framework
  • Real cost: Developers wait, lose focus, tests get skipped

2.2.3 Lost Business Rules

  • Rules scattered across controllers, services, entities, templates
  • Question: "Can we cancel a shipped order?" requires searching multiple locations
  • Same rule duplicated in 5 different places with slight variations
  • Real cost: Bugs, inconsistencies, impossible maintenance

2.2.4 Cannot Evolve

  • Adding GraphQL API requires duplicating all logic
  • Adding CLI commands requires copy-pasting controllers
  • Planning gRPC requires starting from scratch
  • Real cost: Code duplication, diverging implementations

2.2.5 Death by Thousand Cuts

  • "Just one quick change" touches 20 files
  • Simple features become 3-day tasks
  • Fear of refactoring because everything is interconnected
  • Real cost: Technical debt accumulates until rewrite is the only option

2.3 Hexagonal Architecture: Inverting the Dependency Chain

Core Principle: Make business logic independent by inverting who depends on whom.

2.3.0 The Laptop Analogy 💻🔌

Before diving into technical details, let's use a simple, everyday analogy:

Your Laptop and USB Devices

Think about your laptop and how it connects to external devices:

  • Your laptop is the core (business logic)
  • Your laptop has USB ports (interfaces/contracts)
  • You can plug in different devices (adapters):
    • USB mouse
    • USB keyboard
    • USB external hard drive
    • USB printer
    • USB phone charger

Key Insight: Your laptop doesn't care WHAT you plug in, as long as it respects the USB standard (interface).

What if USB didn't exist?

  • Every laptop would need a specific Sony mouse port, HP keyboard port, Samsung phone port
  • Want to change your mouse brand? Buy a new laptop!
  • This is exactly how traditional layered architecture works 🌪️

With USB (Ports & Adapters):

  • Your laptop defines: "I need a USB port"
  • Devices provide: "I implement USB"
  • Change devices anytime without changing the laptop
  • This is Hexagonal Architecture 🎯

Mapping to Software:

Real World Software
💻 Laptop Domain (Business Logic)
🔌 USB Port Port Interface (Contract)
🖱️ USB Mouse Doctrine Adapter
⌨️ USB Keyboard MongoDB Adapter
🖨️ USB Printer Redis Adapter
📱 USB Phone InMemory Adapter (for tests)

The Power:

  • Your laptop (domain) doesn't know if a mouse, keyboard, or hard drive is plugged in
  • It just knows: "Something respecting USB is connected"
  • Your domain doesn't know if MySQL, MongoDB, or Redis is used
  • It just knows: "Something respecting UserRepositoryInterface is connected"

Real Example in Code:

// 🌪️ Traditional: Domain depends on concrete MySQL
class OrderService {
    public function __construct(
        private DoctrineRepository $repo  // Tightly coupled!
    ) {}
}
// Problem: Want MongoDB? Rewrite OrderService!

// 🎯 Hexagonal: Domain depends on interface (USB port)
class PlaceOrderHandler {
    public function __construct(
        private OrderRepositoryInterface $repo  // Just a port!
    ) {}
}
// Solution: Want MongoDB? Create MongoOrderRepository implementing the interface!

Why This Matters:

Just like you can use your mouse on any laptop (Windows, Mac, Linux) because they all have USB ports, your business logic works with any database (MySQL, MongoDB, Redis) because they all implement your port interfaces.

You control the "USB standard" (interface), not the device manufacturers (infrastructure libraries).


2.3.1 Dependency Direction

Traditional (Dependencies flow DOWN):

%%{init: {'theme':'base', 'themeVariables': { 'fontSize':'16px'}}}%%
graph TD
    A["🎮 Controllers<br/><small>UI Layer</small>"]
    B["⚙️ Services<br/><small>Business Logic</small>"]
    C["💾 Infrastructure<br/><small>Doctrine/Database</small>"]

    A ==>|"🌪️ depends on"| B
    B ==>|"🌪️ depends on"| C

    style A fill:#E3F2FD,stroke:#1976D2,stroke-width:3px,color:#000
    style B fill:#FFF9C4,stroke:#F57C00,stroke-width:3px,color:#000
    style C fill:#FFCDD2,stroke:#C62828,stroke-width:3px,color:#000

    classDef problemArrow stroke:#C62828,stroke-width:3px

Problem: Change database = rewrite business logic 🌪️

Hexagonal (Dependencies flow INWARD):

%%{init: {'theme':'base', 'themeVariables': { 'fontSize':'16px'}}}%%
graph BT
    C["🔌 Infrastructure<br/><small>Doctrine/Database</small>"]
    B["🔗 Ports<br/><small>Interfaces</small>"]
    A["💎 Domain<br/><small>Business Logic - CORE</small>"]

    C -.->|"🎯 implements"| B
    B ==>|"🎯 defined by"| A

    style A fill:#C8E6C9,stroke:#2E7D32,stroke-width:4px,color:#000,rx:10,ry:10
    style B fill:#FFF9C4,stroke:#F9A825,stroke-width:3px,color:#000
    style C fill:#F8BBD0,stroke:#C2185B,stroke-width:3px,color:#000

Solution: Change database = new adapter, business logic untouched 🎯

2.3.2 Practical Implications

Traditional: Order entity has Doctrine annotations. Remove Doctrine? Domain breaks.

  • 🔴 Like having a laptop with a hardwired Sony mouse - want a Logitech? Buy a new laptop!

Hexagonal: Order is pure PHP with business rules. Infrastructure adapts to it. Remove Doctrine? Create a new adapter.

  • 🟢 Like having a laptop with USB ports - want a different mouse? Just plug it in!

Key insight: Business logic doesn't know (and doesn't care) if data is stored in MySQL, MongoDB, Redis, or a text file. It defines WHAT it needs (interfaces/ports), and infrastructure provides HOW (adapters).

Back to the Laptop Analogy:

  • Your laptop doesn't have "Logitech mouse code" or "HP printer code" inside
  • It has generic USB port logic: "Accept anything that implements USB"
  • Similarly, your domain doesn't have "Doctrine code" or "MongoDB code" inside
  • It has generic port logic: "Accept anything that implements OrderRepositoryInterface"

The Freedom This Gives You:

  • 🔄 Switch databases like swapping USB devices
  • 🧪 Test with in-memory storage (like testing laptop without plugging real devices)
  • 🚀 Deploy with different storage per environment (dev/staging/prod)
  • 🔌 Add new storage types without touching business logic

2.4 Real-World Scenarios

2.4.1 Framework Update (Symfony 4 to 7)

Layered Architecture Impact:

  • Business services depend on Symfony APIs (TokenStorage, Session, etc.)
  • Framework APIs changed? Business logic needs rewriting
  • Risk: Breaking changes cascade through entire application
  • Time: Days to weeks fixing compatibility issues

Hexagonal Architecture Impact:

  • Business handlers depend only on your own interfaces
  • Framework APIs changed? Update adapters, handlers stay untouched
  • Risk: Isolated to infrastructure layer
  • Time: Hours updating specific adapters

2.4.2 Testing Speed Comparison

Layered Architecture Reality:

  • Boot Symfony kernel for every test
  • Connect to database, load fixtures
  • Run test with full I/O operations
  • Time per test: 2-3 seconds
  • Result: 100 tests = 5 minutes (developers skip tests)

Hexagonal Architecture Reality:

  • Instantiate handler with in-memory repository
  • Test pure PHP logic with zero I/O
  • Time per test: 0.001 seconds
  • Result: 1000 tests = 1 second (developers run tests constantly)

Impact: You can run 1000 hexagonal tests in the time layered runs 10 tests.

Laptop Analogy for Testing:

  • 🔴 Traditional: Testing laptop functionality requires plugging in real mouse, keyboard, printer, etc.

    • Takes forever to set up
    • Need physical devices for every test
    • If printer is broken, can't test laptop!
  • 🟢 Hexagonal: Testing laptop functionality with "mock USB devices"

    • Instant setup (no physical devices needed)
    • Test laptop logic in isolation
    • Real devices can be broken, laptop tests still pass!

This is exactly what in-memory repositories do - they're "mock USB devices" for your tests!

2.4.3 Lost Business Rules

The Question: "Can we cancel a shipped order?"

Layered Architecture: Rule scattered across:

  • Controller (validation: if ($status === 'shipped') return error;)
  • Service (business logic with different check)
  • Template (UI hiding/showing cancel button with yet another condition)
  • Repository (queries filtering "cancelable" orders)

Result: 4 different implementations, slight variations, which one is correct? Nobody knows.

Hexagonal Architecture: One method in Domain:

Order->cancel() throws exception if status is SHIPPED

Result: ONE source of truth. Crystal clear. Impossible to miss.

2.4.4 Adding New Interface (GraphQL)

Layered Architecture Problem:

  • Business logic mixed in REST controllers
  • Want GraphQL? Must copy-paste logic
  • Result: Same business rules duplicated in 2+ places
  • Maintenance nightmare: change rule = update everywhere

Hexagonal Architecture Solution:

  • Business logic in handler (once)
  • REST controller dispatches command
  • GraphQL resolver dispatches same command
  • CLI command dispatches same command
  • Result: Write once, use everywhere

Laptop Analogy for Multiple Interfaces:

  • 🔴 Traditional: Want to use mouse with desktop, laptop, and tablet?

    • Need to buy 3 different mice (one for each device)
    • Each mouse has custom logic for its device
    • Update mouse firmware? Do it 3 times!
  • 🟢 Hexagonal: One USB mouse works with ALL devices

    • Same mouse plugs into desktop, laptop, tablet
    • Mouse logic written once
    • Update firmware once, works everywhere!

Your business logic is the "mouse" - write it once, plug it into REST/GraphQL/CLI/gRPC!


2.5 Benefits Comparison

Benefit Concrete Impact Time Saved
Direction Control Change database, framework, or any infrastructure without touching business logic Weeks to Days
Single Source of Truth Business rules in ONE place (Domain), not scattered across 10 files 50% less bugs
Lightning Tests 1000x faster (in-memory vs database I/O) 10 min to 10 sec
Technology Freedom Swap MySQL to MongoDB, Doctrine to Another ORM in days not months 80% effort reduction
Reusability Same business logic for REST, GraphQL, CLI, gRPC, message queue Write once, use everywhere
Team Scalability Juniors on adapters (infrastructure), Seniors on domain (business) Clear separation of skill levels
Long-term Viability Code survives framework updates, technology shifts, team changes 10 years vs 10 months

2.6 Cost Predictability: The "5-Day Rule"

In traditional layered architecture, components are tightly coupled. When you add feature "X" after two years into the project, you must navigate code where business logic is mixed with database concerns and framework dependencies. Each modification risks breaking hidden dependencies.

2.6.1 The Layered Architecture "Fail"

The Problem: Time spent is no longer dedicated to coding the feature itself, but to:

  • Doing archaeology in the codebase
  • Understanding tangled dependencies
  • Fixing side effects and cascading breakages
  • Working around technical debt accumulated over time

Real Impact: A feature that should take 5 days now takes 15 days because:

  • 3 days understanding existing code
  • 5 days implementing the feature
  • 7 days fixing broken tests and side effects

2.6.2 The Hexagonal Architecture Advantage

The Solution: Because the domain is isolated, adding business feature "X" happens in a protected environment (the core). Technical complexity (ports and adapters) is pushed to the periphery.

Result: Similar features always cost approximately 5 days, because technical "friction" doesn't increase with business complexity growth.

Why This Works:

  • 📦 Isolated Domain: Business logic lives in its own protected space
  • 🔌 Peripheral Complexity: Technical concerns stay at the edges
  • 📏 Predictable Costs: No hidden dependencies to unravel
  • 🎯 Focus: Developers code features, not fix technical debt

2.6.3 Tests: The Safety Net That Accelerates Development

This is the most powerful technical argument for maintaining the 5-day velocity:

High-Fidelity, Fast Tests:

  • Write unit tests for your domain covering 100% of business rules
  • Never need to start a database or server
  • Tests run in milliseconds, not seconds

Immediate Feedback:

  • Developer knows in 10 seconds if new feature "X" broke something
  • In traditional architecture, database-dependent tests are slow or skipped
  • Bugs discovered later cost 10x-100x more to fix

Example:

// 🎯 Hexagonal: Test in 10ms
$handler = new PlaceOrderHandler(new InMemoryOrderRepository());
$result = $handler->handle($command);
$this->assertTrue($result->isSuccess());

// 🌪️ Traditional: Test in 2-3 seconds
// - Boot Symfony kernel
// - Connect to database
// - Load fixtures
// - Execute test
// - Rollback transaction

2.6.4 The Investment vs. Credit Analogy

Layered Architecture is Consumer Credit:

  • ✨ Easy and fast at the beginning
  • 💸 But interest (technical debt) compounds
  • 🔒 Eventually, debt strangles you
  • 💰 Every feature becomes exponentially more expensive

Hexagonal Architecture is Investment:

  • 💪 Pay a bit more upfront (learning curve, initial structure)
  • 📈 Each future feature costs its real price
  • 🚫 No additional complexity tax
  • 💎 Value grows over time instead of degrading

2.6.5 The Long-Term Math

Year 1: Both architectures similar speed

Year 2:

  • Layered: Features take 2x longer (archaeology + side effects)
  • Hexagonal: Features still take baseline time

Year 3:

  • Layered: Features take 3-5x longer (massive technical debt)
  • Hexagonal: Features still take baseline time

Year 5:

  • Layered: Major refactoring or rewrite required
  • Hexagonal: Still delivering features at predictable pace

The "5-Day Rule" in Practice:

  • Traditional: "Similar" feature in Year 3 = 15-25 days
  • Hexagonal: "Similar" feature in Year 3 = 5-7 days
  • Savings: 10-20 days per feature × 50 features/year = 500-1000 days saved

2.7 Database Migration: The Ultimate Test

Challenge: Change from MySQL (Doctrine ORM) to MongoDB (Document Database)

Laptop Analogy:

  • 🔴 Traditional: Laptop hardwired to Sony mouse - want Logitech? Buy new laptop and reinstall all your software!
  • 🟢 Hexagonal: Laptop with USB port - want different mouse? Unplug Sony, plug Logitech. Laptop works instantly!

This is database migration in a nutshell. Let's see the real impact:

Layered Architecture Impact:

What must change:

  1. All Entities with [@ORM](https://github.com/ORM) annotations must be rewritten as documents
  2. All Repositories using Doctrine API must be rewritten for MongoDB driver
  3. All Services depending on EntityManager must refactor dependencies
  4. All Tests with database fixtures must be rewritten for MongoDB
  5. All Query Builder usage must be rewritten as MongoDB queries

Estimated effort: 2-4 weeks of full-team work Risk level: HIGH - touching 60-80% of codebase Regression probability: Very high - every query must be rewritten and retested

Hexagonal Architecture Impact:

What must change:

  1. Create MongoUserRepository implements UserRepositoryInterface
  2. Create MongoOrderRepository implements OrderRepositoryInterface
  3. Update dependency injection configuration
  4. Done.

What stays the same:

  • All Domain entities (pure PHP, no annotations)
  • All Application handlers (depend on interfaces, not implementations)
  • All Business rules (in Domain, framework-agnostic)
  • All Unit tests (use in-memory repositories)

Estimated effort: 1-2 days Risk level: LOW - only infrastructure adapters change Regression probability: Minimal - business logic untouched

The Math: Hexagonal saves you 10-20x the effort on technology changes.


2.8 Decision Guide

2.8.1 Choose Hexagonal Architecture When:

Criterion Why It Matters
Long-term project (> 2 years) Architecture ROI pays off over time as tech evolves
Growing team (> 3 devs) Clear boundaries help multiple developers work in parallel
Complex business rules Need single source of truth for domain logic
Multiple interfaces REST + GraphQL + CLI + Events = reusable handlers
Tech might change Framework updates, database migrations, cloud migrations
Testing is critical Fast, reliable tests enable continuous deployment
Enterprise/production Business continuity requires technology independence

2.8.2 Stick with Simpler Architecture When:

Situation Better Approach
Quick prototype (< 3 months) Speed matters more than structure
Simple CRUD Little to no business logic = overkill
Solo dev, tiny project Overhead not justified
Stack 100% frozen If you're SURE nothing will ever change (rarely true)

2.8.3 The Core Truth About Decoupling

What People Think Decoupling Means:

"My code doesn't depend on anything! Zero coupling!"

Reality: Impossible. Your code will always call other code. That's programming.

What Decoupling ACTUALLY Means:

"My business logic defines WHAT it needs (interfaces). Infrastructure provides HOW (implementations)."

The Fundamental Shift

Traditional Mindset:

%%{init: {'theme':'base', 'themeVariables': { 'fontSize':'15px'}}}%%
graph LR
    A["🤔 What can we do with<br/>the tools we have?"]
    B["🔒 Business logic limited<br/>by database capabilities"]
    C["👀 Rules adapt to<br/>framework constraints"]
    D["🌪️ Domain serves<br/>infrastructure"]

    A ==> B ==> C ==> D

    style A fill:#FFEBEE,stroke:#C62828,stroke-width:3px,color:#000
    style B fill:#FFF3E0,stroke:#E65100,stroke-width:3px,color:#000
    style C fill:#FFF3E0,stroke:#E65100,stroke-width:3px,color:#000
    style D fill:#FFEBEE,stroke:#C62828,stroke-width:4px,color:#000

Hexagonal Mindset:

%%{init: {'theme':'base', 'themeVariables': { 'fontSize':'15px'}}}%%
graph LR
    A["💡 What does the<br/>business need?"]
    B["📋 Define domain<br/>rules first"]
    C["🔧 Infrastructure adapts<br/>to serve those rules"]
    D["🎯 Infrastructure<br/>serves domain"]

    A ==> B ==> C ==> D

    style A fill:#E8F5E9,stroke:#2E7D32,stroke-width:3px,color:#000
    style B fill:#E8F5E9,stroke:#2E7D32,stroke-width:3px,color:#000
    style C fill:#E1F5FE,stroke:#0277BD,stroke-width:3px,color:#000
    style D fill:#E8F5E9,stroke:#2E7D32,stroke-width:4px,color:#000

2.9 Summary

Hexagonal architecture is not about:

  • Eliminating all coupling
  • Making code more complex
  • Following a trendy pattern

Hexagonal architecture IS about:

  • Direction Control: Your business logic depends on abstractions, infrastructure depends on your business
  • Stability: Business rules stable while technology changes around them
  • Clarity: One place for each business rule, impossible to miss
  • Freedom: Change tech stack in days, not months
  • Speed: Test suite runs in seconds, not minutes

The Question to Ask:

"If we need to change databases, frameworks, or add new interfaces next year, do I want to spend 2 weeks or 2 days?"

If your answer is "2 days," hexagonal architecture is your solution.

Remember: The coupling doesn't go away. You're still calling repositories and services. What changes is who is in charge—your business logic or your database.


2.10 Hexagonal Architecture & Core Design Principles

Hexagonal Architecture isn't just a pattern—it's the natural embodiment of fundamental software engineering principles. Here's how it adheres to and enforces the most important design principles:

###...

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor