{"id":15666939,"url":"https://github.com/Tochemey/ego","last_synced_at":"2025-10-14T05:31:33.704Z","repository":{"id":153307101,"uuid":"628768209","full_name":"Tochemey/ego","owner":"Tochemey","description":"[Go]Minimal event sourcing/CQRS library using protocol buffers for commands, events and states.","archived":false,"fork":false,"pushed_at":"2025-01-10T20:28:48.000Z","size":581,"stargazers_count":41,"open_issues_count":2,"forks_count":5,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-01-21T06:11:26.104Z","etag":null,"topics":["cloud-computing","concurrent-programming","cqrs","cqrs-es","distributed-systems","event-driven","event-sourcing","go","golang","microservice","protocol-buffers","reactive-programming"],"latest_commit_sha":null,"homepage":"","language":"Go","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/Tochemey.png","metadata":{"files":{"readme":"readme.md","changelog":null,"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":"2023-04-16T23:58:44.000Z","updated_at":"2025-01-10T20:22:47.000Z","dependencies_parsed_at":"2023-09-27T15:22:49.249Z","dependency_job_id":"bdf10193-251d-4713-b4a8-6c8391f2b8fa","html_url":"https://github.com/Tochemey/ego","commit_stats":{"total_commits":114,"total_committers":3,"mean_commits":38.0,"dds":0.2192982456140351,"last_synced_commit":"9d13afbf9d65c996db607806d5d50450aaeba2de"},"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tochemey%2Fego","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tochemey%2Fego/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tochemey%2Fego/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tochemey%2Fego/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Tochemey","download_url":"https://codeload.github.com/Tochemey/ego/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":236451125,"owners_count":19150805,"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":["cloud-computing","concurrent-programming","cqrs","cqrs-es","distributed-systems","event-driven","event-sourcing","go","golang","microservice","protocol-buffers","reactive-programming"],"created_at":"2024-10-03T14:01:27.464Z","updated_at":"2025-10-14T05:31:33.699Z","avatar_url":"https://github.com/Tochemey.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# eGo\n\n[![build](https://img.shields.io/github/actions/workflow/status/Tochemey/ego/build.yml?branch=main)](https://github.com/Tochemey/ego/actions/workflows/build.yml)\n[![Go Reference](https://pkg.go.dev/badge/github.com/tochemey/ego.svg)](https://pkg.go.dev/github.com/tochemey/ego)\n[![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/tochemey/ego)](https://go.dev/doc/install)\n![GitHub Release](https://img.shields.io/github/v/release/Tochemey/ego)\n[![codecov](https://codecov.io/gh/Tochemey/ego/branch/main/graph/badge.svg?token=Z5b9gM6Mnt)](https://codecov.io/gh/Tochemey/ego)\n\neGo is a minimal library that helps build event-sourcing and CQRS applications through a simple interface, and it allows\ndevelopers to describe their **_commands_**, **_events_** and **_states_** _**are defined using google protocol buffers**_.\nUnder the hood, ego leverages [Go-Akt](https://github.com/Tochemey/goakt) to scale out and guarantee performant,\nreliable persistence.\n\n## Table of Content\n\n- [Installation](#-installation)\n- [Versioning](#-versioning)\n- [Features](#-features)\n    - [Event Sourced Behavior](#event-sourced-behavior)\n        - [Command Handlers](#command-handlers)\n        - [Event Handlers](#event-handlers)\n        - [Howto](#howto)\n        - [Events Stream](#events-stream)\n        - [Projection](#projection)\n        - [Events Store](#events-store)\n        - [Offset Store](#offsets-store)\n    - [Durable State Behavior](#durable-state-behavior)\n        - [State Recovery andPersistence](#state-recovery-and-persistence) \n        - [State Store](#state-store)\n        - [Howto](#howto-1)\n        - [State Stream](#events-stream-1)\n    - [Event Sourced vs. Durable State Behavior](#event-sourced-vs-durable-state-behavior)\n    - [Publisher APIs](#publishers)\n- [Cluster](#-cluster)\n- [Testkit](#-testkit)\n- [Mocks](#-mocks)\n- [Examples](#-examples)\n- [Contribution](#-contribution)\n\n## 💻 Installation\n\n```bash\ngo get github.com/tochemey/ego/v3\n```\n\n\u003e Note: eGo requires **Go 1.25.1** or higher.\n\n## 🔢 Versioning\n\nThe version system adopted in eGo deviates a bit from the standard semantic versioning system.\nThe version format is as follows:\n\n- The `MAJOR` part of the version will stay at `v3` for the meantime.\n- The `MINOR` part of the version will cater for any new _features_, _breaking changes_ with a note on the breaking\n  changes.\n- The `PATCH` part of the version will cater for dependency upgrades, bug fixes, security patches, and co.\n\nThe versioning will remain like `v3.x.x` until further notice. The current version is **`v3.5.3`**\n\n## ✨ Features\n\n### Event Sourced Behavior\n\nThe [`EventSourcedBehavior`](./behavior.go) is central to maintaining data consistency, particularly in distributed systems. \nIt defines how to handle commands—requests to perform actions—which are always directed at the event-sourced entity. \nIn eGo, `EventSourcedBehavior` instances are **serializable**, allowing them to be transported over the wire during relocation (e.g., in a clustered environment).\n\nCommands sent to an `EventSourcedBehavior` are processed **sequentially**. \nWhen a command is handled, it may produce one or more **events**, which are then persisted in an **event store**. \nEach persisted event is tagged with a **revision number** and a **timestamp**, enabling precise tracking and versioning.\n\nThe `EventSourcedBehavior` is also responsible for defining how these events are **applied** to update the internal state of the entity. \nThe ultimate goal of event handling is to **rebuild the current state** from a history of past events. \nWhen running in **cluster mode**, aggregate roots are automatically **sharded** for scalability and fault tolerance.\n\n#### Command Handlers\nCommand handlers define the business logic of the event-sourced actor. They are responsible for:\n\n - Validating incoming commands against the current state. \n - Deciding which events, if any, should be generated and persisted. \n - Returning nil for no-op operations when no state changes are needed.\n\nA command handler acts as the **gatekeeper** of your system’s business rules, ensuring that commands are only applied when valid. \nIf validation succeeds, one or more **events** are returned, which express the state mutations. These events are then persisted and applied to produce a **new, valid state**.\n\n#### Event Handlers\n\nEvent handlers define how the state should be updated in response to events. \nThese functions must be **pure and deterministic**, as they are used both when initially handling commands and when **replaying** the event log to reconstruct the entity’s state.\n\n#### Howto\n\nTo define an event-sourced entity, one needs to:\n\n1. define the state of the event-sourced entity using google protocol buffers message\n2. define the various commands that will be handled by the event-sourced entity\n3. define the various events that are a result of the command handlers and that will be handled by the event sourced\n   entity to return the new state of the event-sourced entity\n4. define and make sure the [`events store`](./persistence/events_store.go) is properly implemented.\n5. implement the [`EventSourcedBehavior`](./behavior.go) interface.\n6. call the `Entity` method of eGo [engine](./engine.go)\n\n#### Events Stream\n\nEvery event handled by an event-sourced entity is pushed to an events stream. That enables real-time processing of events\nwithout having to interact with the events store.\nJust use `Subscribe` method of [Engine](./engine.go) and start iterating through the messages and cast every message to\nthe [Event](./protos/ego/v3/ego.proto).\n\n#### Projection\n\nOne can add a projection to the eGo engine to help build a read model. Projections in eGo rely on an [offset store](#offsets-store) to\ntrack how far they have consumed events\npersisted by the write-model. The offset used in eGo is a _timestamp-based offset_. One can also:\n\n- remove a given projection: this will stop the projection and remove it from the system\n- check the status of a given projection\n\n#### Events Store\n\nOne can implement a custom events store. See [EventsStore](persistence/events_store.go).\nThere are some pre-built events stores one can use out of the box. See [Contrib](https://github.com/Tochemey/ego-contrib/tree/main/eventstore)\n\n#### Offsets Store\n\nOne can implement a custom offsets store. See [OffsetStore](./offsetstore/iface.go).\nThere are some pre-built offsets stores one can use out of the box. See [Contrib](https://github.com/Tochemey/ego-contrib/tree/main/offsetstore)\n\n### Durable State Behavior\n\nThe [`DurableStateBehavior`](./behavior.go) represents a type of actor that **persists its entire state** after processing each command—unlike event-sourced actors, which persist only the events. \nLike its event-sourced counterpart, DurableStateBehavior is **serializable**, meaning the actor can be moved across the network during relocation in distributed systems.\n\nThis actor maintains its current state **in memory** while handling commands. Based on the outcome of a command, it **persists the full**, **updated state** to a **durable store** (such as a SQL or NoSQL database). \nThe behavior follows a simple and predictable model:\n\n```\n (State, Command) =\u003e State\n```\n\nEach command results in a **new version** of the actor’s state. Only **the latest version** is stored—there is no retained event history. \nTherefore, `DurableStateBehavior` is suitable for use cases where **audit trails or state reconstruction** are not required.\n\nAlthough history is not tracked, each state version is tagged with a **version number**. \nThis version must **increment by one** with every successful state transition. It is the responsibility of the **command handler** to ensure that the new state has a version exactly one greater than the previous\n\n#### State Recovery and Persistence\n\n- Upon startup, `DurableStateBehavior` will attempt to **recover the last known state** from the durable store. \n- During a graceful shutdown, it **persists the current state** before stopping. \n- This ensures **consistency and resilience**, even in clustered or distributed deployments.\n\n`DurableStateBehavior` is ideal for scenarios where simplicity, low-overhead persistence, and state durability are required—without the complexity of full event sourcing.\n\n#### Durable Store\n\nOne can implement a custom state store. See [Durable Store](persistence/state_store.go).\nThere are some pre-built durable stores one can use out of the box. See [Contrib](https://github.com/Tochemey/ego-contrib/tree/main/durablestore)\n\n#### Howto\n\nTo define a durable state entity, one needs to:\n\n1. define the state of the entity using google protocol buffers message\n2. define the various commands that will be handled by the entity\n3. define and make sure the [`durable state store`](./persistence/state_store.go) is properly implemented.\n4. implements the [`DurableStateBehavior`](./behavior.go) interface\n5. start eGo engine with the option durable store using `WithStateStore`\n6. call the `DurableStateEntity` method of eGo [engine](./engine.go)\n\n### Event Sourced vs. Durable State Behavior\n\n| Aspect                   | `EventSourcedBehavior`                                        | `DurableStateBehavior`                                |\n|--------------------------|---------------------------------------------------------------|-------------------------------------------------------|\n| **Persistence Model**    | Persists events that describe state changes                   | Persists the full state after each command            |\n| **State Reconstruction** | Rebuilds state by replaying stored events                     | Loads the latest persisted state directly             |\n| **History Tracking**     | Full event history is retained                                | No event history, only latest state is kept           |\n| **Versioning**           | Revision number per event                                     | Version number per full state snapshot                |\n| **Command Handlers**     | Produces one or more events from each command                 | Produces a new state directly from each command       |\n| **Event Handlers**       | Required to evolve state based on events                      | Not required (no events are emitted)                  |\n| **Auditability**         | High – event log can be replayed for audit or debugging       | Low – only the final state is available               |\n| **Complexity**           | Higher – requires modeling both events and state evolution    | Lower – simpler, especially for CRUD-style operations |\n| **Storage**              | Typically event stores (e.g., Kafka, EventStoreDB)            | SQL, NoSQL, or any key-value store                    |\n| **Use Cases**            | Financial ledgers, domain-driven designs, traceable workflows | Caches, configuration entities, simple aggregates     |\n| **Recovery**             | Via event replay                                              | Via full state rehydration                            |\n| **Serialization**        | Serializable and relocatable                                  | Serializable and relocatable                          |\n\n\n#### Events Stream\n\n[`DurableStateBehavior`](./behavior.go) full state is pushed to an events stream.\nThat enables real-time processing of state without having to interact with the state store.\nJust use `Subscribe` method of [Engine](./engine.go) and start iterating through the messages and cast every message to\nthe [DurableState](./protos/ego/v3/ego.proto).\n\n### Publishers\n\neGo offers the following publisher APIs:\n\n* [EventPublisher](./publisher.go) - publishes `EventSourcedBehavior` events to any streaming platform\n* [StatePublisher](./publisher.go) - publishes `DurableStateBehavior` state to any streaming platform\n\nThe following streaming connectors are implemented out of the box:\n\n* [Kafka](https://github.com/Tochemey/ego/tree/publisher/kafka/v0.1.0)\n* [Pulsar](https://github.com/Tochemey/ego/tree/publisher/pulsar/v0.1.0)\n* [NATs](https://github.com/Tochemey/ego/tree/publisher/nats/v0.1.0)\n* [Websocket](https://github.com/Tochemey/ego/tree/publisher/websocket/v0.1.0)\n\n## 🌐 Cluster\n\nThe cluster mode heavily relies on [Go-Akt](https://github.com/Tochemey/goakt#clustering) clustering. To enable clustering one need to use `WithCluster` option\nwhen creating the eGo engine.\n\n## 🧪 Testkit\n\neGo comes bundle with in-memory datastore that can be found in the [testkit](./testkit) package. This can help play with eGo.\n\n## 🏗️ Mocks\n\neGo ships in some [mocks](./mocks) that can help mock the data stores for unit tests purpose.\n\n## 🎯 Examples\n\nCheck the [examples](./example)\n\n## 🤲 Contribution\n\nkindly follow the instructions in the [contribution doc](./contributing.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTochemey%2Fego","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FTochemey%2Fego","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTochemey%2Fego/lists"}