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.