{"id":13550290,"url":"https://github.com/pholactery/eventsourcing","last_synced_at":"2026-04-09T03:32:10.065Z","repository":{"id":51564204,"uuid":"131071209","full_name":"pholactery/eventsourcing","owner":"pholactery","description":"Event Sourcing Library for Rust","archived":false,"fork":false,"pushed_at":"2020-05-03T04:35:22.000Z","size":10585,"stargazers_count":87,"open_issues_count":1,"forks_count":17,"subscribers_count":6,"default_branch":"master","last_synced_at":"2026-03-18T17:11:09.818Z","etag":null,"topics":["event-sourcing","events","eventstore","rust","rust-library"],"latest_commit_sha":null,"homepage":null,"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/pholactery.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-04-25T22:40:46.000Z","updated_at":"2025-12-29T17:33:06.000Z","dependencies_parsed_at":"2022-08-21T18:20:37.761Z","dependency_job_id":null,"html_url":"https://github.com/pholactery/eventsourcing","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/pholactery/eventsourcing","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pholactery%2Feventsourcing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pholactery%2Feventsourcing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pholactery%2Feventsourcing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pholactery%2Feventsourcing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pholactery","download_url":"https://codeload.github.com/pholactery/eventsourcing/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pholactery%2Feventsourcing/sbom","scorecard":{"id":732147,"data":{"date":"2025-08-11","repo":{"name":"github.com/pholactery/eventsourcing","commit":"31c1f9d5589ede0fd0e98f4a7665b85a53ca7962"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.9,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Code-Review","score":1,"reason":"Found 3/30 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 4 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"32 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: RUSTSEC-2020-0006 / GHSA-vqx7-pw4r-29rr","Warn: Project is vulnerable to: RUSTSEC-2022-0078 / GHSA-f85w-wvc7-crwc","Warn: Project is vulnerable to: RUSTSEC-2020-0159","Warn: Project is vulnerable to: RUSTSEC-2020-0061 / GHSA-p9m5-3hj7-cp5r","Warn: Project is vulnerable to: RUSTSEC-2020-0060 / GHSA-r93v-9p5q-vhpf","Warn: Project is vulnerable to: RUSTSEC-2020-0062 / GHSA-5r9g-j7jj-hw6c","Warn: Project is vulnerable to: RUSTSEC-2023-0034 / GHSA-f8vr-r385-rh5r","Warn: Project is vulnerable to: RUSTSEC-2024-0003 / GHSA-8r5v-vm4m-4g25","Warn: Project is vulnerable to: RUSTSEC-2024-0332 / GHSA-q6cp-qfwq-4gcv","Warn: Project is vulnerable to: RUSTSEC-2021-0020 / GHSA-6hfq-h8hq-87mf","Warn: Project is vulnerable to: RUSTSEC-2021-0079 / GHSA-5h46-h7hh-c6x9","Warn: Project is vulnerable to: RUSTSEC-2021-0078 / GHSA-f3pg-qwvg-p99c","Warn: Project is vulnerable to: RUSTSEC-2022-0022 / GHSA-f67m-9j94-qv9j","Warn: Project is vulnerable to: RUSTSEC-2024-0421 / GHSA-h97m-ww89-6jmq","Warn: Project is vulnerable to: RUSTSEC-2020-0080","Warn: Project is vulnerable to: RUSTSEC-2020-0078 / GHSA-458v-4hrf-g3m4 / GHSA-c79c-gwph-gqfm","Warn: Project is vulnerable to: RUSTSEC-2020-0016","Warn: Project is vulnerable to: RUSTSEC-2023-0022 / GHSA-3gxf-9r58-2ghg","Warn: Project is vulnerable to: RUSTSEC-2023-0024 / GHSA-6hcf-g6gr-hhcr","Warn: Project is vulnerable to: RUSTSEC-2023-0023 / GHSA-9qwg-crg9-m2vc","Warn: Project is vulnerable to: RUSTSEC-2023-0044 / GHSA-xcf7-rvmh-g6q4","Warn: Project is vulnerable to: RUSTSEC-2023-0072","Warn: Project is vulnerable to: GHSA-q445-7m23-qrmw","Warn: Project is vulnerable to: RUSTSEC-2024-0357","Warn: Project is vulnerable to: RUSTSEC-2025-0004 / GHSA-rpmj-rpgj-qmpm","Warn: Project is vulnerable to: RUSTSEC-2023-0018 / GHSA-mc8h-8q98-g5hr","Warn: Project is vulnerable to: RUSTSEC-2021-0003 / GHSA-43w2-9j62-hq99","Warn: Project is vulnerable to: RUSTSEC-2020-0071 / GHSA-wcg3-cvx6-7396","Warn: Project is vulnerable to: RUSTSEC-2021-0124 / GHSA-fg7r-2g4j-5cgr","Warn: Project is vulnerable to: RUSTSEC-2023-0005 / GHSA-4q83-7cq4-p6wg","Warn: Project is vulnerable to: GHSA-rr8g-9fpq-6wmg","Warn: Project is vulnerable to: RUSTSEC-2025-0023"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-22T14:43:47.846Z","repository_id":51564204,"created_at":"2025-08-22T14:43:47.846Z","updated_at":"2025-08-22T14:43:47.846Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31584572,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"online","status_checked_at":"2026-04-09T02:00:06.848Z","response_time":112,"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":["event-sourcing","events","eventstore","rust","rust-library"],"created_at":"2024-08-01T12:01:31.180Z","updated_at":"2026-04-09T03:32:10.042Z","avatar_url":"https://github.com/pholactery.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"# Event Sourcing\n\nAn eventsourcing library for Rust\n\nOne of the benefits of [event sourcing](https://martinfowler.com/eaaDev/EventSourcing.html)\nis that in most cases, embracing this pattern does not require that much code.\nHowever, there's still a bit of boilerplate required as well as the discipline for ensuring\nthe events, commands, and aggregates all perform their roles without sharing concerns.\n\nThe fundamental workflow to remember is that **commands** are applied to **aggregates**,\nwhich then emit one or more events. An **aggregate**'s business logic is also responsible\nfor returning a new state from a previous state combined with a new event. Put\nmathematically, this looks like:\n\n ```terminal\n f(state1, event) = state2\n ```\n\nThere are some event sourcing libraries that allow for, or even encourage, mutation of\nstate in memory. I prefer a more functional approach, and the design of this library\nreflects that. It encourages you to write unit tests for your aggregate business logic that\nare predictable and can be executed in isolation without concern for how you receive events\nor how you persist them in a store.\n \nTo start, you create an undecorated enum for your **Command** type:\n ```rust\n enum LocationCommand {\n    UpdateLocation { lat: f32, long: f32, alt: f32 },\n}\n ```\n\n Next, you create an enum for your events and use a derive macro to write some boilerplate\n on your behalf. Note how the command variants are _imperative_ statements while the\n event variants are _verbs phrases in the past tense_. While this is by convention and\n not enforced via code, this is a good practice to adopt.\n \n ```rust\n#[derive(Serialize, Deserialize, Debug, Clone, Event)]\n#[event_type_version(\"1.0\")]\n#[event_source(\"events://github.com/pholactery/eventsourcing/samples/location\")]\nenum LocationEvent {\n    LocationUpdated { lat: f32, long: f32, alt: f32 },\n}\n ```\n\n We then define a type that represents the state to be used by an aggregate.\n With that in place, we write all of our business logic, the core of our event sourcing system,\n in the aggregate.\n \n```rust\n#[derive(Debug, Clone)]\nstruct LocationData {\n    lat: f32,\n    long: f32,\n    alt: f32,\n    generation: u64,\n}\n\nimpl AggregateState for LocationData {\n    fn generation(\u0026self) -\u003e u64 {\n        self.generation\n    }\n}\n\nstruct Location;\nimpl Aggregate for Location {\n   type Event = LocationEvent;\n   type Command = LocationCommand;\n   type State = LocationData;\n\n   fn apply_event(state: \u0026Self::State, evt: \u0026Self::Event) -\u003e Result\u003cSelf::State\u003e {\n       // TODO: validate event\n       let ld = match evt {\n           LocationEvent::LocationUpdated { lat, long, alt } =\u003e LocationData {\n               lat: *lat,\n               long: *long,\n               alt: *alt,\n               generation: state.generation + 1,\n           },\n       };\n       Ok(ld)\n   }\n\n   fn handle_command(_state: \u0026Self::State, cmd: \u0026Self::Command) -\u003e Result\u003cVec\u003cSelf::Event\u003e\u003e {\n       // TODO: add code to validate state and command\n\n       // if validation passes...\n       Ok(vec![LocationEvent::LocationUpdated { lat: 10.0, long: 10.0, alt: 10.0 }])\n   }\n}\n ```\n \n For more examples of usage, check out the [examples](https://github.com/pholactery/eventsourcing/tree/master/examples) directory in github.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpholactery%2Feventsourcing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpholactery%2Feventsourcing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpholactery%2Feventsourcing/lists"}