0Pricing
PHP Academy · Aula

Da arquitetura em camadas à arquitetura limpa

Entenda por que as dependências devem apontar para dentro.

Da arquitetura em camadas à arquitetura limpa é uma aula grátis de PHP Academy no CoddyKit. Esta é a aula 1 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de PHP Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de PHP Academy inclui 4 aulas no total.

Por que a Arquitetura Limpa

Você já conhece a clássica pilha PHP de três camadas: Controlador → Serviço → Repositório → Banco de dados. Ela funciona, mas a lógica de negócio acaba acoplada ao Eloquent, ao Doctrine, à solicitação HTTP e ao ciclo de vida do arcabouço. A Arquitetura Limpa inverte a direção das dependências para que seu domínio não saiba nada sobre a infraestrutura. A recompensa: casos de uso testáveis, adaptadores substituíveis e uma base de código que sobrevive às atualizações do arcabouço.

A regra das dependências

A única regra da Arquitetura Limpa: as dependências do código-fonte apontam somente para dentro. Os círculos internos (entidades, casos de uso) nunca devem referenciar os círculos externos (controladores, mapeadores objeto-relacional, arcabouços). Em tempo de execução, o fluxo de controle segue para fora por meio de interfaces, mas, durante a compilação e a importação, nada interno importa algo externo.

  • Entidades: regras corporativas
  • Casos de uso: regras de aplicação
  • Adaptadores: controladores, apresentadores, gateways
  • Arcabouços e controladores: banco de dados, HTTP, a rede

Exemplo de camadas acopladas

Este é o tipo de serviço que a maioria dos aplicativos PHP disponibiliza. Observe como a lógica de domínio está entrelaçada com o Eloquent e a resposta HTTP. Não é possível testar unitariamente a regra de desconto sem um banco de dados e um arcabouço.

<?php
class OrderService
{
    public function place(Request $request)
    {
        $user = User::find($request->user_id); // Eloquent
        $total = 0;
        foreach ($request->items as $i) {
            $total += Product::find($i['id'])->price * $i['qty'];
        }
        if ($user->is_vip) {
            $total *= 0.9; // business rule trapped in infra code
        }
        Order::create(['user_id' => $user->id, 'total' => $total]);
        return response()->json(['total' => $total]);
    }
}

Entidades: domínio independente de arcabouço

Uma entidade codifica regras que abrangem toda a empresa e não depende de nada. PHP puro, sem anotações e sem classe-base do mapeador objeto-relacional. Ela pode ser totalmente instanciada em um teste.

<?php
final class Money
{
    public function __construct(public readonly int $cents) {
        if ($cents < 0) throw new InvalidArgumentException('negative money');
    }
    public function multiply(float $factor): self {
        return new self((int) round($this->cents * $factor));
    }
}

final class Order
{
    /** @param array<int,int> $lineCents */
    public function __construct(private array $lineCents, private bool $vip) {}
    public function total(): Money {
        $sum = array_sum($this->lineCents);
        $money = new Money($sum);
        return $this->vip ? $money->multiply(0.9) : $money;
    }
}

echo (new Order([1000, 2000], true))->total()->cents, PHP_EOL; // 2700

Os casos de uso controlam o fluxo de trabalho

Um caso de uso (intermediário) coordena entidades e se comunica com o mundo externo somente por meio de interfaces (portas). Ele recebe um objeto de transferência da solicitação e retorna um objeto de transferência da resposta — nunca um objeto HTTP.

<?php
interface OrderRepository {
    public function save(Order $order): void;
}

final class PlaceOrder
{
    public function __construct(private OrderRepository $orders) {}

    public function execute(array $lineCents, bool $vip): int {
        $order = new Order($lineCents, $vip);
        $this->orders->save($order);
        return $order->total()->cents;
    }
}

A fronteira é uma interface

O caso de uso declara a interface OrderRepository de que precisa. A interface vive no círculo interno; a implementação concreta do Eloquent/Doctrine vive fora e depende do interior. Este é o Princípio da Inversão de Dependência aplicado a um limite arquitetural.

Direção da dependência do código-fonte: EloquentOrderRepository → OrderRepository (interface), nunca o contrário.

<?php
// Lives in infrastructure layer, points INWARD to the domain interface
final class EloquentOrderRepository implements OrderRepository
{
    public function save(Order $order): void {
        OrderModel::create(['total' => $order->total()->cents]);
    }
}

Testes sem infraestrutura

Como o caso de uso depende de uma interface, os testes injetam um dublê. Sem banco de dados e sem inicialização do arcabouço — testes unitários extremamente rápidos que verificam o comportamento puro do negócio.

<?php
final class InMemoryOrders implements OrderRepository {
    public array $saved = [];
    public function save(Order $o): void { $this->saved[] = $o; }
}

