Routing¶
As a service grows, handlers move out of main.rs into their own modules. A Router collects a
module's handlers into one mountable group; include_router mounts the whole group on a broker
scope.
Building a router¶
A Router mirrors the broker scope: alongside include / include_on and include_publishing /
include_publishing_on (which take the reply wiring positionally - a TypedPublisher stack over
a publish policy, since a consuming builder cannot chain a postfix .publisher(..)) it has
with_codec (switches the chain's decode codec, see
Codecs) and the manual handle / subscribe registrations. Every call
consumes the router and returns a new one, so registrations chain:
use ruststream::runtime::Router;
fn orders() -> Router<MemoryBroker, impl RouterDef<MemoryBroker>> {
Router::new().include(accept)
}
fn shipping() -> Router<MemoryBroker, impl RouterDef<MemoryBroker>> {
Router::new().include(dispatch)
}
Handlers that publish a reply register on the router the same way as on the scope, with a
TypedPublisher stack over a publish policy - a pure declaration, so the router needs no broker:
use ruststream::memory::{MemoryBroker, MemoryPublish};
use ruststream::runtime::{Router, RouterDef, TypedPublisher};
use crate::orders;
// The reply wiring is a publish policy: pure declaration, so the router needs no broker at all.
pub(crate) fn orders() -> impl RouterDef<MemoryBroker> {
let replies = TypedPublisher::new(MemoryPublish);
Router::new()
.include_publishing(orders::confirm, replies)
.include(orders::handle)
}
Router middleware¶
A router can carry its own layer stack: Router::layer wraps every handler in that router when it
is mounted. The application's global stack (added with RustStream::layer) wraps around it at
include_router - scopes nest, app outermost:
fn routes() -> impl RouterDef<MemoryBroker> {
Router::new().include(confirm).include(reject)
}
Because a router hides its handlers' concrete types, a layer reaching them must be a
BlanketLayer. Both scopes, the BlanketLayer requirement, and writing your own layer are covered
in Middleware.
Composing and mounting¶
Build routers per module, then combine them however suits the service:
// Mount several routers on one broker - include_router can be called more than once.
RustStream::new(info).with_broker(broker, |b| {
b.include_router(routes::orders());
b.include_router(routes::shipping());
});
Or merge groups into one router before mounting (the whole program is
examples/routing.rs):
// Merge groups into one router, then mount the result.
let all = orders().merge(shipping());
b.include_router(all);
merge appends another router's registrations in order. Each router keeps its own codec and layer
stack; when the result is mounted, the outer router's layers (and the app's global stack) wrap
around the merged router's own.
Next¶
- The handler contract and the
#[subscriber]macro: Subscribers. - How the decode codec is resolved for
include: Codecs.