Arquitetura .NET: Otimizando o registro de tarefas recorrentes com Hangfire

Em nosso mais recente projeto, enfrentamos o desafio de gerenciar múltiplos processos assíncronos de forma resiliente com escalabilidade. Entre esses processos estavam fluxos de pré-agendamento, monitoramento de NFe integrado a serviços externos e rotinas automatizadas de No-Show, responsáveis por identificar e tratar situações de não comparecimento de forma automática

Para centralizar e garantir a execução dessas tarefas, implementamos o Hangfire integrado ao ciclo de vida da aplicação via IHostedService.

Mas o que seria o Hangfire? Hangfire é biblioteca de código aberto para o ecossistema .NET, projetada para facilitar a criação, processamento e gerenciamento de tarefas em segundo plano (background) de formar resiliente e assíncronos.

Desacoplamento e Injeção de Dependência:

Criamos um método de extensão corporativo AddBackgroundJobs para estender a IServiceCollection. Isso mantém a classe Program.cs (Startup) limpa, legível e aderente ao princípio de responsabilidade única.

				
					public static IServiceCollection AddBackgroundJobs(this IServiceCollection services)
{
   // Registra os recorrentes de Scheduling no startup da aplicacao.
   services.AddHostedService<SchedulingRecurringJobsHostedService>();

   // Job de sincronizacao NFe de entrada legado.
   services.AddHostedService<SincNfEntrada>();

   return services;
}
				
			

Trecho d configuração no Program.cs.

				
					# region Job Hangfire
builder.Services.AddBackgroundJobs();
#endregion
				
			

Ciclo de Vida Controlado via Hosted Service:

Utilizamos o SchedulingRecurringJobsHostedService derivado de IHostedService. O registro das rotinas cron (RecurringJob.AddOrUpdate) acontece estritamente no método StartAsync, garantindo que os agendamentos sejam atualizados no storage assim que a aplicação é inicializada pelo servidor.

				
					public sealed class SchedulingRecurringJobsHostedService(ILogger<SchedulingRecurringJobsHostedService> logger) : IHostedService
{
    private readonly ILogger<SchedulingRecurringJobsHostedService> _logger = logger;

    public Task StartAsync(CancellationToken cancellationToken)
    {
        var timeZone = TimeZoneInfo.FindSystemTimeZoneById(
            RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
                ? "E. South America Standard Time"
                : "America/Sao_Paulo");
    
        // Mantem os mesmos IDs historicos para atualizar registros existentes no storage.
        RecurringJob.AddOrUpdate<SchedulingWorkflowJobs>(
            "WorkflowPreAgendamen",
            job => job.RunPreScheduleWorkflowAsync(),
            "7 5 * * 1-5",
            new RecurringJobOptions
            {
                TimeZone = timeZone
            });
        
            _logger.LogInformation("Recurring jobs de scheduling registrados no Hangfire.");
        return Task.CompletedTask;
    }
}
				
			

Compatibilidade Cross-Platform (Windows vs. Linux/Docker):

Um dos maiores gargalos em agendamentos globais é a divergência de fusos horários entre ambientes de desenvolvimento e produção. Resolvemos isso dinamicamente avaliando a plataforma em tempo de execução:

  • Se Windows: “E. South America Standard Time”
  • Se Linux (Containers): “America/Sao_Paulo”
    Dessa forma, garantimos a execução precisa dos gatilhos nos horários de Brasília, independente da infraestrutura de hospedagem.
				
					var timeZone = TimeZoneInfo.FindSystemTimeZoneById(
            RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
                ? "E. South America Standard Time"
                : "America/Sao_Paulo");
				
			

Preservação de Histórico de Execução:

Mantivemos identificadores fixos e explícitos para cada job (como “WorkflowPreAgendamen”, “MoniturarNFeJob” e “JobNoShow”). Isso evita a duplicação de tarefas no banco de dados e preserva as métricas históricas de auditoria no dashboard do Hangfire.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *