wakuwaku

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
Cargo.toml
[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.

Read about proof-driven design

What you build with

The framework crate wires the components; each integration lives in its own crate, re-exported behind a feature flag.

A 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.

Follow the quick start
src/main.rs
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. 1.0.0-rc1Start

    The first release candidate.

  2. 1.0.0-rc3Now

    The release candidate these docs describe.

  3. 1.0.0Stable

    After end-to-end tests on real-world projects.

  4. 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.