A armadilha da teoria
Muitos devs frontend leem sobre S.O.L.I.D, decoram os termos bonitos para as entrevistas... e no dia a dia continuam criando God Components (aqueles componentes gigantes que fazem absolutamente tudo).
Os princípios não são check-lists de regras engessadas. São a divisa exata entre um código onde você tem medo de alterar algo e um código escalável e seguro para o futuro.
Confira agora 15 cenários dissecados no código, com os "Erros", os "Acertos" e O Mais Importante: O Porquê de evitar aquele padrão!
O Princípio da Responsabilidade Única
A teoria define: "Uma classe deve ter um e apenas um motivo para mudar".
No desenvolvimento Angular, a separação de responsabilidades significa garantir que seus componentes fiquem imunes às regras complexas da malha de dados. O Componente apenas desenha a View (HTML), enquanto os Services lidam com a regra de Negócios pura.
SRP - Exemplo 1 💻
O que evitar? Injetar o HTTP direto na View.
Se a API sofre um breaking change, a atualização deve ocorrer em um único Service isolado. Sua view jamais deveria tratar erros nativos de infraestrutura do HttpClient ou ficar gastando RAM gerenciando cancelamento de Subscriptions manuais em Componentes Folha.
SRP - Exemplo 2 💻
O que evitar? Arquivos que acumulam ações sistêmicas gigantes.
Quando seu colega mexer no código do Carrinho, todos os componentes (até os da Home) que injetam e importam esse "GodService" revalidarão a injeção dele inutilmente. Isso gera conflitos desnecessários no Git e aumenta o uso de RAM na sessão sem nenhuma utilidade.
SRP - Exemplo 3 💻
O que evitar? Popular o Componente View de calculadoras Strings brutas.
Anotar a chamada {{ formatarRegraPesada(user.id) }} direto na View força o Change Detector do Angular a executar o método novamente a cada ciclo de detecção de mudanças, travando a interface do usuário! Um Custom Pipe puro recalcula o valor estritamente só quando o argumento de entrada sofre mutação.
O Princípio Aberto/Fechado
A teoria define: "As entidades devem ser abertas para extensão, mas fechadas para modificação".
Trata-se de arquitetar módulos que permitam extensibilidade funcional sem termos que invadir o arquivo base do componente (quebrar o status de código fechado dele) apenas para colocar um caminhão de novos IFs lógicos perigosos a cada Variante na Sprint de Layout.
OCP - Exemplo 1 💻
O que evitar? Quebrar e sujar Cards Genéricos exigindo Inputs Infinitos.
Atrelar múltiplos cenários baseados em Inputs IFs engessa o time inteiro. Ao usar ng-content, nós fechamos o Container Base com absoluta imunidade a quebras futuras. Amanhã para adicionar botões animados basta Você acoplar e estender via Tags Externas blindadas sem encostar no arquivo fonte do Modal!
OCP - Exemplo 2 💻
O que evitar? Infestar o HTML com lógicas gigantes e fixas acopladas de IF Arrays.
Manter lógica sensível de permissões colada na malha frágil do HTML em cada View resulta em dívidas técnicas irredutíveis. Se a regra de acesso mudar ou a matriz de permissões crescer, você ajusta em um único arquivo TypeScript central da Diretiva, eliminando a caça exaustiva em dezenas de templates espalhados.
OCP - Exemplo 3 💻
O que evitar? Modificar à mão as instâncias Request Bases de cada Service.
Numa squad com 120 Services corporativos distintos enviando arquivos com HttpClient base nativos, exigir e obrigar que a base seja furada num Breaking Change gigante de Refatoração em cada Service é um crime funcional! O Interceptor do Angular funciona perfeitamente como Cascata Middleware, acoplável e seguro globalmente sem você editar uma letra dos Services.
Substituição de Liskov
A teoria define: "Subtipos devem ser perfeitamente substituíveis pelas suas classes-base sem corromper a consistência do sistema.".
Se o Serviço Base TS determina nos contratos rigorosos que tem um Output String na linha final, mas seu Componente Substituto "customizado" da squad resolve falhar essa regra de silêncio e mentir o Type retornando um Number cru onde a regra original era uma String, os Async Pipes e Filtros dos Observables derretem em cascata.
LSP - Exemplo 1 💻
O que evitar? Sub-versões de dublês de Mock trapaceando em types forçados.
O TypeScript confia plenamente na assinatura do contrato da classe-base. Se o Mock retorna null onde o sistema espera um objeto, qualquer acesso a propriedades desse retorno — como user.name ou user.id — vai lançar um TypeError em runtime, quebrando componentes que dependem do dado corretamente tipado.
LSP - Exemplo 2 💻
O que evitar? Perder ou jogar fora premissas em Componentes Extends.
Heranças hierárquicas em Componentes Angulares são baseadas de Promessas Absolutas. Se o UI Kit da Empresa decreta que *todo Button* semáforo aceitará ser disabled, o Desenvolvedor do SubComponent Customizado não pode simplesmente "Apagar" do código dele a funcionalidade bloqueadora de inputs. Isso fura segurança do Liskov nativo do app.
LSP - Exemplo 3 💻
O que evitar? A classe Derivada Emitir Formatos estranhos quebrando as Assinaturas Originais.
Causar crash no Runtime com uma bomba Type "any". Funções mestres conectadas ao seu Output ngClick engasgam completamente pois o programador original usava um método Substring/Replace nativo na recepção do Evento esperando o String Base Prometido. Lançar o payload falso (Ex: O Event pointer do click do DOM puro) detona o Javascript em Produção!
A Segregação de Interfaces
A teoria define: "Clientes não devem depender de lógicas e interfaces gigantes das quais na prática não utilizam.".
Pense puramente em micro-contratos! Chega de trafegar e vazar chaves de uma Database User completa na RAM apenas porque uma View Burra exigia o simples acesso minúsculo de exibir o campo `AvatarPicUrl` preso lá dentro do buraco.
ISP - Exemplo 1 💻
O que evitar? Jogar Componentes inteiros no State sendo que você precisa de Uma String.
Conhecido também pelos arquitetos como ("Data-Leak" de Scope). Espalhar componentes de interface pesados como Database Rows para trafegar no DOM, causa cópias profundas violentas de megabytes inúteis de Memória RAM na vida do Client-Side e fere brutamente o Isolamento Componentizado Básico de Views.
ISP - Exemplo 2 💻
O que evitar? Puxar o peso de Árvores de Diretório corporativas num Type solto.
Importações descontroladas acarretam problemas no Ciclo de Arquivos da sua Arquitetura Angular. O uso Mágico do Typescript Pick/Omit clona os contratos do BackEnd base em Miniaturas Blindadadas estritamente cirúrgicas eliminando a força de Acoplamento do iceberg da dependência Master!
ISP - Exemplo 3 💻
O que evitar? Você pede para o Service Injetor limpar um Cache, mas Força na Injeção acesso raiz destruidor ao construtor inteiro copiando até os acessos Write/Save e Auth!
Assine barreiras de Segurança na camada do próprio Injector. Modelando as Classes de Injeção em Sub-Tópicos nós proibiremos eternamente o seu programador Júnior de ativar acidentalmente chamadas arriscadas profundas destrutivas baseadas no Storage Main porque o Token barrou seu Component.
Inversão das Dependências
A teoria define: "Dependa inteiramente de camadas limitadas por abstrações seguras, não importe pacotes fixos cegamente".
O DI System fantástico do Angular brilha muito aqui. A lógica raiz de um Painel limpo Checkout Front-End nunca importa do NPM a marca do seu Gateway Atual de Testes (ex: MercadoPago SDK, Stripe Lib Base). O Token delega na Abstração a inversão limpa completa!
DIP - Exemplo 1 💻
O que evitar? O temível Vendor Lock-in! Importar referências estrábicas Npm nos construtores vitais Centrais.
O Dogma do Clean-Code impõe que Sua App é sagrada perante o Vendor (O Fornecedor das Libs Externas). Ao se trancar direto no Pacote "Datadog" o tempo todo, migrar sua Engine para Firebase futuramente demorará meses catando os cacos pra varrer cada string import! Delega O Provider central resolver e você será livre.
DIP - Exemplo 2 💻
O que evitar? Atropelar as estruturas pedindo Imports literais de configurações soltas locais.
Paths de Importação soltos geram dor física em Corporações com Repositórios Distribuídos Gigantes e Front-ends anabolizados pelo Micro-Frontends. Ao providenciar Inversão por Injection Tokens Genéricos o Engine permite virtualizar seus componentes base à quente sem encavalar caminhos de Build-Files.
DIP - Exemplo 3 💻
O que evitar? Subserviência direta entre Regra Custom Mestra do Cart e APIs SDK Pagadoras complexas.
Garantia Base para Testes Unitários de alta velocidade em CIs Remotos Cloud! Ao puxar um `import` raiz de Lib de terceiros enorme no módulo, levantar o container Angular isolado Mockado pro Karma TestRunner irá engasgar fatalmente quebrando tempo vital tentando imitar a arquitetura do BackEnd Alheio!
Quando estamos começando, nosso foco é 100% em "fazer funcionar e subir logo para produção". E tudo bem!
Porém, conforme suas bases crescem, o diferencial de um Developer Pleno ou Senior não é "jogar o HTML pra Main". É implementar sólidas defesas limpas em que o seu "eu-do-futuro" sobreviva à manutenção sem reescrever pedaços inteiros de projeto dezenas de vezes.
O Segredo: Não saia enfiando regras arbitrárias e mágicas de forma cega. Aplique os fundamentos matemáticos e veja a raiz dos erros sumirem!
Gostou das 15 Lendas explicadas pra valer nos 💻 Códigos?
Qual Princípio de Arquitetura S.O.L.I.D te dá mais problemas no dia a dia da equipe onde atua?
Manda nos comentários! 👇