2. Os Três Tipos de Complexidade
Vamos compreender os três tipos de complexidade no desenvolvimento de software e por que essa distinção é fundamental para aplicar DDD corretamente.
A Natureza da Complexidade no Desenvolvimento de Software
Para compreender as raízes do cenário atual do desenvolvimento de software e a necessidade de retomar os fundamentos do DDD, precisamos primeiro entender a definição de ‘complexidade’ no contexto do desenvolvimento de software.
No âmbito da engenharia de software, a análise da complexidade pode ser categorizada em três formas distintas: a complexidade da solução técnica, a complexidade do legado e a complexidade do domínio.
2.1. Complexidade da Solução Técnica (Acidental)
A primeira, referida como Complexidade da Solução Técnica, diz respeito às decisões arquiteturais e instrumentais, tais como:
- A escolha entre message brokers (e.g., Kafka ou RabbitMQ)
- A definição de infraestrutura (Kubernetes ou serverless)
- Decisões sobre bancos de dados (SQL vs. NoSQL)
- Frameworks e bibliotecas (Spring Boot, Quarkus, Node.js)
- Protocolos de comunicação (REST, gRPC, GraphQL)
Tais decisões, embora fundamentais para o funcionamento do sistema, configuram-se como complexidade acidental, uma vez que derivam da implementação e não do problema de negócio em si (BROOKS, 1987; JUNIOR, 2020).
O que é complexidade acidental?
Frederick Brooks, em seu artigo seminal “No Silver Bullet” (1987), estabeleceu a distinção fundamental entre:
- Complexidade Essencial: inerente ao problema que estamos resolvendo
- Complexidade Acidental: introduzida pela forma como escolhemos resolver o problema
A complexidade técnica é acidental porque poderíamos, em teoria, resolver o mesmo problema de negócio com tecnologias diferentes. Se amanhã surgir uma nova ferramenta ou framework, poderíamos migrar para ela sem que o problema de negócio tenha mudado.
2.2. Complexidade do Legado (Acidental)
Em segundo plano, temos a Complexidade do Legado, originada pela necessidade de manutenção de sistemas preexistentes. O acúmulo de dívida técnica e a dificuldade em evoluir códigos herdados constituem barreiras que também se classificam como complexidade acidental, drenando recursos cognitivos da equipe (VERNON, 2016).
Essa complexidade manifesta-se através de:
- Código escrito com tecnologias obsoletas ou descontinuadas
- Falta de documentação ou documentação desatualizada
- Arquitetura que já não serve aos propósitos atuais
- Dependências antigas com vulnerabilidades de segurança
- Conhecimento concentrado em poucos desenvolvedores (ou pior, em nenhum)
A complexidade do legado é particularmente insidiosa porque consome tempo e energia que poderiam ser investidos em entender e resolver o problema de negócio. Cada hora gasta tentando entender por que um sistema legado funciona de determinada maneira é uma hora não gasta compreendendo as reais necessidades dos usuários.
2.3. Complexidade do Domínio (Essencial)
Por fim, destaca-se a Complexidade do Domínio. Esta compreende as regras e desafios intrínsecos ao negócio que o software visa atender.
Segundo Evans (2003), esta é a única complexidade essencial, pois reside no coração do software e justifica o investimento financeiro e o esforço de desenvolvimento para solucionar um problema real.
Exemplos de complexidade do domínio:
Setor Financeiro:
- Regras de cálculo de juros compostos
- Legislação tributária e compliance
- Gestão de risco de crédito
- Detecção de fraudes
Setor de Saúde:
- Protocolos médicos e interações medicamentosas
- Regulamentações de privacidade (HIPAA, LGPD)
- Fluxos de triagem e atendimento
- Gestão de leitos e recursos
E-commerce:
- Regras de precificação dinâmica
- Cálculo de frete e logística reversa
- Gestão de estoque distribuído
- Programas de fidelidade
Observe que nenhum desses problemas desaparece se trocarmos o banco de dados ou migrarmos para Kubernetes. A complexidade do domínio é inerente ao negócio, independentemente de como escolhemos implementar a solução tecnológica.
2.4. O Foco Deslocado: Tecnologia vs. Negócio
Apesar da Complexidade do Domínio ser a complexidade essencial, há uma inclinação natural entre nós, profissionais de tecnologia, em priorizar o gerenciamento das complexidades acidentais, sejam elas técnicas ou legadas.
Por que isso acontece?
A discussão sobre arquitetura, padrões de projeto e ferramentas constitui uma ‘zona de conforto’ cognitiva para nós que somos especialistas técnicos. É mais fácil e mais gratificante debater:
- “Devemos usar Redis ou Memcached para cache?”
- “Nossa arquitetura deve ser em camadas ou hexagonal?”
- “Precisamos migrar para microsserviços ou um monólito modular é suficiente?”
Do que enfrentar questões como:
- “Por que esse processo de aprovação tem 7 etapas?”
- “Qual a diferença entre ‘cancelamento’ e ‘devolução’ do ponto de vista do negócio?”
- “Em que circunstâncias um pedido pode ser editado após confirmação?”
Entretanto, a eficácia do projeto reside na resolução justamente da complexidade essencial. Um sistema pode ter a arquitetura mais sofisticada do mundo, usar as melhores práticas e as tecnologias mais modernas, mas se não resolver corretamente o problema de negócio, ele falhou.
💡 Reflexão: No seu último projeto, sua equipe gastou mais energia tentando configurar o cluster Kubernetes e o ambiente Docker, escolhendo entre diferentes bibliotecas e/ou banco de dados não-relacionais, ou tentando entender profundamente os motivos pelos quais o usuário precisa de determinada funcionalidade? Em outras palavras, seu esforço é maior na Complexidade Acidental ou na Complexidade Essencial? Se a resposta foi a primeira, é importante ficar atento. Talvez sua equipe esteja “vencendo a batalha tecnológica”, mas “perdendo a guerra do negócio”.
2.5. A Barreira da Comunicação
É aqui, neste cenário, que surge um dos entraves mais críticos ao desenvolvimento de software eficaz: a dissonância comunicativa histórica entre as equipes de tecnologia e os especialistas de negócio.
Conforme Evans (2003), quando não há uma linguagem partilhada, o projeto sofre com o custo da ‘tradução’ constante, onde o conhecimento se perde na interpretação entre modelos mentais distintos. É como se cada lado falasse um idioma diferente:
Do lado do negócio:
De um lado, os especialistas de negócio tendem a trivializar demandas complexas, focando na interface ou no resultado imediato:
- “É só um botãozinho novo”
- “Puxa um relatório simples pra mim”
- “Quero um sisteminha ‘tipo o iFood’”
Essa simplificação excessiva ignora a complexidade subjacente e gera atrito com a equipe técnica, que percebe a subestimação do esforço necessário (JUNIOR, 2020).
Do lado da TI:
Em contrapartida, a equipe de Tecnologia da Informação (TI) frequentemente recorre a um “dialeto tecnocrático”, repleto de terminologias incompreensíveis para os especialistas de negócio:
- “Temos que versionar a API”
- “Para essa feature precisamos configurar o OAuth2 para gerenciar 2FA”
- “Nosso cluster Kubernetes está com problema!”
Esse comportamento reflete a tendência dos desenvolvedores de se refugiarem na complexidade acidental, alienando os stakeholders e obscurecendo o modelo de domínio real (VERNON, 2013).
O Resultado: Um Abismo Comunicativo
O resultado é um abismo onde a colaboração se torna inviável e o software produzido não reflete as reais necessidades da organização.
💡 Reflexão: Quando um usuário diz que algo “é simples, é só colocar um botãozinho novo ali”, ele ignora todo o trabalho de backend, banco de dados e testes. Mas olhe o outro lado da moeda: quando respondemos uma demanda com “Preciso refatorar a camada de infraestrutura para injetar a dependência correta”, eles sentem a mesma frustração. O objetivo do DDD é fazer essas duas pessoas se entenderem.
2.6. O Verdadeiro Fundamento do DDD
A proposição fundamental de Evans (2003) visa justamente transpor essa barreira comunicacional e realinhar o foco do desenvolvimento.
O racional do Domain-Driven Design estabelece que a mitigação temporária das complexidades acidentais, inerentes à tecnologia e ao legado, é condição necessária para permitir a imersão profunda na complexidade essencial do domínio.
Para operacionalizar essa diretriz, a metodologia prescreve uma mudança de postura:
O DDD nos pede para:
- Deixar um pouco de lado as complexidades técnica e de legado e seus vocabulários
- Interessar-nos genuinamente pela complexidade essencial: a do domínio
- Conversar ativamente com os especialistas de domínio
- Encontrar formas de romper as barreiras de comunicação, trazendo a linguagem do domínio (o vocabulário do negócio) para dentro do código
Esse objetivo não significa ignorar completamente questões técnicas ou de legado — elas são importantes e precisam ser gerenciadas. Mas significa priorizar o entendimento do problema de negócio acima da sofisticação técnica.
2.7. Quando a Implementação do DDD Dá Errado: O DDD-Lite
A distorção metodológica mais comum na adoção do DDD, conhecida como DDD-Lite, manifesta-se quando a equipe técnica subverte a hierarquia de prioridades, negligenciando a colaboração com os especialistas de domínio.
Ao omitir a etapa de modelagem estratégica e a construção da Linguagem Ubíqua, o foco desloca-se indevidamente para a aplicação mecânica de padrões táticos (VERNON, 2013).
Sintomas do DDD-Lite:
Nesse contexto, observa-se uma ênfase excessiva na implementação de artefatos técnicos como:
- Entidades (Entities)
- Objetos de Valor (Value Objects)
- Repositórios (Repositories)
- Shared Kernels
- Agregados (Aggregates)
Tratados muitas vezes como itens obrigatórios de um checklist arquitetural.
Entretanto, tal abordagem contradiz a essência da metodologia. Conforme Evans (2003), esses padrões não constituem o objetivo final do desenvolvimento. Eles são, na verdade, mecanismos expressivos projetados para articular o modelo de domínio dentro do código, e não um objetivo em si mesmo.
A utilização desses padrões de forma dissociada de um entendimento profundo do problema de negócio resulta em uma complexidade acidental elevada, sem entregar o valor estratégico proposto pelo DDD (JUNIOR, 2020).
Ou seja, ao invés de atacar a complexidade no coração do software, acaba-se introduzindo complexidade acidental no desenvolvimento de software.
2.8. O DDD “Anêmico”
Essa priorização da técnica sobre o negócio culmina, frequentemente, na proliferação de Modelos de Domínio Anêmicos.
Segundo Fowler (2003), esta anti-padrão caracteriza-se por objetos que, embora possuam a aparência de objetos de domínio, limitam-se a estruturas de dados desprovidas de comportamento, compostas estritamente por métodos de acesso (getters e setters).
O Paradoxo:
Paradoxalmente, esses modelos costumam estar envoltos em uma arquitetura de alta sofisticação técnica, normalmente repleta de:
- Repositórios genéricos complexos
- Múltiplas camadas de abstração
- Serviços complexos e acoplados
Isso representa o oposto do que o DDD prega. Arquitetura inflada e overengineering é a antítese da filosofia proposta por Evans, uma vez que isso adiciona complexidade acidental sem capturar a riqueza do negócio (VERNON, 2013).
2.9. Conclusão: O Retorno aos Fundamentos
A implementação do Domain-Driven Design exige o reconhecimento de que seu propósito fundamental é o gerenciamento da complexidade essencial, que é a de domínio.
A metodologia não foi pensada como panaceia ou bala de prata para os problemas da solução técnica ou para a gestão do legado. DDD não serve para resolver os problemas da solução técnica ou lidar melhor com o legado.
Quando DDD faz sentido:
Além disso, DDD se encaixa melhor em cenários onde os domínios são complexos. Se o domínio do problema é simples, o DDD agrega pouco valor e pode até mesmo atrapalhar.
Ou seja, em domínios triviais ou de baixa complexidade cognitiva, a adoção de padrões táticos do DDD tende a gerar um custo de implementação injustificável, agregando burocracia em vez de valor (EVANS, 2003; JUNIOR, 2020).
O Objetivo Final:
Devemos sempre nos lembrar do objetivo final do DDD: atacar a complexidade no coração do software, não introduzir complexidade artificial no processo de desenvolvimento de software.
Na próxima seção, exploraremos o mecanismo central para atacar essa complexidade essencial: a Linguagem Ubíqua.