A modular .NET distributed framework for background jobs and event-driven architectures.
- At-Least-Once Delivery with idempotency keys
- Priority Queues - higher priority jobs processed first
- Batch Operations - enqueue thousands of jobs efficiently
- Graceful Shutdown - drain mode for zero job loss
- Transactional Outbox - atomic job creation with your DB
- Distributed Locks - Redis-backed coordination
- Rate Limiting - sliding window algorithm
- OpenTelemetry - native tracing support
dotnet add package Valir.Redis
dotnet add package Valir.AspNetbuilder.Services.AddValir(options =>
{
options.RedisConnectionString = "localhost:6379";
});app.MapPost("/jobs", async (IJobQueue queue) =>
{
byte[] payload = JsonSerializer.SerializeToUtf8Bytes(new { Email = "user@example.com" });
string jobId = await queue.EnqueueAsync("send-email", payload);
return Results.Ok(new { JobId = jobId });
});// Define your job handler
public class EmailJobHandler : IJobHandler<EmailData>
{
public async Task HandleAsync(EmailData job, JobContext context)
{
await SendEmailAsync(job, context.CancellationToken);
}
}
// Register and run
builder.Services.AddValir(options =>
{
options.RedisConnectionString = "localhost:6379";
});
builder.Services.AddSingleton<IJobHandler<EmailData>, EmailJobHandler>();
var worker = app.Services.GetRequiredService<WorkerRuntime>();
await worker.RunAsync();| Package | Description |
|---|---|
| Valir.Abstractions | Core interfaces (IJobQueue, IEventBroker) |
| Valir.Core | Worker runtime, retry policies |
| Valir.Redis | Redis job queue implementation |
| Valir.AspNet | ASP.NET Core integration |
| Valir.EntityFrameworkCore | Transactional Outbox pattern |
| Valir.Brokers.Kafka | Apache Kafka adapter |
| Valir.Brokers.RabbitMQ | RabbitMQ adapter |
| Valir.Brokers.AzureSB | Azure Service Bus adapter |
builder.Services.AddValir(options =>
{
options.RedisConnectionString = "localhost:6379";
options.KeyPrefix = "valir:";
options.Concurrency = 4;
options.DefaultMaxAttempts = 3;
options.RetryBaseDelay = TimeSpan.FromSeconds(10);
options.DefaultVisibilityTimeout = TimeSpan.FromSeconds(30);
});builder.Services.AddValirKafka(options =>
{
options.BootstrapServers = "localhost:9092";
options.GroupId = "my-service";
});builder.Services.AddValirRabbitMQ(options =>
{
options.HostName = "localhost";
options.UserName = "guest";
options.Password = "guest";
});builder.Services.AddValirAzureServiceBus(options =>
{
options.ConnectionString = "Endpoint=sb://...";
});builder.Services.AddValirOutbox<AppDbContext>();
// Jobs are written to outbox table (same transaction)
await _outboxQueue.EnqueueAsync("process-order", payload);
await _context.SaveChangesAsync(); // Atomic!MIT