DEV Community

Denis Augusto
Denis Augusto

Posted on

DDD no seu CRUD é canhão pra matar mosquito

Sete arquivos pra salvar um cadastro

Peguei um projeto uma vez pra fazer uma alteração pequena: adicionar o campo "telefone secundário" no cadastro de cliente. Coisa de dez minutos, né?

Uma hora e meia depois eu tinha mexido em: a entidade Cliente, o value object Telefone, o DTO de entrada, o mapper que traduz DTO pra entidade, a interface do repositório, a implementação Eloquent do repositório, o use case AtualizarClienteUseCase, e o request do controller.

Oito arquivos. Pra um campo de texto que vai direto pra uma coluna varchar.

O sistema não fazia nada de mirabolante com esse cliente. Cadastrava, listava, editava, apagava. Um CRUD. Mas estava vestido com o terno de um sistema bancário.

Já pegou um projeto assim? Ou — pergunta mais desconfortável — já escreveu um?

O que parece sofisticação e é só cerimônia

Olha o caminho que um POST /clientes percorria naquele projeto:

// 1. Controller recebe e converte pra DTO
$dto = new CriarClienteDTO(
    nome: $request->nome,
    email: $request->email,
    telefone: $request->telefone,
);

// 2. Use case recebe o DTO
$useCase->executar($dto);

// 3. Dentro do use case: DTO vira entidade de domínio
$cliente = new ClienteEntity(
    nome: new Nome($dto->nome),
    email: new Email($dto->email),
    telefone: new Telefone($dto->telefone),
);

// 4. Repositório recebe a entidade
$this->repositorio->salvar($cliente);

// 5. Dentro do repositório: entidade vira Model
$model = new ClienteModel();
$model->nome = $cliente->nome->valor();
$model->email = $cliente->email->valor();
$model->telefone = $cliente->telefone->valor();
$model->save();

// 6. E aí volta tudo, agora no sentido contrário 🙃
Enter fullscreen mode Exit fullscreen mode

Cinco traduções do mesmo dado. Nome vira Nome, Nome vira string de novo, string vira coluna.

Agora a pergunta honesta: qual bug isso evitou? Qual regra de negócio ficou protegida? Nesse projeto, nenhuma. O ClienteEntity era um saco de propriedades sem nenhum comportamento — exatamente o que a gente acusa de "modelo anêmico", só que espalhado em quatro camadas.

DDD nunca foi sobre pastas

Aqui mora a confusão. A maior parte do que a galera chama de DDD é a parte tática: entidade, value object, agregado, repositório, use case. É a parte fácil de copiar, porque é estrutura de pasta.

Só que o DDD é sobre a parte estratégica: entender o negócio a fundo, conversar com quem trabalha nele, descobrir que "pedido" pro pessoal do estoque não é a mesma coisa que "pedido" pro financeiro, e desenhar fronteiras a partir disso. Linguagem ubíqua, contextos delimitados.

A estrutura de pastas é consequência disso. Não substituto.

Quando você copia só as pastas, o que sobra é o custo — mais arquivos, mais indireção, mais tempo pra qualquer mudança — sem o benefício, que seria um código que fala a língua do negócio.

A régua: complexidade de dado ou complexidade de regra?

A pergunta que eu faço hoje antes de montar qualquer camada:

Esse dado tem regra ou só tem formulário?

Se o fluxo é "recebe do form, valida formato, salva no banco, mostra numa listagem", isso é complexidade de dado. Laravel já resolve isso lindamente com Form Request, Eloquent e Resource. Adicionar camada aí só aumenta a distância entre você e o varchar.

Agora, se o fluxo é "recebe a solicitação, verifica se o cliente tem limite, calcula juros conforme a modalidade, aplica desconto de fidelidade, agenda a cobrança e emite o título" — aí é complexidade de regra. É aqui que separar as coisas te salva, porque a regra vive mais tempo que o framework e muda por motivos diferentes.

E o ponto que quase ninguém fala: os dois convivem no mesmo projeto. Não precisa escolher.

Como fica na prática

O CRUD continua CRUD. Sem culpa nenhuma:

class ClienteController extends Controller
{
    public function store(StoreClienteRequest $request)
    {
        $cliente = Cliente::create($request->validated());

        return new ClienteResource($cliente);
    }
}
Enter fullscreen mode Exit fullscreen mode

Três linhas. Todo mundo entende, inclusive o dev que entrar no time semana que vem.

O que tem regra ganha uma Action: o caminho do meio que resolve 90% dos casos.

final class AprovarEmprestimo
{
    public function __invoke(Emprestimo $emprestimo, User $analista): void
    {
        // aqui mora a regra, isolada e testável, sem quatro camadas
    }
}
Enter fullscreen mode Exit fullscreen mode

E as peças táticas você usa avulso, onde valem a pena. Value Object pra CPF e Dinheiro, Enum pra status, DTO na fronteira de integração. Não é pacote fechado — dá pra pegar só as ferramentas que resolvem o seu problema.

A pegadinha: "mas e quando o projeto crescer?"

Esse é o argumento que eu mais ouço. E ele tem um erro escondido: assume que refatorar depois é mais caro do que carregar a estrutura desde o começo.

Quase nunca é. Transformar um controller gordo em Action é trabalho de uma tarde, e você faz isso sabendo qual é a regra de verdade, porque o negócio já se revelou.

O contrário é que dói: desmontar uma arquitetura de quatro camadas que ninguém entende e que já espalhou tradução de dado por trinta arquivos. Aí é semana, não tarde.

E tem o custo que não aparece no gráfico: cada dev novo no time leva três semanas pra fazer o primeiro CRUD, porque precisa aprender o dialeto local antes de escrever a primeira linha.

Bônus: o teste do "conta pro estagiário"

Antes de adicionar uma camada, tenta explicar em voz alta pra alguém que acabou de chegar qual problema aquilo resolve.

Se a resposta sair como "é pra desacoplar do framework" ou "é boa prática", segura a onda. Não é justificativa, é slogan.

Se a resposta for "porque a regra de cálculo de comissão muda por estado e a gente precisa testar as 27 combinações sem tocar no banco" — aí sim. Isso é um problema concreto, e a camada tem endereço.

Antes de você fechar a aba

Não estou dizendo que DDD é ruim. Em domínio complexo de verdade, com regra de negócio que muda toda semana e gente do negócio discutindo termo por termo, ele é o que segura o sistema em pé.

O que eu tô dizendo é que arquitetura é resposta a um problema. Sem o problema, ela é só custo com aparência de sofisticação.

Cadastro de cliente não precisa de camada anticorrupção. Precisa de Cliente::create().

E aí, você já pegou (ou construiu) um projeto vestido pra uma complexidade que ele não tinha? Ou é do time que acha que camada nunca é demais? Vem discutir nos comentários — esse tema divide a comunidade ao meio e eu quero ouvir os dois lados. 🍿


Top comments (0)