Conceito e intenção
Chain of Responsibility passa uma requisição por uma cadeia de handlers. Cada handler decide se processa a requisição ou a repassa para o próximo handler na cadeia. O padrão desacopla o remetente dos processadores — o remetente não sabe quais handlers existem, nem qual deles vai processar a requisição.
O problema motivador é a validação ou processamento em camadas. Um pedido de compra pode precisar passar por validação de estoque, verificação de limite de crédito, análise de fraude e aprovação de gerente para pedidos acima de determinado valor. Sem Chain of Responsibility, a lógica seria um if gigante ou chamadas sequenciais explícitas acopladas. Com Chain, cada handler é independente, testável e a ordem pode ser configurada em runtime.
A diferença em relação ao Observer: Observer notifica todos os ouvintes. Chain of Responsibility para quando um handler processa a requisição (ou a rejeita). São propósitos distintos.
Estrutura
Sender ──▶ H1 ──▶ H2 ──▶ H3 ──▶ null (fim)
processou? processou?
não → passa sim → para┌─────────────────────────────────────────┐
│ <<interface>> Handler │
│ + handle(request) │
│ + setNext(handler) │
└─────────────────────────────────────────┘
▲
┌─────────┴────────┐
┌──────┴──────┐ ┌──────┴──────┐
│ HandlerA │ │ HandlerB │
│ - next │ │ - next │
│ + handle() │ │ + handle() │
└─────────────┘ └─────────────┘Participantes:
- Handler — interface com
handle()e referência ao próximo handler - AbstractHandler — implementação base que cuida de repassar ao próximo
- ConcreteHandler — processa ou repassa baseado em sua responsabilidade
Implementação Java — pipeline de aprovação de pedido
// Requisição que percorre a cadeia
public record OrderApprovalRequest(
Order order,
Customer customer,
List<String> approvalNotes // acumula anotações ao longo da cadeia
) {
public OrderApprovalRequest withNote(String note) {
List<String> newNotes = new ArrayList<>(approvalNotes);
newNotes.add(note);
return new OrderApprovalRequest(order, customer, Collections.unmodifiableList(newNotes));
}
}
// Resultado da cadeia
public record ApprovalResult(
boolean approved,
String reason,
List<String> notes
) {
public static ApprovalResult approve(List<String> notes) {
return new ApprovalResult(true, "Aprovado", notes);
}
public static ApprovalResult reject(String reason, List<String> notes) {
return new ApprovalResult(false, reason, notes);
}
}
// Handler interface
public interface OrderHandler {
ApprovalResult handle(OrderApprovalRequest request);
OrderHandler setNext(OrderHandler next);
}
// Base abstrata — cuida de repassar ao próximo
public abstract class AbstractOrderHandler implements OrderHandler {
private OrderHandler next;
@Override
public OrderHandler setNext(OrderHandler next) {
this.next = next;
return next; // permite encadeamento fluente: a.setNext(b).setNext(c)
}
protected ApprovalResult passToNext(OrderApprovalRequest request) {
if (next != null) return next.handle(request);
return ApprovalResult.approve(request.approvalNotes()); // fim da cadeia = aprovado
}
}
// Handler 1: Verificação de estoque
public class StockHandler extends AbstractOrderHandler {
private final InventoryService inventory;
public StockHandler(InventoryService inventory) {
this.inventory = inventory;
}
@Override
public ApprovalResult handle(OrderApprovalRequest request) {
for (OrderItem item : request.order().getItems()) {
if (!inventory.hasAvailable(item.getSku(), item.getQuantity())) {
return ApprovalResult.reject(
"Sem estoque para: " + item.getSku(),
request.approvalNotes()
);
}
}
OrderApprovalRequest updated = request.withNote("Estoque verificado: OK");
return passToNext(updated);
}
}
// Handler 2: Limite de crédito
public class CreditLimitHandler extends AbstractOrderHandler {
private final CustomerService customerService;
public CreditLimitHandler(CustomerService customerService) {
this.customerService = customerService;
}
@Override
public ApprovalResult handle(OrderApprovalRequest request) {
Money creditLimit = customerService.getCreditLimit(request.customer().getId());
if (request.order().getTotal().isGreaterThan(creditLimit)) {
return ApprovalResult.reject(
String.format("Total R$%.2f excede limite de crédito R$%.2f",
request.order().getTotal().toDouble(), creditLimit.toDouble()),
request.approvalNotes()
);
}
return passToNext(request.withNote("Crédito verificado: OK"));
}
}
// Handler 3: Análise de fraude
public class FraudHandler extends AbstractOrderHandler {
private final FraudDetectionService fraudService;
public FraudHandler(FraudDetectionService fraudService) {
this.fraudService = fraudService;
}
@Override
public ApprovalResult handle(OrderApprovalRequest request) {
FraudScore score = fraudService.evaluate(request.order(), request.customer());
if (score.isHighRisk()) {
return ApprovalResult.reject(
"Pedido suspeito — bloqueado para análise (score: " + score.getValue() + ")",
request.approvalNotes()
);
}
if (score.isMediumRisk()) {
// Não rejeita, mas adiciona nota de alerta
return passToNext(request.withNote("Risco médio de fraude — monitorar"));
}
return passToNext(request.withNote("Análise de fraude: OK"));
}
}
// Handler 4: Aprovação gerencial para pedidos grandes
public class ManagerApprovalHandler extends AbstractOrderHandler {
private final Money threshold;
private final ApprovalService approvalService;
public ManagerApprovalHandler(Money threshold, ApprovalService approvalService) {
this.threshold = threshold;
this.approvalService = approvalService;
}
@Override
public ApprovalResult handle(OrderApprovalRequest request) {
if (request.order().getTotal().isLessThan(threshold)) {
return passToNext(request); // abaixo do limite — não precisa de aprovação
}
// Acima do limite — solicita aprovação gerencial
ApprovalTicket ticket = approvalService.requestApproval(
request.order().getId(),
request.customer().getId(),
request.order().getTotal()
);
if (!ticket.isApproved()) {
return ApprovalResult.reject(
"Aprovação gerencial negada: " + ticket.getReason(),
request.approvalNotes()
);
}
return passToNext(request.withNote("Aprovação gerencial: " + ticket.getApproverName()));
}
}
// Montagem da cadeia — ordem é importante e configurável
@Configuration
public class OrderHandlerConfig {
@Bean
public OrderHandler orderApprovalChain(
InventoryService inventory,
CustomerService customers,
FraudDetectionService fraud,
ApprovalService approval) {
StockHandler stock = new StockHandler(inventory);
CreditLimitHandler credit = new CreditLimitHandler(customers);
FraudHandler fraudCheck = new FraudHandler(fraud);
ManagerApprovalHandler mgr = new ManagerApprovalHandler(Money.of(10_000), approval);
// Encadeamento fluente
stock.setNext(credit).setNext(fraudCheck).setNext(mgr);
return stock; // retorna o primeiro da cadeia
}
}
// Uso — o chamador só conhece o início da cadeia
@Service
public class OrderApprovalService {
private final OrderHandler approvalChain;
public OrderApprovalService(OrderHandler approvalChain) {
this.approvalChain = approvalChain;
}
public ApprovalResult approve(Order order, Customer customer) {
OrderApprovalRequest request = new OrderApprovalRequest(order, customer, List.of());
return approvalChain.handle(request);
}
}Middleware HTTP — padrão de cadeia em frameworks web
Chain of Responsibility é a base de todos os sistemas de middleware:
// Padrão de Servlet Filter (Jakarta EE)
@WebFilter("/*")
public class AuthenticationFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) request;
String token = httpReq.getHeader("Authorization");
if (token == null || !tokenService.isValid(token)) {
((HttpServletResponse) response).sendError(401, "Unauthorized");
return; // para a cadeia
}
chain.doFilter(request, response); // passa para o próximo filtro
}
}
@WebFilter("/*")
public class RateLimitFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
String clientIp = request.getRemoteAddr();
if (rateLimiter.isExceeded(clientIp)) {
((HttpServletResponse) response).sendError(429, "Too Many Requests");
return; // para a cadeia
}
chain.doFilter(request, response);
}
}Variação funcional (Java 8+)
// Handler como função — sem classes concretas
@FunctionalInterface
public interface OrderValidator {
Optional<String> validate(Order order, Customer customer);
}
public class ValidationChain {
private final List<OrderValidator> validators = new ArrayList<>();
public ValidationChain add(OrderValidator validator) {
validators.add(validator);
return this;
}
public ApprovalResult validate(Order order, Customer customer) {
return validators.stream()
.map(v -> v.validate(order, customer))
.filter(Optional::isPresent)
.findFirst()
.map(err -> ApprovalResult.reject(err.get(), List.of()))
.orElse(ApprovalResult.approve(List.of()));
}
}
// Validadores como lambdas
ValidationChain chain = new ValidationChain()
.add((order, customer) ->
inventory.hasAll(order.getItems())
? Optional.empty()
: Optional.of("Sem estoque"))
.add((order, customer) ->
order.getTotal().isLessThan(customer.getCreditLimit())
? Optional.empty()
: Optional.of("Limite excedido"))
.add((order, customer) ->
!fraudService.evaluate(order, customer).isHighRisk()
? Optional.empty()
: Optional.of("Suspeita de fraude"));Implementação TypeScript
// Request que percorre a cadeia
interface OrderRequest {
orderId: string;
customerId: string;
total: number;
items: OrderItem[];
}
interface HandlerResult {
approved: boolean;
reason?: string;
notes: string[];
}
// Handler interface
interface Handler {
handle(request: OrderRequest): Promise<HandlerResult>;
setNext(handler: Handler): Handler;
}
// Classe base abstrata
abstract class BaseHandler implements Handler {
private next: Handler | null = null;
setNext(handler: Handler): Handler {
this.next = handler;
return handler;
}
protected async passToNext(request: OrderRequest & { notes: string[] }): Promise<HandlerResult> {
if (this.next) return this.next.handle(request);
return { approved: true, notes: request.notes };
}
}
// Handler concreto
class StockHandler extends BaseHandler {
constructor(private readonly inventory: InventoryService) {
super();
}
async handle(request: OrderRequest & { notes?: string[] }): Promise<HandlerResult> {
const notes = request.notes ?? [];
const inStock = await this.inventory.hasAll(request.items);
if (!inStock) return { approved: false, reason: 'Sem estoque', notes };
return this.passToNext({ ...request, notes: [...notes, 'Estoque: OK'] });
}
}
class FraudHandler extends BaseHandler {
constructor(private readonly fraudService: FraudService) {
super();
}
async handle(request: OrderRequest & { notes?: string[] }): Promise<HandlerResult> {
const notes = request.notes ?? [];
const score = await this.fraudService.evaluate(request);
if (score > 0.8) return { approved: false, reason: 'Suspeita de fraude', notes };
return this.passToNext({ ...request, notes: [...notes, `Fraude score: ${score.toFixed(2)}`] });
}
}
// Montagem e uso
const stockHandler = new StockHandler(inventoryService);
const fraudHandler = new FraudHandler(fraudService);
stockHandler.setNext(fraudHandler);
const result = await stockHandler.handle(orderRequest);
if (!result.approved) throw new Error(result.reason);No mundo real
Servlet Filter Chain (FilterChain) — cada filtro é um handler que pode processar a requisição HTTP ou repassar com chain.doFilter(). Authentication, CORS, Rate Limiting, Logging são filtros típicos.
Spring Security filter chain — série de filtros (UsernamePasswordAuthenticationFilter, JwtAuthenticationFilter, ExceptionTranslationFilter) aplicados em ordem.
Express.js middleware — app.use((req, res, next) => { ... next(); }) é Chain of Responsibility. next() repassa para o próximo handler; não chamar next() para a cadeia.
Spring MVC HandlerInterceptor — preHandle() retorna false para interromper a cadeia; true para continuar.
javax.servlet.Filter no Tomcat/Jetty — toda requisição passa pela cadeia de filtros configurada no web.xml ou por anotações.
Apache Camel Processor chains — pipelines de integração onde cada processador transforma ou roteia a mensagem.
Quando usar
- Mais de um objeto pode processar a requisição e não é sabido a priori qual processará
- Pipeline de validação onde múltiplas regras são aplicadas em sequência
- Middleware HTTP ou de mensagens onde filtros podem interceptar e parar o fluxo
- Quando a ordem dos processadores precisa ser configurada dinamicamente
- Sistemas de autorização em camadas (autenticação → autorização → rate limiting → logging)
Quando NÃO usar
- Quando a cadeia precisa garantir que todos os handlers sejam executados — use Observer
- Quando a cadeia pode crescer indefinidamente sem garantia de processamento — verifique que a cadeia sempre termina
- Quando a lógica de roteamento é simples e não precisa de extensibilidade —
if/elsesequencial é mais legível - Para cálculos onde todos os handlers contribuem ao resultado — use um aggregator/reducer em vez de chain
Observer vs Chain of Responsibility
| Aspecto | Observer | Chain of Responsibility |
|---|---|---|
| Quem processa | Todos os ouvintes | Um ou nenhum handler |
| Pode parar | Não (todos são notificados) | Sim (handler para a cadeia) |
| Acoplamento | Subject não conhece observers | Handlers conhecem o próximo |
| Uso típico | Eventos, reações assíncronas | Validação, autorização, middleware |
Combinações com outros padrões
Chain of Responsibility + Command: Cada handler pode receber um Command como requisição, processá-lo ou repassar. A fila de Commands pode ser processada por uma chain.
Chain of Responsibility + Decorator: Ambos envolvem sequências de processamento, mas Chain pode interromper o fluxo; Decorator sempre chama o próximo. Em middlewares HTTP, a linha é tênue — alguns frameworks usam Decorator (sempre executa), outros usam Chain (pode parar).
Chain of Responsibility + Template Method: O handle() da classe base pode ser um Template Method que define o esqueleto: verificar condição, processar se aplicável, repassar se não. Subclasses implementam apenas a condição e o processamento específico.
Chain of Responsibility + Composite: Uma “chain” pode conter nós que são eles mesmos chains menores — util para agrupar validações relacionadas em subgrupos.