Pular para o conteúdo

Como Tudo se Conecta

graph TD
    SOLID["SOLID<br/>princípios de design"]
    DDD["DDD<br/>o que vai no núcleo"]
    ARQ["Hexagonal / Clean<br/>como organizar em volta"]

    SOLID -->|"DIP habilita a inversão"| ARQ
    SOLID -->|"SRP/OCP guiam as classes"| DDD
    DDD -->|"Entidades, VOs, Agregados<br/>preenchem o núcleo"| ARQ
    ARQ -->|"protege"| DDD

Os princípios SOLID operam no nível da classe e do módulo, mas explicam por que as arquiteturas funcionam:

  • O DIP é literalmente o mecanismo por trás de Ports e da Regra da Dependência. Uma “porta” nada mais é que uma abstração da qual tanto o núcleo quanto o detalhe dependem.
  • O OCP é o resultado: com o domínio dependendo de interfaces, você adiciona um novo adapter (novo banco, novo protocolo) sem tocar no núcleo.
  • O SRP justifica separar caso de uso, entidade e adapter — cada um tem uma razão distinta para mudar.
  • ISP mantém as portas pequenas e coesas; LSP garante que adapters diferentes de uma mesma porta sejam de fato substituíveis.

Hexagonal e Clean definem uma “caixa” no centro (o núcleo / as Entities), mas não dizem o que colocar dentro. É o DDD que fornece o conteúdo:

Como detalhado em Comparação - Hexagonal vs Clean vs Camadas, as duas arquiteturas descrevem a mesma estrutura com vocabulários diferentes. Escolher entre elas é secundário; o que importa é: domínio no centro, dependências apontando para dentro, tecnologia nas bordas atrás de portas.

Imagine um caso de uso “transferir dinheiro entre contas”:

  1. DDD modela Conta como agregado, Dinheiro como value object com invariantes (saldo não negativo), e define a interface Contas (repositório = driven port).
  2. Hexagonal/Clean posicionam a Conta no núcleo, o serviço TransferirDinheiro como caso de uso atrás de uma driving port, e a implementação JPA de Contas como adapter secundário.
  3. DIP garante que TransferirDinheiro dependa da interface Contas, não do Spring Data.
  4. OCP permite trocar o adapter JPA por um de outro banco, ou adicionar um adapter de mensageria, sem tocar na regra de transferência.

Veja isso em código em Exemplo Hexagonal com Spring e Exemplo Clean com Spring.

A dúvida prática mais comum depois de aprender os pilares:

graph TD
    Q{"Nova logica de negocio:<br/>o que ela faz?"}
    Q -->|"valida ou deriva o valor<br/>de um conceito"| VO["Value Object"]
    Q -->|"muda o estado de um objeto<br/>com identidade"| ENT["Entidade / raiz do Agregado"]
    Q -->|"regra que cruza<br/>varios agregados"| DS["Domain Service"]
    Q -->|"orquestra transacao,<br/>repositorios e eventos"| AS["Application Service / Use Case"]
    Q -->|"reage a um fato<br/>que ja aconteceu"| EV["Domain Event + listener"]
    Q -->|"traduz formato<br/>ou protocolo"| AD["Adapter"]
Se a lógica… Vai para Nota
Valida ou deriva um valor (CPF, dinheiro, vigência) Value Object Value Objects
Protege estado e invariantes de um objeto rastreável Entidade / raiz de agregado Entidades, Agregados
Envolve vários agregados numa regra de negócio Domain Service Domain Services
Coordena repositórios, transação e publicação de eventos Application Service / interactor Application Services, Use Cases
Reage a algo que já aconteceu, sem acoplar o autor Domain Event + listener Domain Events
Converte JSON/SQL/fila de e para o domínio Adapter Adapters

Os sinais de alerta espalhados pelas notas, consolidados para revisão rápida:

Cheiro no código Provável violação Onde estudar
switch/instanceof sobre “tipo” espalhado pela base OCP OCP - Open-Closed Principle
UnsupportedOperationException em override LSP (e quase sempre ISP) LSP - Liskov Substitution Principle, ISP - Interface Segregation Principle
Descrever a classe exige a palavra “e” SRP SRP - Single Responsibility Principle
new de infraestrutura dentro da regra de negócio DIP DIP - Dependency Inversion Principle
import jakarta.persistence/org.springframework no domínio Regra da Dependência A Regra da Dependência
Entidade só com getters/setters + service gigante Modelo anêmico Entidades, Domain Services
Um repositório por tabela Agregado mal delimitado Agregados, Repositórios
Evento nomeado no imperativo (ConfirmarPedido) Comando confundido com fato Domain Events
Entity ou tipo de framework atravessando fronteira Vazamento de fronteira A Regra da Dependência, Ports
Duas raízes de agregado na mesma transação, sem decisão consciente Fronteira transacional Agregados
Interface com vocabulário técnico (salvarNaTabela) Inversão de fachada, sem inversão real DIP - Dependency Inversion Principle, Ports
Regra de negócio em controller ou adapter Núcleo vazando para a borda Adapters, Use Cases

Um CRUD sem regras de negócio relevantes não se beneficia de portas, agregados ou boundaries — vira cerimônia pura. O custo dessas abstrações só se paga quando existe domínio complexo que vale a pena proteger da tecnologia. Reconhecer isso é, em si, uma aplicação madura de SOLID e do bom senso arquitetural.

  1. Por que se diz que o DIP é o “fio” que conecta SOLID às arquiteturas?
Resposta

Porque portas (Hexagonal) e a Regra da Dependência (Clean) são aplicações diretas do DIP: núcleo e detalhes dependem de abstrações definidas pelo núcleo, com a infraestrutura implementando-as.

  1. Qual o papel do DDD dentro de uma arquitetura Hexagonal ou Clean?
Resposta

Preencher o núcleo/Entities com um modelo rico: entidades, value objects, agregados, repositórios (como portas) e serviços de domínio. A arquitetura fornece a caixa; o DDD, o conteúdo.

  1. Um repositório do DDD corresponde a que elemento arquitetural?
Resposta

A uma driven port (Hexagonal) / output boundary-gateway (Clean): interface definida no domínio, implementada por um adapter de infraestrutura.

  1. Qual sinal indica que aplicar essas abstrações seria overkill?
Resposta

Um domínio essencialmente CRUD, sem regras de negócio relevantes a proteger. Aí portas, agregados e boundaries adicionam custo sem retorno.