Pular para o conteúdo
← Voltar para o Skalablog

Artigo publicado

Como aplicar o princípio da responsabilidade única hoje

Engenharia de Software

A definição mais popular do princípio da responsabilidade única, 'uma classe, uma responsabilidade', é justamente a que menos ajuda. A versão útil é outra: uma classe deve ter apenas uma razão para mudar. É essa leitura que guia a refatoração abaixo.

O que é o princípio da responsabilidade única

O princípio da responsabilidade única (SRP) afirma que uma classe deve ter apenas uma razão para mudar. O S do SOLID, popularizado por Robert C. Martin no início dos anos 2000, não fala de quantas coisas a classe faz, mas de quantos motivos externos existem para você editar o arquivo dele (Agile Software Development, Robert C. Martin, 2002).

Essa leitura resolve a confusão mais comum. Perguntar 'quantas responsabilidades essa classe tem?' é subjetivo: quem decide o que conta como responsabilidade? Perguntar 'quantos atores pedem mudanças nessa classe?' é concreto. Se mudanças de validação, de regra de negócio e de banco de dados todas passam pelo mesmo arquivo, ele tem três razões para mudar.

Você pode lembrar desse conteúdo por aulas e mentorias que circulam na comunidade, como as de Felipe Rocha no Full Stack Club e materiais de nomes como Gustavo Dev Doido. A base, porém, é sempre a mesma ideia do SRP aplicada camada por camada.

Por que 'uma responsabilidade por classe' confunde mais do que ajuda

A definição 'uma classe pode ter apenas uma responsabilidade' parece simples e na prática não diz nada verificável. Toda classe faz várias coisas menores: formata strings, chama métodos, lança erros. Se você levar essa frase ao pé da letra, acaba fragmentando o código sem ganho real.

Já 'uma razão para mudar' dá um teste objetivo. Em uma aplicação web típica existem três motivos de mudança recorrentes, e cada um pertence a uma camada diferente:

  • Recepção e validação da requisição — mudou o formato dos dados de entrada? Você edita o driver.
  • Regras de negócio — agora e-mails podem se repetir? Você edita a application.
  • Persistência — trocou PostgreSQL por MongoDB? Você edita o resource.

Quando as três mudanças batem no mesmo handler, você tem um arquivo acoplado a três atores diferentes. É isso que o SRP proíbe.

Um handler Node.js que viola o SRP

O exemplo clássico é uma rota POST /users que cria usuário e faz tudo dentro do handler: valida o formato dos dados (e-mail válido, senha com mais de 8 caracteres), aplica a regra de negócio (verificar se já existe usuário com o mesmo e-mail) e persiste no banco com hash de senha. Um único arquivo, três motivos para mudar.

Na versão construída no vídeo de 2026 do canal Full Stack Club, o projeto usa Fastify, framework backend para Node.js focado em performance e tipagem, com Zod para validar e tipar o body das rotas e Drizzle ORM para falar com o PostgreSQL. A documentação do Fastify ainda sai pronta via integração com Swagger.

O problema não é o tamanho do arquivo, é o acoplamento. Mudar a validação do e-mail toca esse código. Mudar a regra de duplicidade toca esse código. Trocar o banco toca esse código. Cada mudança arrisca quebrar as outras duas.

Arquitetura hexagonal: o mapa das responsabilidades

A arquitetura hexagonal, proposta por Alistair Cockburn nos anos 2000, divide a aplicação em três camadas e é a forma mais direta de respeitar o SRP no backend (Hexagonal Architecture, Alistair Cockburn). Cada camada responde a uma única razão para mudar.

CamadaResponsabilidadeExemplo no artigo
DriverReceber a requisição e validar os dadosRota Fastify com schema Zod
ApplicationExecutar as regras de negócioUse case CreateUser
Resource (driven)Persistir dados ou chamar serviços externosUserDAO com Drizzle

O driver não sabe o que é regra de negócio. A application não conhece HTTP nem banco. O resource só conversa com o banco de dados ou com uma fila como SQS. Quando cada camada se limita ao seu papel, cada classe dentro dela herda a mesma disciplina: uma razão para mudar.

Refatoração passo a passo: do handler ao use case

A refatoração do vídeo segue uma sequência reproduzível. Ela funciona em qualquer stack com orientação a objetos, inclusive em projetos orientados por cursos como o Crazystack Typescript ou o Bootcamp do Dev Doido, porque depende apenas de separar camadas e não de uma biblioteca específica.

  1. Escreva (ou confirme) os testes antes de mexer em qualquer linha. Refatorar sem testes é editar às cegas: você não tem como saber se quebrou o comportamento.
  2. Crie o use case na pasta application. A classe CreateUser com um método execute recebe a regra de negócio que estava no handler: verificar e-mail duplicado, comparar senha com a confirmação e gerar o hash.
  3. Defina DTOs de entrada e saída. O use case não conhece request nem response; um InputDTO e um OutputDTO em TypeScript fixam o contrato entre driver e application. No output, por exemplo, a senha nunca volta.
  4. Crie erros customizados na application, como PasswordDoesntMatchError e EmailAlreadyInUseError. O use case lança o erro de negócio; o driver decide o status HTTP correspondente, 400 ou 409.
  5. Enxugue o handler. Ele passa a fazer três coisas: validar com Zod, chamar execute e mapear erros para respostas. Nada mais.

Um detalhe prático do vídeo: um teste que verificava a resposta 500 quando o banco fica inacessível foi removido durante a refatoração, porque a rota deixou de chamar o banco diretamente e o teste cobria um cenário em que todos os outros já falhariam. Nem todo teste sobrevive a uma mudança de arquitetura.

DAO: tirando a persistência do use case

Mesmo depois do passo anterior, o use case ainda tinha duas razões para mudar: regra de negócio e persistência. A solução é o padrão Data Access Object, descrito no livro Patterns of Enterprise Application Architecture de Martin Fowler (P of EAA, 2002): uma classe que chama o banco e retorna o que está lá, nada além disso.

O UserDAO criado em src/resources expõe dois métodos: create, que grava o usuário e retorna nulo se falhar, e findByEmail, que procura por e-mail e retorna nulo quando não encontra. O Drizzle fica confinado aqui dentro; o use case passa a depender do DAO e não sabe mais que o Drizzle existe.

Com isso, cada arquivo do projeto tem exatamente um motivo para mudar. Mudou a regra de e-mail duplicado? Você edita o CreateUser. Trocou o ORM? Você edita o DAO. Mudou o contrato da API? Você edita a rota. O commit do vídeo fecha o ciclo com add user dao.

Testes como rede de segurança da refatoração

O critério de sucesso da refatoração não é 'parece melhor', é 'os testes continuam passando'. O projeto do vídeo roda contra um PostgreSQL subido via Docker Compose, e os testes cobrem os cenários de negócio: e-mail duplicado retorna 409, senha e confirmação divergentes retornam 400.

Sem essa rede, mover código entre camadas é arriscado demais para valer a pena. A ordem recomendada é sempre a mesma: teste verde antes, refatora, teste verde depois. Se você não tem testes, escrever eles é o primeiro passo do SRP, não o último.

Perguntas frequentes sobre o princípio da responsabilidade única

  • O SRP significa que uma classe só pode ter um método? Não. Uma classe pode ter quantos métodos forem necessários, desde que todos respondam à mesma razão para mudar. O teste é quem pede mudanças nela, não a contagem de métodos.
  • Preciso de arquitetura hexagonal para aplicar o SRP? Não, mas a arquitetura hexagonal dá o mapa mais claro de onde cada responsabilidade mora: driver, application e resource. Você pode aplicar o SRP em pastas simples como controllers, services e repositories.
  • Use case e service são a mesma coisa? Na prática, ambos moram na camada de application e guardam regras de negócio. Use case costuma denotar uma operação única com um método execute; service pode agrupar operações relacionadas a uma entidade.
  • Como sei que uma classe viola o SRP? Liste os motivos externos que forçariam uma edição nela. Se você nomeia dois atores diferentes (o time de produto mudando regra e o time de infraestrutura mudando banco), há violação.
  • O SRP vale só para classes? O princípio se aplica a qualquer unidade de código: módulos, funções e camadas. Em uma API REST, a leitura por camadas é a aplicação mais útil no dia a dia.

Transforme aulas em vídeo em artigos com o Skala Blog

Separar uma razão para mudar por camada é o que torna um código entendível meses depois. O mesmo vale para conteúdo: uma aula de trinta minutos sobre SRP vira um texto muito mais útil quando você extrai dela a estrutura, os passos e o exemplo.

Se você tem conhecimento valioso gravado em vídeos do YouTube, aulas, mentorias, entrevistas ou opiniões, o Skala Blog transforma esse material em artigo: você cola a URL do vídeo, o conteúdo é transcrito e vira um texto estruturado e pronto para revisar. É a sua aula sobre arquitetura, agora em formato que dá para buscar, citar e reler.

Source video