{"id":51195642,"url":"https://github.com/nanlong/eventide","last_synced_at":"2026-06-27T20:01:22.103Z","repository":{"id":317144839,"uuid":"1065975055","full_name":"nanlong/eventide","owner":"nanlong","description":"Pragmatic Rust toolkit for Domain-Driven Design with first-class event sourcing and CQRS. Hexagonal architecture, async-native, zero database dependencies in the domain   layer.","archived":false,"fork":false,"pushed_at":"2026-05-02T05:54:10.000Z","size":845,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-04T05:14:06.085Z","etag":null,"topics":["aggregate","async","clean-architecture","cqrs","ddd","domain-driven-design","event-driven","event-sourcing","hexagonal-architecture","proc-macros","rust","tokio"],"latest_commit_sha":null,"homepage":"https://docs.rs/eventide","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nanlong.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-28T20:02:04.000Z","updated_at":"2026-05-25T04:04:03.000Z","dependencies_parsed_at":"2025-09-29T07:07:52.222Z","dependency_job_id":"506b3b21-b2e1-4a62-8bc8-efc4a5ff2c93","html_url":"https://github.com/nanlong/eventide","commit_stats":null,"previous_names":["nanlong/ddd","nanlong/eventide"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/nanlong/eventide","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanlong%2Feventide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanlong%2Feventide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanlong%2Feventide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanlong%2Feventide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nanlong","download_url":"https://codeload.github.com/nanlong/eventide/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanlong%2Feventide/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34866118,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-27T02:00:06.362Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aggregate","async","clean-architecture","cqrs","ddd","domain-driven-design","event-driven","event-sourcing","hexagonal-architecture","proc-macros","rust","tokio"],"created_at":"2026-06-27T20:01:09.886Z","updated_at":"2026-06-27T20:01:22.094Z","avatar_url":"https://github.com/nanlong.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# eventide\n\n[![Crates.io](https://img.shields.io/crates/v/eventide.svg)](https://crates.io/crates/eventide)\n[![Documentation](https://docs.rs/eventide/badge.svg)](https://docs.rs/eventide)\n[![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](#license)\n\n\u003e 中文版本: [README.zh.md](README.zh.md)\n\nA pragmatic Rust toolkit for **Domain-Driven Design** with first-class\nsupport for **event sourcing** and **CQRS**, organised as a hexagonal\nworkspace.\n\n```text\neventide-application  →  eventide-domain  ←  eventide-macros\n                                 ↑\n                                 └──── (optional) infra-sqlx adapters\n```\n\n## Workspace layout\n\n| Crate                                             | What it ships                                                                          |\n|---------------------------------------------------|----------------------------------------------------------------------------------------|\n| [`eventide`](eventide/)                           | Umbrella crate. Re-exports the three layers and a curated `prelude`.                   |\n| [`eventide-domain`](eventide-domain/)             | Pure domain layer: aggregates, entities, value objects, events, repository traits.     |\n| [`eventide-application`](eventide-application/)   | Application layer: command bus, query bus, handlers, application context.              |\n| [`eventide-macros`](eventide-macros/)             | Procedural macros (`#[entity]`, `#[entity_id]`, `#[domain_event]`, `#[value_object]`). |\n\n## Quick start\n\nMost users only need to depend on the umbrella crate:\n\n```toml\n[dependencies]\neventide = \"0.1\"\n```\n\n\u003e A single dependency covers the common path. Macro-generated\n\u003e `Serialize` / `Deserialize` derives route through `eventide`'s internal\n\u003e `serde` re-export, so the macros work without a direct `serde`\n\u003e dependency. `eventide::tokio` exposes the Tokio runtime (with the\n\u003e `eventing` feature, default), and `eventide::async_trait` lets you\n\u003e write `#[async_trait]` on your own trait impls. Add `serde` / `tokio` /\n\u003e `async-trait` to your own `Cargo.toml` only when you reach for them\n\u003e outside these re-exports (extra derives, custom `#[serde(...)]`\n\u003e attributes, advanced runtime features). See\n\u003e [`eventide/README.md`](eventide/README.md) for details.\n\nA minimal aggregate looks like this:\n\n```rust\nuse eventide::prelude::*;\n\n#[entity_id]\nstruct AccountId(String);\n\n#[entity(id = AccountId)]\n#[derive(Clone)]\nstruct BankAccount {\n    balance: i64,\n}\n\n#[derive(Debug)]\nenum AccountCommand {\n    Deposit(i64),\n    Withdraw(i64),\n}\n\n#[domain_event(version = 1)]\nenum AccountEvent {\n    #[event(event_type = \"account.deposited\")]\n    Deposited { amount: i64 },\n    #[event(event_type = \"account.withdrawn\")]\n    Withdrawn { amount: i64 },\n}\n\nimpl Aggregate for BankAccount {\n    const TYPE: \u0026'static str = \"bank_account\";\n    type Command = AccountCommand;\n    type Event = AccountEvent;\n    type Error = DomainError;\n\n    fn execute(\u0026self, cmd: AccountCommand) -\u003e Result\u003cVec\u003cAccountEvent\u003e, DomainError\u003e {\n        match cmd {\n            AccountCommand::Deposit(n) if n \u003e 0 =\u003e Ok(vec![AccountEvent::Deposited {\n                id: uuid::Uuid::new_v4().to_string(),\n                aggregate_version: self.version().next().value(),\n                amount: n,\n            }]),\n            AccountCommand::Withdraw(n) if n \u003e 0 \u0026\u0026 self.balance \u003e= n =\u003e {\n                Ok(vec![AccountEvent::Withdrawn {\n                    id: uuid::Uuid::new_v4().to_string(),\n                    aggregate_version: self.version().next().value(),\n                    amount: n,\n                }])\n            }\n            _ =\u003e Err(DomainError::invalid_command(\"invalid amount or insufficient\")),\n        }\n    }\n\n    fn apply(\u0026mut self, event: \u0026AccountEvent) {\n        match event {\n            AccountEvent::Deposited { aggregate_version, amount, .. } =\u003e {\n                self.balance += amount;\n                self.version = Version::from_value(*aggregate_version);\n            }\n            AccountEvent::Withdrawn { aggregate_version, amount, .. } =\u003e {\n                self.balance -= amount;\n                self.version = Version::from_value(*aggregate_version);\n            }\n        }\n    }\n}\n```\n\n## Build, test, run examples\n\nRequires Rust 1.85+ (workspace uses Rust 2024 edition).\n\n```bash\ncargo build --workspace\ncargo test  --workspace --all-features\n\n# Domain examples\ncargo run -p eventide-domain --example event_upcasting\ncargo run -p eventide-domain --example event_repository\ncargo run -p eventide-domain --example snapshot_repository\ncargo run -p eventide-domain --example eventing_inmemory\n\n# Application examples\ncargo run -p eventide-application --example inmemory_command_bus\ncargo run -p eventide-application --example inmemory_query_bus\n```\n\n## Why eventide\n\n- **Hexagonal-friendly.** The domain layer defines abstractions (`EventRepository`, `SnapshotRepository`, `AggregateRepository`); infrastructure crates implement them.\n- **Event sourcing built in.** Aggregates emit events, the engine persists them, and an upcasting chain handles schema evolution without touching historical data.\n- **CQRS by default.** Separate `CommandBus` and `QueryBus` with type-safe handler registration.\n- **Procedural macros that disappear.** `#[entity]`, `#[entity_id]`, `#[domain_event]`, and `#[value_object]` remove the boilerplate so business invariants stay readable.\n- **Async-native.** Tokio-based event engine with cooperative dispatch, retry, and dead-letter handling.\n- **No vendor lock-in.** The domain crate has zero database dependencies; pick (or write) the infrastructure adapter you need.\n\n## Feature flags (umbrella crate)\n\n| Flag           | Default | What it does                                                                                       |\n|----------------|---------|----------------------------------------------------------------------------------------------------|\n| `eventing`     | yes     | Asynchronous event subsystem (bus / engine / dispatcher / reclaimer) on top of `tokio`.            |\n| `macros`       | yes     | Re-export `eventide-macros` as `eventide::macros`.                                                 |\n| `application`  | yes     | Re-export `eventide-application` as `eventide::application`.                                       |\n| `infra-sqlx`   | no      | Opt-in `sqlx` conversions on serialized events / snapshots for Postgres-backed event stores.       |\n\nPick individual sub-crates instead of the umbrella when you want finer control:\n\n```toml\n[dependencies]\neventide-domain = \"0.1\"\neventide-macros = \"0.1\"\n# Skip eventide-application if you do not need the bus.\n```\n\n## Documentation\n\n- API reference: \u003chttps://docs.rs/eventide\u003e\n- Per-layer docs:\n  - \u003chttps://docs.rs/eventide-domain\u003e\n  - \u003chttps://docs.rs/eventide-application\u003e\n  - \u003chttps://docs.rs/eventide-macros\u003e\n\nEach sub-crate also ships its own `README.md` with focused documentation.\n\n## License\n\nLicensed under either of\n\n- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or \u003chttp://www.apache.org/licenses/LICENSE-2.0\u003e)\n- MIT license ([LICENSE-MIT](LICENSE-MIT) or \u003chttp://opensource.org/licenses/MIT\u003e)\n\nat your option.\n\n## Contributing\n\nUnless you explicitly state otherwise, any contribution intentionally\nsubmitted for inclusion in the work by you, as defined in the Apache-2.0\nlicense, shall be dual licensed as above, without any additional terms or\nconditions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnanlong%2Feventide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnanlong%2Feventide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnanlong%2Feventide/lists"}