Choosing between .NET background services and durable workflows
For plain background work, .NET's built-in BackgroundService usually suffices, but the balance shifts once a process spans multiple dependent steps, approvals and recovery after a restart. The article compares both approaches on state, retries, recovery and compensation, and shows when a workflow engine actually earns its setup cost.
Key points
- BackgroundService hands you an ExecuteAsync loop and AddHostedService<T>() registration; persistence, retry and recovery stay in your code.
- The worker lives inside the web host, so every instance starts its own copy and can process the same pending job without atomic claims or a queue.
- Durable workflows store execution state per step, which survives restarts and lets you retry only the failed step.
- Replaying a whole order flow risks double charges or duplicate reservations, so idempotency keys must be enforced by the payment provider.
- Compensation is a new business action such as releasing a reservation, not an exact undo of the earlier step.
- A plain job table with status, RetryCount and NextRetryAtUtc covers many cases; map job types to registered handlers and never store arbitrary type names.
BackgroundService is the base class you get for work that runs beside an ASP.NET Core host: polling a queue, deleting temp files, syncing data. You receive an ExecuteAsync loop and AddHostedService<T>() registration, nothing more. State, retries and recovery are yours to build, and with three instances each one may pick up the same pending job unless you claim work atomically or use a distributed queue.
Durable workflows persist each step instead of holding progress in memory, so a long order, loan or approval process can resume where it stopped, retry one step and run a compensation that releases reserved inventory when payment fails for good. The author compares both, and suggests a database-backed job table first, before an engine like Temporal, Azure Durable Functions or Dapr Workflow.
Real-world use
- An API that deletes expired temp files or refreshes a small cache hourly, where a missed run causes no business damage.
- A SaaS order fulfilment flow with payment, invoice and delivery steps that may wait days for manual approval.
- A loan application that resumes exactly where it paused when a clerk approves hours later.
- A multi-instance ASP.NET Core deployment where the same background job must not be processed twice.
Why it matters
The choice decides whether you carry needless infrastructure for a simple task or end up with money-touching processes left half-done after a restart. How long the work runs, how many steps it has and what happens if it stops midway should drive the decision.