Arquitetura Hexagonal em PHP: código limpo sem dor

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Seu código é uma fortaleza, não um espaguete

\n\n

Você já precisou reescrever um projeto do Laravel para o Symfony, ou migrar de MySQL para PostgreSQL? Se sim, conhece bem a dor: controllers cheios de SQL, models que sabem tudo sobre requisições HTTP e uma lógica de negócio que morre ao trocar de framework. Familiar?

\n\n

Bem-vindo ao mundo da Arquitetura Hexagonal em PHP (também conhecida como Ports & Adapters). Não é um buzzword da moda, mas sim uma tábua de salvação para projetos que desejam viver por muito tempo. Neste artigo, vamos construir uma aplicação PHP que não tem medo de mudanças: trocar de banco de dados, migrar para um novo cache ou integrar uma API diferente — tudo isso se torna uma simples substituição de adaptador, e não uma reescrita de metade do código.

\n\n

O que é a Arquitetura Hexagonal, afinal?

\n\n

Imagine que sua aplicação é um núcleo (core) que não sabe de onde os dados vêm: web, console, bot do Telegram ou testes. Em volta desse núcleo, temos os portos (interfaces) e, externamente, os adaptadores (implementações). A arquitetura hexagonal em PHP permite separar a lógica de negócio do código de infraestrutura. Você escreve as regras uma vez e as reutiliza em qualquer lugar.

\n\n

Princípios-chave:

\n
    \n
  • Inversão de dependências — o código de alto nível não depende do de baixo nível. É o contrário.
  • \n
  • Lógica de negócio isolada — nenhum framework penetra nos seus Use Cases.
  • \n
  • Testabilidade — você pode testar o núcleo sem banco de dados ou HTTP.
  • \n
\n\n

Diferente da Clean Architecture de Robert Martin, a Hexagonal é mais flexível: ela não dita o número de camadas, apenas diz: "Dentro fica o domínio, fora fica a infraestrutura, conecte-os através de interfaces".

\n\n

Estrutura do projeto: da bagunça à organização

\n\n

Vamos direto ao ponto. Aqui está uma estrutura de pastas típica para a Arquitetura Hexagonal em PHP:

\n\n
src/\n├── Domain/\n│   ├── Entity/\n│   │   └── Order.php\n│   ├── Repository/\n│   │   └── OrderRepositoryInterface.php\n│   └── Service/\n│       └── OrderService.php\n├── Application/\n│   ├── UseCase/\n│   │   └── CreateOrderUseCase.php\n│   └── Port/\n│       ├── Input/\n│       │   └── CreateOrderInputPort.php\n│       └── Output/\n│           └── OrderOutputPort.php\n├── Infrastructure/\n│   ├── Persistence/\n│   │   └── DoctrineOrderRepository.php\n│   └── Http/\n│       └── OrderController.php\n└── Framework/\n    └── Symfony/\n        └── Kernel.php
\n\n

Percebeu? Domain e Application não sabem da existência do Doctrine, Symfony ou MySQL. Eles trabalham apenas com interfaces. Os adaptadores vivem em Infrastructure e são conectados via Injeção de Dependência.

\n\n

Passo 1: Definindo o núcleo — Domain

\n\n

Vamos começar com a entidade de pedido. Sem anotações de ORM, sem setters que quebram invariantes:

\n\n
<?php\n// src/Domain/Entity/Order.php\nnamespace App\\Domain\\Entity;\n\nclass Order\n{\n    public function __construct(\n        private readonly string $id,\n        private readonly string $productName,\n        private float $totalAmount,\n        private string $status = 'pending'\n    ) {}\n\n    public function approve(): void\n    {\n        if ($this->status !== 'pending') {\n            throw new \\DomainException('Only pending orders can be approved.');\n        }\n        $this->status = 'approved';\n    }\n\n    public function getId(): string { return $this->id; }\n    public function getProductName(): string { return $this->productName; }\n    public function getTotalAmount(): float { return $this->totalAmount; }\n    public function getStatus(): string { return $this->status; }\n}
\n\n

Aqui está o port (interface) para o repositório. Observe: ele não sabe nada sobre Doctrine ou PDO:

\n\n
<?php\n// src/Domain/Repository/OrderRepositoryInterface.php\nnamespace App\\Domain\\Repository;\n\nuse App\\                

Blogs

Book Recommendations