Apalis: Orientation
Apalis turns an ordinary async function into the center of a durable job worker. Around that small function it composes a backend, typed extraction, Tower middleware, readiness, bounded concurrency, acknowledgement, retry, events, and cooperative shutdown.
async fn send_email(job: Email, state: Data<Arc<App>>) -> Result<(), MailError> {
state.mailer.send(job).await
}
let worker = WorkerBuilder::new("email")
.backend(storage)
.data(app)
.concurrency(16)
.parallelize(tokio::spawn)
.build(send_email);
The architectural center is not the syntax of send_email. It is this
admission rule:
A backend may release the next durable task only when the composed service is ready to own it.
That connects storage semantics to Tower backpressure without making either side implement the other.
Design thesis
Apalis keeps application handlers ordinary while concentrating durable job policy in typed backend and Tower middleware boundaries.
- Payloads move into handlers so their futures can run independently.
- Service readiness is checked before durable work is claimed.
- Backends own encoding, polling, locking, and acknowledgement semantics.
- Generics describe the hot path; type erasure appears at heterogeneous edges.
We will preserve ordinary async handlers, owned task payloads, typed context extraction, backend-independent execution, readiness before dequeue, middleware-defined policy, tracked completion, and graceful drain.