Layered
Layered Services
Build composable async services with layered middleware.
This crate provides the Service trait and a layer system for adding cross-cutting
concerns like timeouts, retries, and logging.
Why not Tower?
Tower predates async fn in traits, requiring manual Future types
or boxing and poll_ready back-pressure semantics. Tower’s &mut self also requires cloning
for concurrent requests. This crate uses async fn with &self, enabling simpler middleware
and natural concurrency. Tower interop is available via the tower-service feature.
Quick Start
A Service transforms an input into an output asynchronously:
use Service;
;
Use Execute to turn any async function into a service:
use ;
let greeter = new;
assert_eq!;
Key Concepts
- Service: An async function
In → Outthat processes inputs. - Middleware: A service that wraps another service to add behavior (logging, timeouts, retries).
- Layer: A factory that wraps any service with middleware. Stack layers using tuples
like
(layer1, layer2, service).
Layers and Middleware
A Layer wraps a service with additional behavior:
use ;
// A simple logging layer
;
;
// Stack layers with the service (layers apply outer to inner)
let service = .build;
let result = service.execute.await;
Thread Safety
All services must implement Send and Sync, and returned futures must be Send.
This ensures compatibility with multi-threaded async runtimes like Tokio.
Features
intercept: EnablesInterceptmiddlewaredynamic-service: EnablesDynamicServicefor type erasuretower-service: Enables Tower interoperability via thetowermodule