🚀 Oferta especial: 60% OFF no CrazyStack - Últimas vagas!Garantir vaga →
Clean Code

Os 5 pilares do código limpo SOLID Principles

Transforme seu código TypeScript em uma obra de arte com os princípios SOLID

Guia Definitivo

5 regras que todo dev sênior domina SOLID Principles

Pare de escrever código que quebra. Aprenda os princípios que separam devs júnior de sênior.

5
Princípios
80%
Menos bugs
3x
Mais rápido
solid-example.ts
// ❌ Código que quebra class User { save() { /* salva no banco */ } sendEmail() { /* envia email */ } validateCPF() { /* valida CPF */ } generateReport() { /* gera relatório */ } } // ✅ Código SOLID class User { constructor(public name: string) {} } class UserRepository { save(user: User) {} } class EmailService { send(to: string, message: string) {} } class CPFValidator { validate(cpf: string): boolean {} } class ReportGenerator { generate(user: User) {} }

Por que isso é importante

Código que não quebra

SOLID previne 80% dos bugs mais comuns. Seu código funciona e continua funcionando.

Desenvolvimento 3x mais rápido

Menos tempo debugando, mais tempo criando. Features novas sem medo.

Carreira acelerada

SOLID é requisito para dev sênior. Diferencial em entrevistas técnicas.

Trabalho em equipe

Código que outros devs entendem. Onboarding sem dor de cabeça.

Os 5 Princípios SOLID

Cada letra representa um princípio fundamental do código limpo.

S

Single Responsibility Principle

Uma classe, uma responsabilidade

❌ Violando o princípio:

class User {
  constructor(public name: string, public email: string) {}
  
  // Responsabilidade 1: Validação
  validateEmail(): boolean {
    return /^[^s@]+@[^s@]+.[^s@]+$/.test(this.email);
  }
  
  // Responsabilidade 2: Persistência
  save(): void {
    // Salva no banco de dados
    console.log('Salvando usuário...');
  }
  
  // Responsabilidade 3: Notificação
  sendWelcomeEmail(): void {
    // Envia email de boas-vindas
    console.log('Enviando email...');
  }
  
  // Responsabilidade 4: Relatórios
  generateReport(): string {
    return `Relatório do usuário ${this.name}`;
  }
}

✅ Respeitando o princípio:

// Uma responsabilidade por classe
class User {
  constructor(public name: string, public email: string) {}
}

class EmailValidator {
  validate(email: string): boolean {
    return /^[^s@]+@[^s@]+.[^s@]+$/.test(email);
  }
}

class UserRepository {
  save(user: User): void {
    console.log('Salvando usuário...');
  }
}

class EmailService {
  sendWelcomeEmail(user: User): void {
    console.log(`Enviando email para ${user.email}`);
  }
}

class UserReportGenerator {
  generate(user: User): string {
    return `Relatório do usuário ${user.name}`;
  }
}

🎯 Resultado: Cada classe tem um motivo para mudar. Testes isolados e código mais limpo.

O

Open-Closed Principle

Aberto para extensão, fechado para modificação

❌ Violando o princípio:

function calculateDiscount(customer: Customer): number {
  switch (customer.type) {
    case 'regular':
      return customer.total * 0.05;
    case 'premium':
      return customer.total * 0.1;
    case 'vip':
      return customer.total * 0.2;
    // Precisa modificar para adicionar novo tipo
    default:
      return 0;
  }
}

✅ Respeitando o princípio:

abstract class Customer {
  constructor(public total: number) {}
  abstract getDiscount(): number;
}

class RegularCustomer extends Customer {
  getDiscount(): number {
    return this.total * 0.05;
  }
}

class PremiumCustomer extends Customer {
  getDiscount(): number {
    return this.total * 0.1;
  }
}

// Novo tipo SEM modificar código existente
class VIPCustomer extends Customer {
  getDiscount(): number {
    return this.total * 0.2;
  }
}

🎯 Resultado: Novas funcionalidades sem quebrar o que já funciona. Zero regressões.

L

Liskov Substitution Principle

Subclasses devem ser substituíveis por suas classes base

❌ Violando o princípio:

class Bird {
  fly(): void {
    console.log('Voando...');
  }
}

class Penguin extends Bird {
  fly(): void {
    // Pinguim não voa!
    throw new Error('Pinguins não voam!');
  }
}

