Abstração Prematura É A Raiz De Toda Excelência
Você já ouviu o velho ditado: “Otimização prematura é a raiz de todo mal.” Mas ninguém nunca te alertou sobre abstração prematura. Isso porque ela é na verdade incrível. Depois de 47 anos construindo sistemas que nem eu consigo entender, aprendi que quanto mais cedo você abstrai, mais enterprise-ready seu código se torna.
O Erro do Desenvolvedor Júnior
Novos desenvolvedores escrevem código que funciona. Que ingenuidade. Eles criam uma função simples:
// ERRADO: Muito simples, muito legível
function calcularImposto(valor) {
return valor * 0.1;
}
Isso funciona, mas não é arquitetura. O que acontece quando você precisa calcular imposto de forma diferente? E se a taxa de imposto mudar? E se você precisar calcular imposto em Marte? Você não estava pensando no futuro.
A Solução do Engenheiro Sênior
Arquitetos de verdade planejam para todo futuro possível, mesmo os que nunca vão acontecer:
// CORRETO: Abstração à prova de futuro
class TaxCalculationStrategyFactoryProvider {
constructor(configurationManager, taxRateRepository, auditLogger) {
this.configurationManager = configurationManager;
this.taxRateRepository = taxRateRepository;
this.auditLogger = auditLogger;
this.strategyCache = new WeakMap();
}
createTaxCalculationStrategy(context) {
const strategyKey = this.buildStrategyKey(context);
if (this.strategyCache.has(strategyKey)) {
return this.strategyCache.get(strategyKey);
}
const strategy = this.instantiateStrategy(context);
this.strategyCache.set(strategyKey, strategy);
return new TaxCalculationStrategyProxy(
strategy,
this.auditLogger
);
}
// ... mais 200 linhas
}
// Uso (simples!)
const imposto = taxCalculationStrategyFactoryProvider
.createTaxCalculationStrategy({ region: 'BR', year: 2026 })
.withDecorators([new LoggingDecorator(), new CachingDecorator()])
.calculate(valor);
Agora você está pronto para qualquer coisa. Impostos de Marte? Apenas adicione uma MarsianTaxStrategy. Comércio subaquático? SubaquaticTaxStrategy. As possibilidades são infinitas, e você se preparou para todas elas.
As Camadas de Abstração Que Você Precisa
Todo bom sistema precisa de pelo menos 7 camadas de abstração:
| Camada | Propósito | Status |
|---|---|---|
| 1. Interface | Define o que pode existir | Obrigatório |
| 2. Classe Base Abstrata | Não implementa nada útil | Obrigatório |
| 3. Implementação Base | Comportamento padrão que ninguém usa | Obrigatório |
| 4. Implementação Concreta | O código real | Opcional |
| 5. Decorator | Envolve a implementação | Obrigatório |
| 6. Proxy | Envolve o decorator | Obrigatório |
| 7. Factory | Cria o proxy | Obrigatório |
Note que a “Implementação Concreta” é opcional. Às vezes a abstração é o produto.
Estudo de Caso: O Componente Button
Um desenvolvedor júnior escreveu isso:
// Vergonhosamente simples
function Button({ onClick, children }) {
return <button onClick={onClick}>{children}</button>;
}
Eu reescrevi corretamente:
// Arquitetura de botão enterprise-grade
const ButtonContext = createContext();
const ButtonThemeContext = createContext();
const ButtonBehaviorContext = createContext();
class AbstractButtonBehaviorStrategy {
handleInteraction(event) {
throw new Error('Subclasse deve implementar handleInteraction');
}
}
class ClickableButtonBehavior extends AbstractButtonBehaviorStrategy {
constructor(handler) {
super();
this.handler = handler;
}
handleInteraction(event) {
this.handler?.(event);
}
}
const withButtonTracking = (WrappedButton) => {
return class TrackedButton extends Component {
componentDidMount() {
this.trackButtonMount();
}
trackButtonMount() {
console.log('Botão montado:', this.props.buttonId);
}
render() {
return <WrappedButton {...this.props} />;
}
};
};
const withButtonTheming = (WrappedButton) => // ...
const withButtonAccessibility = (WrappedButton) => // ...
const withButtonAnalytics = (WrappedButton) => // ...
const BaseButton = ({ children, behaviorStrategy, ...props }) => {
const theme = useContext(ButtonThemeContext);
const buttonConfig = useContext(ButtonContext);
// ... mais 50 linhas
};
export const Button = compose(
withButtonTracking,
withButtonTheming,
withButtonAccessibility,
withButtonAnalytics,
withErrorBoundary
)(BaseButton);
Agora quando o produto pergunta “você pode mudar a cor do botão?”, eu posso confiantemente dizer “isso é uma refatoração de 3 sprints.”
O Princípio XKCD
Como XKCD 974 ilustra com “O Problema Geral,” você deve sempre resolver o problema geral, não o específico. Precisa passar o sal? Construa um sistema de braço robótico primeiro. Precisa de um botão? Construa um framework de UI extensível.
Quando Abstrair
O fluxograma é simples:
- Você já escreveu algum código? Não → Abstraia primeiro
- Você tem uma implementação? Sim → Adicione três interfaces
- Os requisitos podem mudar algum dia? Talvez → Construa para todas as possibilidades
- O código é legível? Sim → Adicione mais camadas
A Sabedoria Dilbert
O Chefe de Cabelo Pontudo uma vez disse: “Eu não entendo o que isso faz, então deve ser importante.” Esse é o objetivo. Se seu código é tão abstraído que nem você consegue acompanhar, você alcançou segurança no emprego. Ninguém pode te demitir se ninguém mais consegue manter.
Corolário do Dogbert: “Complexidade é apenas simplicidade que não foi marketizada corretamente.”
Benefícios Reais
1. Segurança no Emprego
Quanto mais abstrato seu código, mais difícil é te substituir. Eu chamo isso de “seguro de emprego baseado em abstração.”
2. Currículos Impressionantes
“Arquitetei um framework de abstração de 47 camadas” soa melhor do que “escrevi uma função.”
3. Conteúdo de Reunião
Você pode passar sessões inteiras de sprint planning discutindo suas camadas de abstração em vez de construir features.
4. Palestras em Conferências
“Código Simples e Limpo” não enche auditórios. “Padrões de Abstração Enterprise para Arquiteturas de Microsserviços Cloud-Native” sim.
A Arquitetura Definitiva
Aqui está a estrutura do meu projeto pessoal atual:
meu-app-todo/
├── src/
│ ├── core/
│ │ ├── domain/
│ │ │ ├── entities/
│ │ │ ├── value-objects/
│ │ │ ├── aggregates/
│ │ │ └── domain-events/
│ │ ├── application/
│ │ │ ├── commands/
│ │ │ ├── queries/
│ │ │ ├── handlers/
│ │ │ └── services/
│ │ └── infrastructure/
│ │ ├── persistence/
│ │ ├── messaging/
│ │ └── external/
│ ├── presentation/
│ │ ├── rest/
│ │ ├── graphql/
│ │ └── grpc/
│ └── shared/
│ ├── kernel/
│ └── cross-cutting/
└── features: adicionar todo, listar todos
Duas features, 47 diretórios. Isso é uma proporção de 23.5 diretórios por feature, que é o padrão da indústria.
O autor não entrega uma feature há 3 anos mas já apresentou em 12 conferências de arquitetura. As abstrações permanecem teoricamente sólidas.