# eventide

[![Crates.io](https://img.shields.io/crates/v/eventide.svg)](https://crates.io/crates/eventide)
[![Documentation](https://docs.rs/eventide/badge.svg)](https://docs.rs/eventide)
[![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](#license)

> 中文版本: [README.zh.md](README.zh.md)

A pragmatic Rust toolkit for **Domain-Driven Design** with first-class
support for **event sourcing** and **CQRS**.

`eventide` is the umbrella crate that re-exports the three building blocks
of the toolkit so you can depend on a single crate and pull in everything
you need.

| Module          | Source crate                                                | Purpose                                                            |
|-----------------|-------------------------------------------------------------|--------------------------------------------------------------------|
| `domain`        | [`eventide-domain`](https://crates.io/crates/eventide-domain) | Aggregates, entities, value objects, events, repositories.         |
| `application`   | [`eventide-application`](https://crates.io/crates/eventide-application) | Command bus, query bus, handlers, application context.             |
| `macros`        | [`eventide-macros`](https://crates.io/crates/eventide-macros) | `#[entity]`, `#[entity_id]`, `#[domain_event]`, `#[value_object]`. |

## Quick start

Add the crate to your `Cargo.toml`:

```toml
[dependencies]
eventide = "0.1"
```

> One dependency is enough for the common path. The umbrella crate
> re-exports every runtime symbol you need on top of `eventide`:
>
> - `serde` — generated `Serialize` / `Deserialize` derives route through
>   `eventide`'s internal re-export, so `#[entity]`, `#[entity_id]`,
>   `#[domain_event]` and `#[value_object]` work without a direct `serde`
>   dependency.
> - `eventide::tokio` — the Tokio runtime (e.g. `#[eventide::tokio::main]`,
>   `eventide::tokio::spawn`). Available with the `eventing` feature
>   (default).
> - `eventide::async_trait` — write `#[async_trait]` on your own
>   trait impls (`DomainService`, `EventHandler`, etc.) without depending
>   on `async-trait` directly.
>
> Add `serde`, `tokio` or `async-trait` to your own `Cargo.toml` only when
> you reach for them outside the re-exports (extra derives, custom
> `#[serde(...)]` attributes, advanced runtime features).

Define an aggregate:

```rust
use eventide::prelude::*;

#[entity_id]
struct UserId(String);

#[entity(id = UserId)]
#[derive(Clone)]
struct User {
    name: String,
}

#[derive(Debug)]
enum UserCommand {
    Rename { name: String },
}

#[domain_event(version = 1)]
enum UserEvent {
    #[event(event_type = "user.renamed")]
    Renamed { name: String },
}

impl Aggregate for User {
    const TYPE: &'static str = "user";
    type Command = UserCommand;
    type Event = UserEvent;
    type Error = DomainError;

    fn execute(&self, cmd: UserCommand) -> Result<Vec<UserEvent>, DomainError> {
        match cmd {
            UserCommand::Rename { name } if !name.is_empty() => Ok(vec![UserEvent::Renamed {
                id: uuid::Uuid::new_v4().to_string(),
                aggregate_version: self.version().next().value(),
                name,
            }]),
            _ => Err(DomainError::invalid_value("name must not be empty")),
        }
    }

    fn apply(&mut self, event: &UserEvent) {
        match event {
            UserEvent::Renamed { aggregate_version, name, .. } => {
                self.name = name.clone();
                self.version = Version::from_value(*aggregate_version);
            }
        }
    }
}
```

## Feature flags

All flags are enabled by default. Disable them selectively with
`default-features = false` to trim the dependency tree.

| Flag           | Default | What it does                                                                                       |
|----------------|---------|----------------------------------------------------------------------------------------------------|
| `eventing`     | yes     | Asynchronous event subsystem (bus / engine / dispatcher / reclaimer) on top of `tokio`.            |
| `macros`       | yes     | Re-export `eventide-macros` as `eventide::macros`.                                                 |
| `application`  | yes     | Re-export `eventide-application` as `eventide::application`.                                       |
| `infra-sqlx`   | no      | Opt-in `sqlx` conversions on serialized events / snapshots for Postgres-backed event stores.       |

Example: depend on the domain layer only.

```toml
[dependencies]
eventide = { version = "0.1", default-features = false }
```

## Layered architecture

`eventide` is intentionally split into independent crates so the pieces
you do not need can be left out. The dependency graph flows in one
direction:

```text
eventide-application  →  eventide-domain  ←  eventide-macros
                                 ↑
                                 └──── (optional) infra-sqlx
```

The domain crate never depends on application or infrastructure types,
which keeps your business logic pure and easy to test.

## Why eventide

- **Hexagonal-friendly.** The domain layer defines the abstractions (`EventRepository`, `SnapshotRepository`, `AggregateRepository`); infrastructure crates implement them.
- **Event sourcing built in.** Aggregates emit events, the engine persists them, and an upcasting chain handles schema evolution without touching historical data.
- **CQRS by default.** Separate `CommandBus` and `QueryBus` with type-safe handler registration.
- **Procedural macros that disappear.** `#[entity]`, `#[entity_id]`, `#[domain_event]`, and `#[value_object]` remove the boilerplate so the business invariants stay readable.
- **Async-native.** Tokio-based event engine with cooperative dispatch, retry, and dead-letter handling.
- **No vendor lock-in.** The domain crate has zero database dependencies; pick (or write) the infrastructure adapter you need.

## Documentation

- API reference: <https://docs.rs/eventide>
- Workspace overview: [GitHub](https://github.com/nanlong/eventide)
- Examples: see the `examples/` directory in each sub-crate.

## License

Licensed under either of

- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](../LICENSE-MIT) or <http://opensource.org/licenses/MIT>)

at your option.

## Contributing

Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the Apache-2.0
license, shall be dual licensed as above, without any additional terms or
conditions.
