{"id":21657267,"url":"https://github.com/cantido/calamity","last_synced_at":"2025-10-15T21:44:09.563Z","repository":{"id":43078490,"uuid":"393769560","full_name":"Cantido/calamity","owner":"Cantido","description":"An event-sourcing library with a focus on pure functions and protocols","archived":false,"fork":false,"pushed_at":"2024-06-20T15:10:00.000Z","size":112,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-06-21T08:46:17.527Z","etag":null,"topics":["cqrs-es","elixir","event-sourcing","hacktoberfest"],"latest_commit_sha":null,"homepage":"","language":"Elixir","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/Cantido.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"code_of_conduct.md","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}},"created_at":"2021-08-07T19:05:29.000Z","updated_at":"2024-06-20T15:09:58.000Z","dependencies_parsed_at":"2024-01-09T05:28:26.014Z","dependency_job_id":"8cf49084-9d60-4e51-88ec-b7b4d419bed2","html_url":"https://github.com/Cantido/calamity","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cantido%2Fcalamity","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cantido%2Fcalamity/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cantido%2Fcalamity/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cantido%2Fcalamity/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Cantido","download_url":"https://codeload.github.com/Cantido/calamity/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244554120,"owners_count":20471173,"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","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":["cqrs-es","elixir","event-sourcing","hacktoberfest"],"created_at":"2024-11-25T09:20:20.608Z","updated_at":"2025-10-15T21:44:04.528Z","avatar_url":"https://github.com/Cantido.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--\nSPDX-FileCopyrightText: 2021 Rosa Richter\n\nSPDX-License-Identifier: MIT\n--\u003e\n\n# Calamity\n\n[![builds.sr.ht status](https://builds.sr.ht/~cosmicrose/calamity.svg)](https://builds.sr.ht/~cosmicrose/calamity)\n[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg)](https://github.com/RichardLitt/standard-readme)\n[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg)](code_of_conduct.md)\n\nAn event-sourcing library with a focus on pure functions and protocols.\n\nCalamity is structured similarly to Commanded,\nand it is a goal of this project to support Commanded components via structs implementing Calamity protocols.\n\nUseful implementations of these protocols are not currently provided.\nCalamity only dispatches commands to the given aggregate and process manager stores,\nand you must provide the stores yourself.\n\nThis project is in the experimental stage; do not use it in production.\n\n## Usage\n\nUsing Calamity means implementing a few protocols and behaviors.\nSee `test/support` for a simplified example of how to use this library in a single process.\n\n### Aggregates\n\nTo create an aggregate, `use` the `:aggregate` macro available in the main `Calamity` module,\nthen implement the `Calamity.Aggregate` protocol.\n\n```elixir\ndefmodule MyApp.BankAccount do\n  use Calamity, :aggregate\n\n  defstruct [\n    ...\n  ]\n\n  def new(id) do\n    ...\n  end\n\n  defimpl Calamity.Aggregate do\n    ...\n  end\nend\n```\n\n### Aggregate Stores\n\nTo update and store aggregates, the `Calamity.dispatch/5` function requires a struct implementing the `Access` protocol.\nAggregates must be accessed by ID and return a struct implementing the `Calamity.Aggregate` protocol.\n\nCalamity uses `Access.get_and_update/3` to transactionally update the aggregate in the store and return events.\nThis means that Calamity supports aggregates stored in GenServers as well as simple maps.\n\nRead [`guides/plugins.md`](guides/plugins.md) for examples of aggregate and process manager stores,\nas well as an over-simplified list store for events.\n\n\n### Events\n\nEvents need no protocol, so you can use plain structs.\n\n```elixir\ndefmodule MyApp.Events.AccountCreated do\n  defstruct [\n    ...\n  ]\nend\n```\n\n### Commands\n\nCommands must implement the `Calamity.Command` protocol.\n\n```elixir\ndefmodule MyApp.Commands.DepositFunds do\n  defstruct [\n    ...\n  ]\n\n  defimpl Calamity.Command do\n    ...\n  end\nend\n```\n\n### Process Managers\n\nProcess managers must `use` the `:process_manager` macro available in the main `Calamity` module,\nthen implement the `Calamity.ProcessManager` protocol.\n\n```elixir\ndefmodule MyApp.ProcessManagers.Transfer do\n  use Calamity, :process_manager\n\n  defstruct [\n    ...\n  ]\n\n  def new(pm_id) do\n    ...\n  end\n\n  def interested?(event) do\n    ...\n  end\n\n  defimpl Calamity.ProcessManager do\n    ...\n  end\nend\n```\n\n### Process Manager Stores\n\nActive process managers are stored in a multi-level data structure that must implement the `Access` protocol.\nThe first level maps modules to collections of process managers implementing that module,\nand the second level maps IDs to specific process manager structs.\nIf you were to use `Map`s, the process manager store would look like this:\n\n```elixir\n%{\n  MyApp.ProcessManagers.TransferFunds =\u003e %{\n    \"b0bda821-4bdd-4e27-ad95-f504b00a1282\" =\u003e %MyApp.ProcessManagers.TransferFunds{id: ...}\n  }\n}\n```\n\nCalamity uses `Access.get_and_update/3` to transactionally update the process manager store and return commands.\nThis means that Calamity supports process managers stored in GenServers as well as simple maps.\n\nRead [`guides/plugins.md`](guides/plugins.md) for examples of aggregate and process manager stores,\nas well as an over-simplified list store for events.\n\n### Dispatching Events\n\nEvents are dispatched using `Calamity.dispatch/5`.\nSince Calamity uses pure functions and tries to minimize process count,\nall components of the system must be passed to `dispatch/5`,\nand it will return updated versions of those arguments.\n\nThe arguments to `dispatch/5` are:\n\n1. The command (implementing the `Calamity.Command` protocol)\n1. The aggregate store (implementing the `Access` protocol)\n1. An enumerable of process manager modules (that `use Calamity, :process_manager`)\n1. The process manager store (implementing `Access` at two levels, see above)\n1. The event store (implementing the `Calamity.EventStore` and `Collectable` protocols)\n\nThis example is taken from Calamity's tests.\nIt is dispatching the `RequestTransfer` command to an aggregate store containing two aggregates.\nThe `Transfer` process manager is used, but there are currently no active process managers.\nLast, the `ListEventStore` is used to store and publish events.\n\n```elixir\nCalamity.dispatch(\n  %Calamity.Commands.RequestTransfer{from: \"1\", to: \"2\", amount: 100, transfer_id: \"asdfasdfasdf\"},\n  %{\n    \"1\" =\u003e %Calamity.BankAccount{account_id: \"1\", name: \"From account\", balance: 100},\n    \"2\" =\u003e %Calamity.BankAccount{account_id: \"2\", name: \"To account\", balance: 0}\n  },\n  [Calamity.ProcessManagers.Transfer],\n  %{},\n  %Calamity.ListEventStore{}\n)\n```\n\nThis will return a tuple containing the updated aggregate store, the updated process manager store, and the updated event store.\n\n```elixir\n{new_aggregates, new_process_managers, event_store}\n```\n\nThis library does not implement its own event store or aggregate store.\nIf you're just getting started, use maps like in the examples, and wrap calls in a `GenServer` to ensure sequential access.\n\nRead [`guides/plugins.md`](guides/plugins.md) for examples of aggregate and process manager stores,\nas well as an over-simplified list store for events.\n\n## Maintainer\n\nThis project was developed by [Rosa Richter](https://about.me/rosa.richter).\nYou can get in touch with her on [Keybase.io](https://keybase.io/cantido).\n\n## Contributing\n\nQuestions and pull requests are more than welcome.\nI follow Elixir's tenet of bad documentation being a bug,\nso if anything is unclear, please [file an issue](https://todo.sr.ht/~cosmicrose/calamity) or ask on the [mailing list]!\nIdeally, my answer to your question will be in an update to the docs.\n\nPlease see [CONTRIBUTING.md](CONTRIBUTING.md) for all the details you could ever want about helping me with this project.\n\nNote that this project is released with a Contributor [Code of Conduct].\nBy participating in this project you agree to abide by its terms.\n\n## License\n\nMIT License\n\nCopyright 2021 Rosa Richter\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n[Code of Conduct]: code_of_conduct.md\n[mailing list]: https://lists.sr.ht/~cosmicrose/calamity\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcantido%2Fcalamity","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcantido%2Fcalamity","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcantido%2Fcalamity/lists"}