Skip to content

danieleades/sourcery

Repository files navigation

sourcery

Continuous integration codecov

Building blocks for pragmatic event-sourced systems in Rust.

Highlights

  • Domain-first API – events are plain structs that implement DomainEvent; IDs and metadata live in an envelope rather than in your payloads.
  • Aggregate derive#[derive(Aggregate)] generates the event enum plus serialisation/deserialisation glue so command handlers stay focused on behaviour.
  • Repository orchestrationRepository loads aggregates, executes commands via Handle<C>, and persists the resulting events in a single transaction.
  • Metadata-aware projections – projections receive aggregate IDs, events, and metadata via ApplyProjection, enabling cross-aggregate correlation using causation, timestamps, or custom metadata.
  • Store agnostic – ships with an in-memory store for demos/tests; implement the EventStore trait to plug in your own persistence.

Quick Look

use sourcery::{Apply, DomainEvent, Handle};
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FundsDeposited { pub amount_cents: i64 }

impl DomainEvent for FundsDeposited {
    const KIND: &'static str = "bank.account.deposited";
}

#[derive(Debug, Default, sourcery::Aggregate)]
#[aggregate(id = String, error = String, events(FundsDeposited))]
pub struct Account { balance_cents: i64 }

impl Apply<FundsDeposited> for Account {
    fn apply(&mut self, event: &FundsDeposited) {
        self.balance_cents += event.amount_cents;
    }
}

pub struct DepositFunds { pub amount_cents: i64 }

impl Handle<DepositFunds> for Account {
    fn handle(&self, cmd: &DepositFunds) -> Result<Vec<Self::Event>, Self::Error> {
        Ok(vec![FundsDeposited { amount_cents: cmd.amount_cents }.into()])
    }
}

Key Concepts

Concept Description
Aggregates Rebuild state from events and validate commands via Apply<E> and Handle<C>.
Projections Read models that replay events via ApplyProjection<E>, potentially across multiple aggregates.
Repository Orchestrates loading, command execution, and persistence in a single transaction.
EventStore Trait defining the persistence boundary—implement for Postgres, DynamoDB, S3, etc.

Documentation

Examples

Example Description
quickstart Minimal aggregate + projection
inventory_report Cross-aggregate projection
subscription_billing Real-world billing domain
versioned_events Schema migration via serde
optimistic_concurrency Conflict detection and retry
snapshotting Aggregate snapshots for performance
cargo run --example quickstart

Status

Pre-1.0. APIs will evolve as real-world usage grows. Feedback and contributions welcome!

Licensing

This project is publicly available under the GNU General Public License v3.0. It may optionally be distributed under the MIT license by commercial arrangement.


Was this useful? Buy me a coffee

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Sponsor this project

  •  

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •