Repository navigation
Workflow and Background Jobs
Two separate things that often appear together: jobs run work later, workflows track what state something is in.
Enqueue work now, run it elsewhere.
using Birko.BackgroundJobs;
using Birko.BackgroundJobs.Processing;
using Birko.Time;
var queue = new InMemoryJobQueue(new SystemDateTimeProvider());
var jobId = await queue.EnqueueAsync(new JobDescriptor { JobType = nameof(SendEmailJob) });
// a worker picks it up
var job = await queue.DequeueAsync();
await new SendEmailJob().ExecuteAsync(new JobContext(job!.Guid, 1, job.EnqueuedAt));
await queue.CompleteAsync(jobId);A job is one method:
public class SendEmailJob : IJob
{
public Task ExecuteAsync(JobContext context, CancellationToken cancellationToken = default)
{
// do the work
return Task.CompletedTask;
}
}InMemoryJobQueue is for tests and single-process apps. For anything that must survive a restart,
pick a backend — the API above does not change:
SQL · MongoDB · RavenDB · ElasticSearch · CosmosDB · Redis · JSON · XML
var queue = new SqlJobQueue(settings, new SystemDateTimeProvider());-
RetryPolicyon the descriptor controls attempts and backoff -
JobSchedulerruns jobs on a schedule -
JobProcessoris the loop that dequeues and executes — host it in aBackgroundService
A state machine with guards and actions, for things that move through defined states — an order, an approval, a document.
using Birko.Workflow.Definition;
using Birko.Workflow.Execution;
var workflow = new WorkflowBuilder<Order>("Orders")
.InitialState("New")
.State("New").And()
.State("Paid").And()
.State("Shipped").IsFinal().And()
.Transition("pay", "New", "Paid").And()
.Transition("ship", "Paid", "Shipped").And()
.Build();
var instance = WorkflowInstance<Order>.Create(workflow, new Order());
var result = await new WorkflowEngine().FireAsync(workflow, instance, "pay");
if (result.IsSuccess)
Console.WriteLine(instance.CurrentState); // "Paid"Firing a trigger that isn't valid from the current state fails rather than moving — check
result.IsSuccess.
A transition can be conditional, and can do something on the way through:
- guard — may this transition happen, given the data?
- action — run this as it happens
IWorkflowInstanceStore keeps instances between requests, with backends for SQL, MongoDB, RavenDB,
ElasticSearch, CosmosDB, JSON, XML.
A workflow can export itself as Mermaid or DOT, which is a cheap way to review a state machine with someone who does not read C#.
- Data and Stores — the backends both of these persist to
- AI and LLM — long-running agent work belongs on a queue
Start here
Data
Services
UI