// Quebra quando usa Penguin
function makeBirdFly(bird: Bird): void {
  bird.fly(); // Pode dar erro!
}

✅ Respeitando o princípio:

abstract class Bird {
  abstract move(): void;
}

class FlyingBird extends Bird {
  move(): void {
    console.log('Voando...');
  }
}

class SwimmingBird extends Bird {
  move(): void {
    console.log('Nadando...');
  }
}

class Eagle extends FlyingBird {}
class Penguin extends SwimmingBird {}

// Funciona com qualquer Bird
function makeBirdMove(bird: Bird): void {
  bird.move(); // Sempre funciona!
}

🎯 Resultado: Polimorfismo que funciona. Sem surpresas desagradáveis.

I

Interface Segregation Principle

Muitas interfaces específicas são melhores que uma geral

❌ Violando o princípio:

// Interface muito gorda
interface Worker {
  work(): void;
  eat(): void;
  sleep(): void;
  code(): void;
  design(): void;
  manage(): void;
}

// Robot precisa implementar tudo!
class Robot implements Worker {
  work(): void { console.log('Trabalhando...'); }
  eat(): void { throw new Error('Robôs não comem!'); }
  sleep(): void { throw new Error('Robôs não dormem!'); }
  code(): void { throw new Error('Robôs não programam!'); }
  design(): void { throw new Error('Robôs não fazem design!'); }
  manage(): void { throw new Error('Robôs não gerenciam!'); }
}

✅ Respeitando o princípio:

// Interfaces específicas
interface Workable {
  work(): void;
}

interface Eatable {
  eat(): void;
}

interface Sleepable {
  sleep(): void;
}

interface Programmable {
  code(): void;
}

// Cada classe implementa só o que precisa
class Robot implements Workable {
  work(): void {
    console.log('Robô trabalhando...');
  }
}

class Developer implements Workable, Eatable, Sleepable, Programmable {
  work(): void { console.log('Desenvolvendo...'); }
  eat(): void { console.log('Comendo...'); }
  sleep(): void { console.log('Dormindo...'); }
  code(): void { console.log('Programando...'); }
}

🎯 Resultado: Classes implementam apenas o que realmente usam. Sem métodos vazios.

D

Dependency Inversion Principle

Dependa de abstrações, não de implementações

❌ Violando o princípio:

class EmailService {
  send(message: string): void {
    console.log(`Enviando email: ${message}`);
  }
}

// Acoplado diretamente ao EmailService
class NotificationManager {
  private emailService = new EmailService();
  
  notify(message: string): void {
    // Só pode enviar email!
    this.emailService.send(message);
  }
}

✅ Respeitando o princípio:

// Abstração
interface NotificationService {
  send(message: string): void;
}

class EmailService implements NotificationService {
  send(message: string): void {
    console.log(`Email: ${message}`);
  }
}

class SMSService implements NotificationService {
  send(message: string): void {
    console.log(`SMS: ${message}`);
  }
}

// Depende da abstração
class NotificationManager {
  constructor(private service: NotificationService) {}
  
  notify(message: string): void {
    this.service.send(message);
  }
}

// Uso flexível
const emailManager = new NotificationManager(new EmailService());
const smsManager = new NotificationManager(new SMSService());

🎯 Resultado: Código flexível e testável. Fácil de trocar implementações.

Exemplo Prático: Sistema de Pedidos

Veja todos os princípios SOLID trabalhando juntos.

🛒 Sistema de E-commerce SOLID

1. Entidades e Interfaces (S + I):

// S: Cada classe tem uma responsabilidade
class Product {
  constructor(
    public id: string,
    public name: string,
    public price: number
  ) {}
}

class Order {
  constructor(
    public id: string,
    public customerId: string,
    public items: OrderItem[] = []
  ) {}
  
  getTotal(): number {
    return this.items.reduce((sum, item) => sum + item.getSubtotal(), 0);
  }
}

class OrderItem {
  constructor(
    public product: Product,
    public quantity: number
  ) {}
  
  getSubtotal(): number {
    return this.product.price * this.quantity;
  }
}

// I: Interfaces específicas
interface OrderRepository {
  save(order: Order): Promise<void>;
  findById(id: string): Promise<Order | null>;
}

interface PaymentProcessor {
  process(amount: number, method: string): Promise<boolean>;
}

interface NotificationSender {
  send(message: string, recipient: string): Promise<void>;
}

2. Processamento de Desconto (O + L):

// O: Aberto para extensão, fechado para modificação
abstract class DiscountCalculator {
  abstract calculate(order: Order): number;
}

// L: Substituição de Liskov - todas funcionam igual
class NoDiscount extends DiscountCalculator {
  calculate(order: Order): number {
    return 0;
  }
}

class PercentageDiscount extends DiscountCalculator {
  constructor(private percentage: number) {
    super();
  }
  
  calculate(order: Order): number {
    return order.getTotal() * (this.percentage / 100);
  }
}

class FixedAmountDiscount extends DiscountCalculator {
  constructor(private amount: number) {
    super();
  }
  
  calculate(order: Order): number {
    return Math.min(this.amount, order.getTotal());
  }
}

class BuyTwoGetOneDiscount extends DiscountCalculator {
  calculate(order: Order): number {
    let discount = 0;
    order.items.forEach(item => {
      const freeItems = Math.floor(item.quantity / 3);
      discount += freeItems * item.product.price;
    });
    return discount;
  }
}

3. Serviço Principal (D):

// D: Dependency Inversion - depende de abstrações
class OrderService {
  constructor(
    private orderRepository: OrderRepository,
    private paymentProcessor: PaymentProcessor,
    private notificationSender: NotificationSender,
    private discountCalculator: DiscountCalculator
  ) {}
  
  async processOrder(order: Order, paymentMethod: string): Promise<boolean> {
    try {
      // Calcula desconto
      const discount = this.discountCalculator.calculate(order);
      const finalAmount = order.getTotal() - discount;
      
      // Processa pagamento
      const paymentSuccess = await this.paymentProcessor.process(
        finalAmount, 
        paymentMethod
      );
      
      if (!paymentSuccess) {
        throw new Error('Falha no pagamento');
      }
      
      // Salva pedido
      await this.orderRepository.save(order);
      
      // Envia notificação
      await this.notificationSender.send(
        `Pedido ${order.id} confirmado! Total: R$ ${finalAmount}`,
        order.customerId
      );
      
      return true;
    } catch (error) {
      console.error('Erro ao processar pedido:', error);
      return false;
    }
  }
}

4. Implementações Concretas:

// Implementações específicas
class DatabaseOrderRepository implements OrderRepository {
  async save(order: Order): Promise<void> {
    console.log(`Salvando pedido ${order.id} no banco`);
  }
  
  async findById(id: string): Promise<Order | null> {
    console.log(`Buscando pedido ${id}`);
    return null; // Simulação
  }
}

class StripePaymentProcessor implements PaymentProcessor {
  async process(amount: number, method: string): Promise<boolean> {
    console.log(`Processando R$ ${amount} via ${method}`);
    return true; // Simulação
  }
}

class EmailNotificationSender implements NotificationSender {
  async send(message: string, recipient: string): Promise<void> {
    console.log(`Email para ${recipient}: ${message}`);
  }
}

// Uso do sistema
const orderService = new OrderService(
  new DatabaseOrderRepository(),
  new StripePaymentProcessor(),
  new EmailNotificationSender(),
  new PercentageDiscount(10) // 10% de desconto
);

const order = new Order('001', 'customer123', [
  new OrderItem(new Product('p1', 'Notebook', 2000), 1),
  new OrderItem(new Product('p2', 'Mouse', 50), 2)
]);

await orderService.processOrder(order, 'credit_card');

🎯 Resultado: Sistema flexível, testável e extensível. Cada princípio SOLID em ação!

Checklist SOLID

Como aplicar os princípios SOLID no seu código.

✅ Faça Isso

  • Uma responsabilidade por classe
  • Use herança e polimorfismo
  • Subclasses substituíveis
  • Interfaces específicas
  • Dependency injection
  • Testes unitários isolados

❌ Evite Isso

  • Classes com múltiplas responsabilidades
  • Switch/case gigantes
  • Herança que quebra comportamento
  • Interfaces muito grandes
  • Acoplamento forte
  • Instanciação direta de dependências

🎯 Resumo SOLID

S
Single Responsibility
O
Open-Closed
L
Liskov Substitution
I
Interface Segregation
D
Dependency Inversion

"Clean code is not written by following a set of rules. Clean code is written by programmers who care." - Robert C. Martin

Domine TypeScript Profissionalmente

Aprenda SOLID, Design Patterns e Clean Architecture com projetos reais.