Ao criar uma camada de rede, você inevitavelmente chega a um momento assim.
Uma única requisição precisa de token de autenticação, logs e novas tentativas em caso de falha.
Ao colocar tudo em uma função, send() logo chega a 200 linhas.
É nesse momento que entra o padrão middleware.
A conclusão: em Swift, a abordagem mais limpa é envolver cada funcionalidade com o padrão Decorator e conectar esses wrappers em ordem usando Chain of Responsibility. Eles não competem; formam uma dupla.
Hoje veremos por que usar essa combinação e como montá-la com um exemplo prático.
O que é o padrão middleware?
Middleware é uma camada intermediária de processamento entre a requisição e a resposta.
Se você já usou o RequestInterceptor do Alamofire ou o Middleware do Vapor no servidor, já conhece esse conceito.
A ideia central é simples.
Manter o núcleo que processa a requisição intacto e permitir adicionar ou remover funcionalidades ao seu redor.
Criar autenticação, logs, cache e novas tentativas como componentes independentes.
Assim, você pode combiná-los livremente: apenas autenticação em uma requisição; autenticação e cache em outra.
Qual é a diferença entre Decorator e Chain of Responsibility?
Muita gente confunde esses dois conceitos.
Resumindo, a diferença é esta.
| Categoria | Decorator | Chain of Responsibility |
|---|---|---|
| Objetivo | Adicionar funcionalidade a um objeto existente | Colocar vários processadores em sequência |
| Relação | Estrutura de wrappers (aninhada) | Estrutura conectada (cadeia) |
| Continuidade | Sempre encaminha para o próximo | Pode parar no meio |
| Papel no middleware | Implementar cada funcionalidade | Conectar e ordenar essas funcionalidades |
Depois de usar os dois, percebi que não são ideias opostas, mas conceitos de níveis diferentes.
Decorator trata de “como adicionar uma funcionalidade”; Chain of Responsibility, de “em que ordem executar o que foi adicionado”.
Juntos, eles compensam as limitações um do outro.
Combinação prática: montando com código
Primeiro, definimos a interface comum do middleware. Ela recebe uma requisição e a encaminha ao próximo middleware.
protocol Middleware {
// requestrecebe e processa e, depois, encaminha para a próxima etapa por meio de , next
func handle(_ request: Request,
next: (Request) async throws -> Response)
async throws -> Response
}
Agora criamos cada funcionalidade uma por vez, como um decorator. Este é o middleware de logs.
struct LoggingMiddleware: Middleware {
func handle(_ request: Request,
next: (Request) async throws -> Response)
async throws -> Response {
print("➡️ requisição: \(request.url)")
let response = try await next(request) // para a próxima etapa
print("⬅️ resposta: \(response.status)")
return response
}
}
A chamada a next(request) é o elo do Chain of Responsibility.
Ele faz seu trabalho —registrar o log— e encaminha o restante ao próximo middleware.
Por fim, reunimos vários middlewares em uma única cadeia.
func buildChain(_ middlewares: [Middleware],
final: @escaping (Request) async throws -> Response)
-> (Request) async throws -> Response {
middlewares.reversed().reduce(final) { next, mw in
{ req in try await mw.handle(req, next: next) }
}
}
O ponto principal é envolver tudo de trás para frente com reduce.
Se você inserir o array na ordem [인증, 로깅, 재시도], as requisições passarão nessa ordem e as respostas sairão na ordem inversa. Pense nas camadas de uma cebola.
Quais são as vantagens?
Veja as vantagens que percebi depois de aplicar isso em um projeto real.
-
Adicionar uma funcionalidade vira adicionar uma linha ao array. Se precisar de cache, basta inserir
CachingMiddleware()no array. -
É fácil mudar a ordem. Para executar a autenticação antes ou depois dos logs, basta alterar a ordem do array.
-
Testar fica mais fácil. Como cada middleware é independente, você pode adicionar testes unitários um por um.
-
O núcleo fica limpo. O
send()de 200 linhas volta a ter menos de dez.
Por outro lado, há alguns cuidados.
Quando há middlewares demais, fica difícil rastrear por onde uma requisição passa.
Por isso, quando a cadeia passa de cinco ou seis componentes, coloco o middleware de logs no início e registro o caminho percorrido.
Resumo
Depois que você se acostuma, essa combinação —criar funcionalidades com Decorator e ordená-las com Chain of Responsibility— pode ser usada não só em camadas de rede, mas também no processamento de eventos e na validação.
Recomendo adicionar o exemplo de hoje a um pequeno projeto de teste. Ao desmontar as camadas por conta própria, o conceito fica muito mais fácil de absorver. Boa refatoração!

