Injeção de Dependência Contextual (Request Scope)
July 10, 2026 · View on GitHub
Read this in English or Português (BR).
O Gerenciamento e Injeção de Dependência Contextual (Request Scope) no Horse permite o gerenciamento determinístico do ciclo de vida de instâncias de serviços e classes acopladas diretamente ao ciclo de vida da requisição HTTP ativa.
Ao registrar serviços em escopo de requisição, o desenvolvedor garante o isolamento completo de estado entre requisições concorrentes (thread-safe) e conta com o descarte automático dos recursos instanciados ao final do pipeline de roteamento HTTP, eliminando por completo vazamentos de memória (memory leaks) e a necessidade de blocos try/finally manuais nas closures de rotas.
🗺️ Ciclo de Vida da Injeção de Dependências
O ciclo de vida da propriedade Services na requisição segue a sequência descrita no diagrama abaixo:
sequenceDiagram
autonumber
participant Cliente as Cliente HTTP
participant Horse as THorseRequest (Services)
participant Core as THorseRequestContext (Dicionário)
participant Destrutor as Destruição Automática
Cliente->>Horse: Requisição HTTP iniciada
Note over Horse: Services (Lazy-initialized no primeiro acesso)
rect rgb(20, 20, 30)
Note over Horse: Registro de Serviços
Horse->>Core: Req.Services.Add(TMyService, Instance)
Note right of Core: Registrado como instância pertencente
end
rect rgb(20, 30, 20)
Note over Horse: Resolução de Serviços
Horse->>Core: Req.Services.Resolve(TMyService)
Core-->>Horse: Retorna instância tipada
end
Cliente->>Horse: Pipeline de roteamento é finalizado
Horse->>Destrutor: THorseRequest.Clear ou Destroy é acionado
Destrutor->>Core: Dispara FreeAndNil(FServices)
Note over Core: Destrói todas as instâncias pertencentes (doOwnsValues)
Note over Core: Descarte Concluído (Zero Memory Leaks!)
🛠️ Modos de Injeção e Registro
A propriedade Services fornece duas formas principais de registro de dependências com comportamentos específicos:
1. Injeção de Instância Direta (Add)
Registra uma instância de objeto previamente criada no contexto da requisição corrente. Por padrão, a classe gerenciadora assume a propriedade (ownership) do objeto, descartando-o automaticamente ao final do request.
Req.Services.Add(TMyService, TMyService.Create);
2. Injeção Preguiçosa via Fábrica (AddFactory)
Registra um delegate de fábrica (factory method) que define como criar o serviço sob demanda (Lazy Loading). O serviço só é instanciado fisicamente no primeiro momento em que for resolvido (chamada de Resolve). Uma vez instanciado, ele é cacheado no contexto da requisição corrente e destruído automaticamente ao término da requisição.
Req.Services.AddFactory(TMyService,
function: TObject
begin
Result := TMyService.Create;
end);
💻 Exemplo Prático Completo
program ConsoleDependencyInjection;
{$APPTYPE CONSOLE}
uses
Horse, Horse.Commons, System.SysUtils;
type
TMyService = class
private
FId: string;
public
constructor Create(const AId: string);
destructor Destroy; override;
function GetMessage: string;
end;
{ TMyService }
constructor TMyService.Create(const AId: string);
begin
inherited Create;
FId := AId;
Writeln(Format('[TMyService] Instanciado com ID: %s', [FId]));
end;
destructor TMyService.Destroy;
begin
Writeln(Format('[TMyService] Destruído com ID: %s (Limpo de forma automática)', [FId]));
inherited Destroy;
end;
function TMyService.GetMessage: string;
begin
Result := 'Olá de um Serviço Contextual! ID: ' + FId;
end;
begin
// Rota 1: Usando Injeção de Instância Direta
THorse.Get('/resolve',
procedure(Req: THorseRequest; Res: THorseResponse; Next: TProc)
var
LService: TMyService;
begin
LService := TMyService.Create('Direto');
Req.Services.Add(TMyService, LService);
Next();
end,
procedure(Req: THorseRequest; Res: THorseResponse; Next: TProc)
var
LService: TMyService;
begin
LService := TMyService(Req.Services.Resolve(TMyService));
Res.Send(LService.GetMessage);
end);
// Rota 2: Usando Lazy Factory (Carregamento Preguiçoso)
THorse.Get('/lazy',
procedure(Req: THorseRequest; Res: THorseResponse; Next: TProc)
begin
Req.Services.AddFactory(TMyService,
function: TObject
begin
Result := TMyService.Create('Lazy');
end);
Next();
end,
procedure(Req: THorseRequest; Res: THorseResponse; Next: TProc)
var
LService: TMyService;
begin
// A fábrica só será executada e o serviço só será instanciado na linha abaixo!
LService := TMyService(Req.Services.Resolve(TMyService));
Res.Send(LService.GetMessage);
end);
THorse.Listen(9000);
end.
📈 Benefícios Arquiteturais
- Ciclo de Vida Determinístico e Automático: Garante o descarte seguro de recursos ao término do pipeline HTTP da requisição ativa, eliminando memory leaks.
- Isolamento de Estado Concorrente: Totalmente thread-safe, permitindo que cada thread/request trate suas instâncias de serviços de forma isolada, evitando race conditions.
- Lazy Initialization nativa: Redução no consumo de RAM e no tempo de inicialização de recursos pesados por meio do
AddFactory, carregando somente o que é realmente demandado pela rota executada.