SOLID: O Que Ninguém Te Explicou Sobre Responsabilidade Única! (Parte 1)
Tenho algo a acrescentar!
É um assunto bastante "batido" mas realmente pouco compreendido.
Depois de alguns artigos sobre padrões de projeto (Design Patterns), vou falar um pouco de SOLID. Vamos começar pelo “S” de Single Responsibility Principle (SRP).
O erro clássico: “uma responsabilidade = uma coisa pequena”
Muita gente ensina SRP como se fosse regra de estética: “não pode ter mais de um método”, “não pode importar mais de uma coisa”, “controller não pode ter duas actions”. O problema é que isso confunde tamanho com motivo de mudança.
A definição que realmente importa
Uma versão madura de SRP (para módulos/classes) é:
Um módulo deve ter um, e apenas um, motivo para mudar.
“Motivo” aqui está ligado a quem sofre quando você muda: o time de pagamentos, o time de relatórios, a regulamentação fiscal, etc.
Se uma classe muda porque:
mudou regra de negócio, mudou formato de log e mudou detalhe de SQL
… provavelmente você misturou razões diferentes na mesma unidade.
Exemplo na prática (antes e depois)
Antes: tudo no mesmo lugar
<?php
final class OrderService
{
public function placeOrder(array $payload): void
{
// valida payload
// grava pedido no banco
// envia e-mail
// escreve log em arquivo
}
}
Explicação: funciona no curto prazo, mas qualquer mudança em persistência, e-mail ou log exige tocar no mesmo serviço. O risco é alto e os testes ficam pesados.
Depois: separar motivos
<?php
interface OrderRepository
{
public function save(Order $order): void;
}
interface Notifier
{
public function notifyOrderPlaced(Order $order): void;
}
interface AuditLogger
{
public function info(string $message, array $context = []): void;
}
final readonly class OrderService
{
public function __construct(
private OrderRepository $orders,
private Notifier $notifier,
private AuditLogger $logger,
) {}
public function placeOrder(Order $order): void
{
$this->orders->save($order);
$this->notifier->notifyOrderPlaced($order);
$this->logger->info('Pedido criado', ['orderId' => $order->id()]);
}
}
Explicação: OrderService passa a orquestrar o caso de uso, mas não implementa persistência, e-mail ou log. Cada colaborador pode mudar por motivos diferentes, e você consegue testar o serviço com dublês simples.
Isso não diz que você precisa de 300 interfaces amanhã. Diz que fronteiras ajudam quando os motivos de mudança são reais.
SRP não é “nunca misturar”
Orquestração é uma responsabilidade legítima. Um PlaceOrderHandler pode “saber” do fluxo, o problema é ele fazer tudo ao mesmo tempo.
Conclusão
SRP é sobre acoplamento de razões, não sobre micro-arquivos. Pergunte: “se eu mudar X, por que essa classe teria que mudar?” Se a resposta vira uma lista heterogênea, separe.
Referências
- Clean Architecture / artigos sobre SOLID.