O termo S.O.L.I.D é um acrônimo que representa cinco princípios da programação orientada a objetos, criados para ajudar desenvolvedores a escreverem códigos mais organizados, reutilizáveis e fáceis de manter.
Esses princípios não são teóricos — eles têm impacto real no dia a dia de quem desenvolve sistemas complexos, especialmente em domínios como o financeiro, onde regras de negócio mudam com frequência e o código precisa acompanhar.
Vamos explicar cada um deles com exemplos práticos baseados em um sistema de contas bancárias e transações.
Single Responsibility Principle
Uma classe deve ter apenas um motivo para mudar.
Imagine que temos uma classe TransactionService que cria transações, valida o valor e ainda envia e-mails de confirmação. Isso é um problema, porque se amanhã o time de negócios mudar a regra de validação ou o modelo do e-mail, a mesma classe teria que ser alterada. Isso aumenta o acoplamento e o risco de bugs.
O correto é separar responsabilidades:
- TransactionService → processa a transação
- TransactionValidator → aplica regras de validação
- EmailService → envia notificações
Dessa forma, se o layout do e-mail mudar, só o EmailService é modificado. Esse princípio parece simples, mas ele é a base de um código manutenível. É aqui que começa a diferença entre um sistema que envelhece mal e um que escala bem.
Open/Closed Principle
Você deve poder adicionar novos comportamentos sem alterar o código existente.
Em um sistema financeiro, imagine uma classe FeeCalculator que calcula taxas com vários ifs:
if (type = 'pix') { ... }
if (type = 'ted') { ... }
Funciona no início, mas com o tempo o código vira um pesadelo. Cada novo tipo de transação exige editar a mesma classe.
A aplicação correta do princípio é usar polimorfismo. Crio uma interface FeeStrategy e implemento classes como PixFee, TedFee, BoletoFee. Se surgir um novo tipo de transação, só adiciono uma nova classe, sem tocar no código existente.
Isso é o coração da extensibilidade. É o que permite que um sistema evolua sem virar uma bomba de manutenção.
Liskov Substitution Principle
As subclasses devem poder substituir suas superclasses sem quebrar o comportamento do sistema.
Esse é um dos princípios mais ignorados, mas é bom termos um acerta atenção. Imagine que temos uma classe Account com o método withdraw(). Agora criamos uma subclasse FixedDepositAccount, que não permite saque, e jogamos uma exceção quando withdraw() é chamado.
O problema é que quem usa Account espera poder sacar. Quando recebe FixedDepositAccount, o comportamento muda. Isso quebra o contrato da abstração.
interface Withdrawable {
withdraw(amount: number): void;
}
CheckingAccount implementa Withdrawable, FixedDepositAccount não. Simples, limpo e sem surpresas no comportamento. Respeitar o Liskov é respeitar a previsibilidade do seu sistema.
Interface Segregation Principle
Os clientes não devem ser forçados a depender de métodos que não usam.
Esse é um dos mais fáceis de ver no dia a dia. Se eu tiver uma interface AccountActions com métodos deposit, withdraw e requestLoan, estou obrigando todas as contas a implementar os três, mesmo que nem todas façam empréstimo.
Isso é ruim, porque cria implementações falsas, cheias de exceções desnecessárias. O certo é dividir as interfaces:
interface Depositable { deposit(amount: number): void; }
interface Withdrawable { withdraw(amount: number): void; }
interface LoanRequester { requestLoan(amount: number): void; }
Assim, cada classe implementa apenas o que faz sentido. Uma conta poupança não precisa se preocupar com empréstimos, e uma conta de crédito pode implementar o que for necessário.
Esse princípio é sobre clareza e precisão de contratos. Cada classe deve depender só do que realmente usa.
Dependency Inversion Principle
Dependa de abstrações, não de implementações concretas.
Suponha que o serviço ReportService dependa diretamente de MySQLReportRepository. Se um dia quisermos mudar para PostgreSQL ou uma API externa, teremos que alterar o serviço — o que não deveria acontecer. A correção vem com abstração:
interface ReportRepository {
getData(): any;
}
MySQLReportRepository e PostgreSQLReportRepository implementam essa interface. O ReportService recebe o repositório por injeção de dependência. Agora o código não depende mais de um banco específico — ele depende de uma contrato estável.
É o princípio que fecha o ciclo do SOLID, garantindo baixo acoplamento e alta flexibilidade.