Shared Rust libraries for application services. The workspace provides a typed core vocabulary and reusable boundaries for request entry, messaging, governance, and evidence.
service-interface-boundary is the most complete crate in the workspace. It
hosts a service-supplied Axum router, exposes health endpoints, gates business
traffic by readiness, and coordinates managed startup and shutdown. The other
boundary crates currently provide their initial contracts and no-op adapters.
| Crate | Responsibility | Status |
|---|---|---|
shared-libs-core |
Typed IDs, trace context, deadlines, and errors. | Usable foundation |
service-interface-boundary |
HTTP request entry, health, readiness, drain, and lifecycle protection. | Usable foundation |
messaging-reliability-boundary |
Message envelopes and publisher / consumer contracts. | Initial contract |
governance-control-boundary |
Governed-control vocabulary. | Initial contract |
evidence-boundary |
Evidence publication vocabulary. | Initial contract |
service-interface-boundary is the application process entry point. A service
builds its domain state and Axum Router; the boundary hosts that router and
provides runtime protection around it.
One HTTP listener serves both business routes and platform endpoints:
| Path | Purpose |
|---|---|
/ops/live |
Reports whether the process is live. |
/ops/ready |
Reports whether the service may receive new business traffic. |
/ops/health |
Returns lifecycle, readiness, and drain state. |
| Any other path | Reaches the service-supplied business router only when ready. |
The default listener is 0.0.0.0:8080. Reserve the /ops prefix for the
boundary; do not use it for business routes.
Add the workspace crates to a service:
[dependencies]
async-trait = "0.1"
axum = "0.8"
service-interface-boundary = { path = "../shared-libs/crates/service-interface-boundary" }
shared-libs-core = { path = "../shared-libs/crates/shared-libs-core" }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }Create the business router, then start the interface boundary:
use axum::{Router, routing::get};
use service_interface_boundary::ServiceInterfaceBoundary;
async fn ping() -> &'static str {
"pong"
}
let app = Router::new().route("/ping", get(ping));
let started = ServiceInterfaceBoundary::start("order-service", app).await?;
// Initialize dependencies, restore state, and start business loops first.
started.mark_ready();Use ServiceInterfaceBoundary::start_on(service_name, app, addr) to override
the listener address in local tests or special deployments.
For a long-running service, prefer ServiceInterfaceBoundary::serve(...).
Implement ServiceRuntime in the business service:
use async_trait::async_trait;
use service_interface_boundary::{ServiceLifecycle, ServiceRuntime};
use shared_libs_core::CoreResult;
struct MatchingRuntime;
#[async_trait]
impl ServiceRuntime for MatchingRuntime {
async fn start(&mut self, lifecycle: ServiceLifecycle) -> CoreResult<()> {
// Recover state and start business loops.
let _ = lifecycle;
Ok(())
}
async fn begin_drain(&mut self) -> CoreResult<()> {
// Stop accepting business work and reach a domain-safe point.
Ok(())
}
async fn stop(&mut self) -> CoreResult<()> {
// Release business resources after drain completes.
Ok(())
}
}Then assemble the router and runtime in the executable:
ServiceInterfaceBoundary::serve("matching-service", app, runtime).await?;The service begins in Starting and is live but not ready. serve(...) marks
the service ready after ServiceRuntime::start(...) succeeds. Manual startup
uses StartedServiceInterface directly:
started.mark_ready();
started.mark_not_ready("database.unavailable");
started.begin_draining("shutdown.requested");
started.mark_stopped();Business requests receive 503 Service Unavailable while the service is
starting, not ready, draining, or stopped. The /ops/* routes remain available
until the HTTP server shuts down.
When using serve(...), Ctrl-C or Unix SIGTERM starts this sequence:
begin_draining
-> ServiceRuntime::begin_drain
-> wait up to 30 seconds for business drain and admitted HTTP handlers
-> graceful HTTP shutdown
-> ServiceRuntime::stop
-> mark_stopped
ServiceRuntime::begin_drain owns business work that outlives an HTTP request,
such as matching loops, queued commands, database transactions, and message
consumers.
AdmissionPolicyis a contract only; automatic request-envelope creation and policy middleware are not implemented yet.- Authentication, authorization, idempotency enforcement, deadlines, and standardized error responses are not implemented yet.
- Metrics, logs, traces, and dependency-health methods are extension points, not concrete adapters.
- WebSockets, streaming responses, and detached tasks need explicit service-specific drain policies.
start(...)andstart_on(...)are manual APIs. When not usingserve(...), callwait_for_in_flight_requests()andshutdown_http().
cargo fmt --all --check
cargo test --workspace
cargo clippy --workspace -- -D warningsKeep business state, governance decisions, and message-offset ownership outside the service interface boundary. Add focused tests for every lifecycle, routing, or request-admission behavior.
This repository does not currently declare a license. Add an OSI-approved license before distributing it as an open-source package.