ADR-004: Hangfire over MassTransit for Background Jobs¶
Status: Accepted Date: 2026-03-09
Context¶
Tablez needs background job processing for: SMS/email delivery, waitlist hold expiry (delayed 15 min), reminder scheduling (24h before), no-show cleanup (recurring), and projection rebuilds (manual trigger).
Options Considered¶
| Option | Pros | Cons |
|---|---|---|
| Hangfire | PostgreSQL storage, dashboard, delayed/recurring/fire-and-forget, MIT license | Not a full message bus |
| MassTransit | Full message bus, saga support, multiple transports | Commercial license since v9 (Apache 2 → commercial), requires RabbitMQ or similar |
| Quartz.NET | Mature scheduler | No fire-and-forget, no dashboard, more complex setup |
| Raw BackgroundService | No dependencies | No persistence, no retry, no dashboard, no delayed jobs |
Decision¶
Use Hangfire with PostgreSQL storage for all background job processing.
Rationale¶
- MassTransit went commercial in v9. Previously Apache 2.0, now requires a paid license for production use. Hangfire remains MIT licensed.
- PostgreSQL storage. No additional infrastructure — same database as Marten. No RabbitMQ, no separate message broker.
- Built-in dashboard. Visual monitoring of job queues, failures, and retries.
- All job types covered. Fire-and-forget (SMS), delayed (waitlist expiry), recurring (no-show cleanup), continuations.
- Simple API.
BackgroundJob.Enqueue(() => SendSms(...))— no message contracts or consumers to define.
Consequences¶
- No saga/state machine support in the job system. Complex workflows use Stateless state machine + event sourcing instead.
- Cross-service communication uses Valkey pub/sub, not Hangfire. Hangfire is for within-service background work.
- Dashboard needs to be secured (auth middleware) before exposing to staff.