{"id":26060852,"url":"https://github.com/riker-rs/riker-cqrs","last_synced_at":"2025-08-29T17:26:03.378Z","repository":{"id":57660926,"uuid":"140158221","full_name":"riker-rs/riker-cqrs","owner":"riker-rs","description":"Command Query Responsibility Separation for the Riker framework","archived":false,"fork":false,"pushed_at":"2023-03-15T22:49:55.000Z","size":15,"stargazers_count":37,"open_issues_count":2,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-07-24T09:15:42.234Z","etag":null,"topics":["actors","cqrs","event-sourcing","rust"],"latest_commit_sha":null,"homepage":"https://riker.rs","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/riker-rs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2018-07-08T10:32:01.000Z","updated_at":"2025-02-07T10:46:21.000Z","dependencies_parsed_at":"2025-04-11T09:02:05.995Z","dependency_job_id":"5381bc6a-5c89-4c07-af11-ca5c0e6b4caa","html_url":"https://github.com/riker-rs/riker-cqrs","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/riker-rs/riker-cqrs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/riker-rs%2Friker-cqrs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/riker-rs%2Friker-cqrs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/riker-rs%2Friker-cqrs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/riker-rs%2Friker-cqrs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/riker-rs","download_url":"https://codeload.github.com/riker-rs/riker-cqrs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/riker-rs%2Friker-cqrs/sbom","scorecard":{"id":776450,"data":{"date":"2025-08-11","repo":{"name":"github.com/riker-rs/riker-cqrs","commit":"d8b8f35571d0af59545cfa472b1899e27a1bd991"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"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":"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":"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":"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":"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":"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":"Code-Review","score":0,"reason":"Found 0/16 approved changesets -- score normalized to 0","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":"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":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"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":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"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":"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 6 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"}}]},"last_synced_at":"2025-08-23T03:45:21.034Z","repository_id":57660926,"created_at":"2025-08-23T03:45:21.034Z","updated_at":"2025-08-23T03:45:21.034Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272084332,"owners_count":24870582,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-25T02:00:12.092Z","response_time":1107,"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":["actors","cqrs","event-sourcing","rust"],"created_at":"2025-03-08T14:47:28.681Z","updated_at":"2025-08-29T17:26:03.340Z","avatar_url":"https://github.com/riker-rs.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Riker Command Query Responsibility Separation (CQRS)\n\n## Overview\n\nCommand Query Responsibility Separation (CQRS) builds of top of event sourcing to provide a more structured approach to persistence. Event sourcing alone works well for restoring individual actor state in an actor system with a fixed number of actors. This could be taken further so that data entities can be modelled as actors. For example, an entity could be a User, Account, Post, Transaction, Order, etc, where every instance is represented by its own actor instance.\n\nTo make changes to an entity commands are sent to the actor representing that entity. For example, to change the password of a `User` entity an `UpdatePasswordCmd` can be sent, or to disable the user a `DisableUserCmd` can be sent. When an actor receives a command it validates it and then emits an event that will be persisted and applied:\n\n```\nUpdatePasswordCmd =\u003e PasswordUpdatedEvt\nDisableUserCmd =\u003e UserDisabledEvt\n```\n\nTo help with setting up entities and command management Riker CQRS is a separate crate (`riker-cqrs`) that introduces:\n\n- Entity management\n- Command based messaging\n\nSince each entity has its own actor there needs to be a coordinator that creates actors when needed and routes commands to the right actor. Basic bookkeeping is also required, so that actors can sleep and be removed from memory after a period of inactivity and then restored when they're needed to handle a command.\n\nLet's look at how to set up an entity manager that represents bank accounts `BankAccount`:\n\n`Cargo.toml`:\n```toml\n[dependencies]\nriker = \"0.1.8\"\nriker-default = \"0.1.8\"\nriker-cqrs = \"0.1.8\"\n```\n\nCode:\n```rust\nuse riker::actors::*;\nuse riker_default::DefaultModel;\nuse riker_cqrs::*;\n\nlet model: DefaultModel\u003cTestMsg\u003e = DefaultModel::new();\nlet sys = ActorSystem::new(\u0026model).unwrap();\n\nlet em = Entity::new(\u0026sys,\n                    BankAccountProps,\n                    \"BankAccont\",\n                    None).unwrap();\n```\n\nHere an `Entity` has been created that will manage all instances of bank accounts. It will create new actors if necessary and route commands.\n\nLet's create a new bank account and make a first deposit:\n\n```rust\nlet number = \"12345678\";\nlet name = \"Dolores Abernathy\";\n\n// create bank account\nlet cmd = CQMsg::Cmd(number.into(), Protocol::CreateAccountCmd(name.into()));\nem.tell(cmd, None);\n\n// deposit $1000\nlet cmd = CQMsg::Cmd(number.into(), Protocol::DepositCmd(1000));\nem.tell(cmd, None);\n```\n\nCommands require an ID and based on that ID the entity manager will route the command to the actor for that ID. If there is no currently live actor in memory for that ID the manager will start an actor. Any events associated with that ID will be loaded and the actor state restored before handling the command.\n\nInstead of managing actor creation directly using `actor_of` the entity manager does this instead. You will have noticed that `Entity::new` in the example was passed `BankAccountProps`. This is a struct that implements the `EntityActorProps` trait.\n\nSince each entity actor requires its own unique ID the standard `Props` used in `actor_of` is not sufficient. Instead `EntityActorProps` is implemented:\n\n```rust\nstruct BankAccountProps;\n\nimpl EntityActorProps for BankAccountProps {\n    type Msg = Protocol;\n\n    fn props(\u0026self, id: String) -\u003e BoxActorProd\u003cSelf::Msg\u003e {\n        Props::new_args(Box::new(BankAccountActor::new), id)\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Friker-rs%2Friker-cqrs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Friker-rs%2Friker-cqrs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Friker-rs%2Friker-cqrs/lists"}