$repo = new InMemoryOrders();
$useCase = new PlaceOrder($repo);
$total = $useCase->execute([1000, 2000], true);

assert($total === 2700);
assert(count($repo->saved) === 1);
echo "PASS total=$total saved=" . count($repo->saved) . PHP_EOL;

Controladores tornam-se adaptadores finos

O controlador agora é um adaptador: traduz HTTP em uma chamada ao caso de uso e o resultado de volta para HTTP. Ele não contém regras de negócio. Troque REST por CLI ou por um trabalhador de fila, e o caso de uso permanecerá intacto.

<?php
final class OrderController
{
    public function __construct(private PlaceOrder $placeOrder) {}

    public function store(Request $request): JsonResponse {
        $total = $this->placeOrder->execute(
            lineCents: $request->input('lineCents'),
            vip: (bool) $request->input('vip'),
        );
        return new JsonResponse(['total' => $total], 201);
    }
}

Arquitetura que evidencia o domínio

A estrutura de pastas deve evidenciar o domínio, não o arcabouço. Evite Controllers/ e Models/ no nível superior. Organize por capacidade de negócio (capability), para que uma pessoa recém-chegada veja o que o aplicativo faz.

  • src/Ordering/Domain/ — entidades, objetos de valor
  • src/Ordering/Application/ — casos de uso, interfaces de portas
  • src/Ordering/Infrastructure/ — repositórios Eloquent, controladores HTTP

Cada contexto delimitado é uma pasta de nível superior; o arcabouço vive nas bordas.

Aplicando a regra das dependências

A disciplina se perde sem ferramentas. Use deptrac ou phparkitect na integração contínua para fazer a compilação falhar quando o Domínio importar a Infraestrutura. A regra se torna uma garantia em tempo de compilação, em vez de depender da esperança de uma revisão de código.

# deptrac.yaml
deptrac:
  layers:
    - name: Domain
      collectors: [{ type: directory, value: src/.*/Domain/.* }]
    - name: Application
      collectors: [{ type: directory, value: src/.*/Application/.* }]
    - name: Infrastructure
      collectors: [{ type: directory, value: src/.*/Infrastructure/.* }]
  ruleset:
    Domain: []                       # Domain may depend on nothing
    Application: [Domain]
    Infrastructure: [Application, Domain]

Atravessando fronteiras com objetos de transferência de dados

Para impedir que as entidades vazem para fora, os dados que atravessam uma fronteira viajam como um simples objeto de transferência de dados, não como uma entidade ou um modelo de mapeador objeto-relacional. O caso de uso retorna uma estrutura plana que o adaptador pode serializar, para que o objeto de domínio nunca escape do núcleo e a camada externa nunca obtenha acesso ao estado interno.

<?php
final class OrderSummary // boundary DTO, no behavior, no domain types
{
    public function __construct(
        public readonly string $orderId,
        public readonly int $totalCents,
    ) {}
}

final class PlaceOrderV2 {
    public function __construct(private OrderRepository $orders) {}
    public function execute(array $lineCents, bool $vip): OrderSummary {
        $order = new Order($lineCents, $vip);
        $this->orders->save($order);
        return new OrderSummary('ord_1', $order->total()->cents);
    }
}

Verificação rápida

Qual direção de dependência é permitida pela regra das dependências?

Recapitulação

Você passou de uma pilha de camadas acopladas à Arquitetura Limpa:

  • A regra das dependências: as dependências do código-fonte apontam somente para dentro.
  • Entidades contêm regras corporativas em PHP independente de arcabouço.
  • Casos de uso coordenam por meio de interfaces de portas, retornando objetos de transferência de dados, não HTTP.
  • Controladores e repositórios do mapeador objeto-relacional são adaptadores externos que dependem do interior (DIP).
  • A estrutura deve evidenciar o domínio, e ferramentas como o deptrac aplicam a regra na integração contínua.

Perguntas Frequentes

A aula “Da arquitetura em camadas à arquitetura limpa” é grátis?

Sim — o texto completo de “Da arquitetura em camadas à arquitetura limpa” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de PHP Academy, atualize para CoddyKit PRO. O curso de PHP Academy inclui 4 aulas no total.

O que vou aprender em “Da arquitetura em camadas à arquitetura limpa”?

Entenda por que as dependências devem apontar para dentro. Você pratica PHP Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar PHP Academy?

Nenhuma experiência prévia é necessária. PHP Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 1 de 4.

Quanto tempo leva a aula “Da arquitetura em camadas à arquitetura limpa”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de PHP Academy?

Sim. Cada aula de PHP Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Da arquitetura em camadas à arquitetura limpa
  2. Portas e adaptadores explicados
  3. Casos de uso e serviços de aplicação
  4. Inversão de dependências na prática
← Voltar para PHP Academy