Skip to content

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.