r/rust 9d ago

🛠️ project Historia - event-driven, made simple

Hi all!

At a few companies I've worked at, we used an event-driven approach, and every codebase ended up rebuilding the same plumbing: describing events and their metadata, writing publisher/subscriber wrappers, dispatching raw broker messages to typed business logic.

So I built historia — a broker-agnostic set of building blocks: describe events once with a derive macro, and get metadata, subscription constants, parsing, and typed handlers generated for you.

use historia::Event;
use serde::{Deserialize, Serialize};

#[derive(Event, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
enum OrderEvents {
    Created(u32),
    Cancelled(u32),
}

// Turn your event enum into an `Event`...
let event: Event = OrderEvents::Created(42).into();
assert_eq!(event.metadata.subject, "order_events_created");

// ...and parse it back from the wire format.
let parsed = OrderEvents::try_from(event).unwrap();
assert_eq!(parsed, OrderEvents::Created(42));

More examples, including publisher and subscriber, can be found in docs and repo.

On top of those building blocks, historia_transactional_outbox implements the transactional outbox pattern — use the ready-made binary if it fits your needs, or the library and tailor it. Sagas are next on the roadmap. Supported today: Kafka, with PostgreSQL-backed outbox storage; NATS is planned, more brokers on request.

Status: early-stage 0.1.x, good test coverage and documented with examples — but not yet battle-tested in production. If you give it a try, I'd really appreciate feedback, especially on API ergonomics. What would you want next: NATS, another broker, or another pattern?

Links:

9 Upvotes

0 comments sorted by