A proof-driven microservice framework for Rust
wakuwaku wires a service together. Every component (gRPC server, event handler, cron job) is built from a type-level registry of shared dependencies, and the crates that do the actual work are re-exported behind feature flags, so a service depends on wakuwaku alone.
One dependency
Add wakuwaku and turn on the integrations you need. Third-party crates that appear in its APIs are re-exported under wakuwaku::deps.
- grpc· default
- proof· default
- iggy
- entity
- sqlx-pg
- sqlx-mysql
- redis
- meilisearch
[dependencies]
wakuwaku = { version = "1.0.0-rc2", features = ["iggy", "sqlx-pg", "redis"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread", "signal"] }Proof-driven programming
Correctness is split in two, and each half has its own kind of proof.
Macro correctness
How components fit together is proven by traits and checked by the compiler. A component whose dependency was never registered, a write query run through a read-only handle, or a cron job with a zero interval does not build.
Micro correctness
What one component does is proven by unit tests. Every component is a plain value implementing a Processor-based trait, so a test calls it directly, without starting the service.
What you build with
The framework crate wires the components; each integration lives in its own crate, re-exported behind a feature flag.
Type-level dependency registry
ServiceBuilderShared dependencies live in a type-indexed registry. A component is built from one of them through ServiceCreation, or from several by a compose_* closure. Lookup is resolved by the trait solver: no Any, no downcasting, no runtime map.
Learn moregRPC registration
feature = "grpc"Build tonic servers from the registry and add them all to one router, in registration order, with no boxing and no dynamic dispatch.
Learn moreCron jobs
CronJobA job is a processor of CronTick with a constant CronJobInterval: a pause after each run, a minimum spacing between starts, or a fixed grid whose runs may overlap. Errors and panics are logged and the schedule is kept.
Learn moreTyped events on Apache Iggy
wakuwaku::iggyEvents are ordered per key, not per partition. Ordering fences decide what may move past what, and algebra markers let a polled batch collapse or fold into fewer handler calls.
Learn moreCapability-typed data access
wakuwaku::entityA query declares the entities it reads and writes, and a Db handle must carry a capability that covers them. Data sources for sqlx (PostgreSQL, MySQL), Redis and Meilisearch.
Learn moreProof-carrying validation
wakuwaku::proofprove hands back Proven<T, P>: the value together with evidence that the predicate held. Take Proven in signatures and "forgot to validate" becomes a type error.
Learn moreA service in one file
A cron job built from a shared SessionStore that runs every minute. gRPC servers and Iggy event handlers are registered from the same ServiceBuilder.
use std::time::Duration;
use wakuwaku::deps::kanau::processor::Processor;
use wakuwaku::{CronJob, CronJobInterval, CronTick, ServiceBuilder, ServiceCreation};
#[derive(Clone)]
struct SessionStore;
struct ExpireSessions { store: SessionStore }
impl ServiceCreation for ExpireSessions {
type Dep = SessionStore;
fn create_service(store: SessionStore) -> Self { Self { store } }
}
impl Processor<CronTick> for ExpireSessions {
type Output = ();
type Error = std::convert::Infallible;
async fn process(&self, _tick: CronTick) -> Result<(), Self::Error> {
// Scan for expired sessions and delete them.
Ok(())
}
}
impl CronJob for ExpireSessions {
const INTERVAL: CronJobInterval = CronJobInterval::AtLeast(Duration::from_secs(60));
}
#[tokio::main]
async fn main() {
let (services, cron) = ServiceBuilder::new(SessionStore)
.cron_job::<ExpireSessions, _>()
.start();
// Build gRPC servers and Iggy handlers from `services` the same way:
// `services.grpc_service::<XxxServer<_>, Impl, _>(XxxServer::new)`,
// `services.iggy_consumer::<Handler, _, _>()`, or from several
// services at once with `.compose_grpc_service(|s| ...)`.
let _ = services;
tokio::signal::ctrl_c().await.ok();
cron.shutdown().await;
}Roadmap
The goal is a framework as powerful and robust as Spring is for Java.
- 1.0.0-rc1Start
The first release candidate.
- 1.0.0-rc3Now
The release candidate these docs describe.
- 1.0.0Stable
After end-to-end tests on real-world projects.
- 2.0.0Spring parity
Every meaningful Spring module has a wakuwaku counterpart.
Ready to build?
Set up a project and run your first service in a few minutes.