https://github.com/Tochemey/ego
[Go]Minimal event sourcing/CQRS library using protocol buffers for commands, events and states.
https://github.com/Tochemey/ego
cloud-computing concurrent-programming cqrs cqrs-es distributed-systems event-driven event-sourcing go golang microservice protocol-buffers reactive-programming
Last synced: 5 months ago
JSON representation
[Go]Minimal event sourcing/CQRS library using protocol buffers for commands, events and states.
- Host: GitHub
- URL: https://github.com/Tochemey/ego
- Owner: Tochemey
- License: mit
- Created: 2023-04-16T23:58:44.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2025-01-10T20:28:48.000Z (about 1 year ago)
- Last Synced: 2025-01-21T06:11:26.104Z (about 1 year ago)
- Topics: cloud-computing, concurrent-programming, cqrs, cqrs-es, distributed-systems, event-driven, event-sourcing, go, golang, microservice, protocol-buffers, reactive-programming
- Language: Go
- Homepage:
- Size: 567 KB
- Stars: 41
- Watchers: 3
- Forks: 5
- Open Issues: 2
-
Metadata Files:
- Readme: readme.md
- Contributing: contributing.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
- awesome-ccamel - Tochemey/ego - [Go]Minimal event sourcing/CQRS library using protocol buffers for commands, events and states. (Go)
README
# eGo
[](https://github.com/Tochemey/ego/actions/workflows/build.yml)
[](https://pkg.go.dev/github.com/tochemey/ego)
[](https://go.dev/doc/install)

[](https://codecov.io/gh/Tochemey/ego)
eGo is a minimal library that helps build event-sourcing and CQRS applications through a simple interface, and it allows
developers to describe their **_commands_**, **_events_** and **_states_** _**are defined using google protocol buffers**_.
Under the hood, ego leverages [Go-Akt](https://github.com/Tochemey/goakt) to scale out and guarantee performant,
reliable persistence.
## Table of Content
- [Installation](#-installation)
- [Versioning](#-versioning)
- [Features](#-features)
- [Event Sourced Behavior](#event-sourced-behavior)
- [Command Handlers](#command-handlers)
- [Event Handlers](#event-handlers)
- [Howto](#howto)
- [Events Stream](#events-stream)
- [Projection](#projection)
- [Events Store](#events-store)
- [Offset Store](#offsets-store)
- [Durable State Behavior](#durable-state-behavior)
- [State Recovery andPersistence](#state-recovery-and-persistence)
- [State Store](#state-store)
- [Howto](#howto-1)
- [State Stream](#events-stream-1)
- [Event Sourced vs. Durable State Behavior](#event-sourced-vs-durable-state-behavior)
- [Publisher APIs](#publishers)
- [Cluster](#-cluster)
- [Testkit](#-testkit)
- [Mocks](#-mocks)
- [Examples](#-examples)
- [Contribution](#-contribution)
## đť Installation
```bash
go get github.com/tochemey/ego/v3
```
> Note: eGo requires **Go 1.25.1** or higher.
## đ˘ Versioning
The version system adopted in eGo deviates a bit from the standard semantic versioning system.
The version format is as follows:
- The `MAJOR` part of the version will stay at `v3` for the meantime.
- The `MINOR` part of the version will cater for any new _features_, _breaking changes_ with a note on the breaking
changes.
- The `PATCH` part of the version will cater for dependency upgrades, bug fixes, security patches, and co.
The versioning will remain like `v3.x.x` until further notice. The current version is **`v3.5.3`**
## ⨠Features
### Event Sourced Behavior
The [`EventSourcedBehavior`](./behavior.go) is central to maintaining data consistency, particularly in distributed systems.
It defines how to handle commandsârequests to perform actionsâwhich are always directed at the event-sourced entity.
In eGo, `EventSourcedBehavior` instances are **serializable**, allowing them to be transported over the wire during relocation (e.g., in a clustered environment).
Commands sent to an `EventSourcedBehavior` are processed **sequentially**.
When a command is handled, it may produce one or more **events**, which are then persisted in an **event store**.
Each persisted event is tagged with a **revision number** and a **timestamp**, enabling precise tracking and versioning.
The `EventSourcedBehavior` is also responsible for defining how these events are **applied** to update the internal state of the entity.
The ultimate goal of event handling is to **rebuild the current state** from a history of past events.
When running in **cluster mode**, aggregate roots are automatically **sharded** for scalability and fault tolerance.
#### Command Handlers
Command handlers define the business logic of the event-sourced actor. They are responsible for:
- Validating incoming commands against the current state.
- Deciding which events, if any, should be generated and persisted.
- Returning nil for no-op operations when no state changes are needed.
A command handler acts as the **gatekeeper** of your systemâs business rules, ensuring that commands are only applied when valid.
If 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**.
#### Event Handlers
Event handlers define how the state should be updated in response to events.
These 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.
#### Howto
To define an event-sourced entity, one needs to:
1. define the state of the event-sourced entity using google protocol buffers message
2. define the various commands that will be handled by the event-sourced entity
3. define the various events that are a result of the command handlers and that will be handled by the event sourced
entity to return the new state of the event-sourced entity
4. define and make sure the [`events store`](./persistence/events_store.go) is properly implemented.
5. implement the [`EventSourcedBehavior`](./behavior.go) interface.
6. call the `Entity` method of eGo [engine](./engine.go)
#### Events Stream
Every event handled by an event-sourced entity is pushed to an events stream. That enables real-time processing of events
without having to interact with the events store.
Just use `Subscribe` method of [Engine](./engine.go) and start iterating through the messages and cast every message to
the [Event](./protos/ego/v3/ego.proto).
#### Projection
One 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
track how far they have consumed events
persisted by the write-model. The offset used in eGo is a _timestamp-based offset_. One can also:
- remove a given projection: this will stop the projection and remove it from the system
- check the status of a given projection
#### Events Store
One can implement a custom events store. See [EventsStore](persistence/events_store.go).
There are some pre-built events stores one can use out of the box. See [Contrib](https://github.com/Tochemey/ego-contrib/tree/main/eventstore)
#### Offsets Store
One can implement a custom offsets store. See [OffsetStore](./offsetstore/iface.go).
There are some pre-built offsets stores one can use out of the box. See [Contrib](https://github.com/Tochemey/ego-contrib/tree/main/offsetstore)
### Durable State Behavior
The [`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.
Like its event-sourced counterpart, DurableStateBehavior is **serializable**, meaning the actor can be moved across the network during relocation in distributed systems.
This 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).
The behavior follows a simple and predictable model:
```
(State, Command) => State
```
Each command results in a **new version** of the actorâs state. Only **the latest version** is storedâthere is no retained event history.
Therefore, `DurableStateBehavior` is suitable for use cases where **audit trails or state reconstruction** are not required.
Although history is not tracked, each state version is tagged with a **version number**.
This 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
#### State Recovery and Persistence
- Upon startup, `DurableStateBehavior` will attempt to **recover the last known state** from the durable store.
- During a graceful shutdown, it **persists the current state** before stopping.
- This ensures **consistency and resilience**, even in clustered or distributed deployments.
`DurableStateBehavior` is ideal for scenarios where simplicity, low-overhead persistence, and state durability are requiredâwithout the complexity of full event sourcing.
#### Durable Store
One can implement a custom state store. See [Durable Store](persistence/state_store.go).
There are some pre-built durable stores one can use out of the box. See [Contrib](https://github.com/Tochemey/ego-contrib/tree/main/durablestore)
#### Howto
To define a durable state entity, one needs to:
1. define the state of the entity using google protocol buffers message
2. define the various commands that will be handled by the entity
3. define and make sure the [`durable state store`](./persistence/state_store.go) is properly implemented.
4. implements the [`DurableStateBehavior`](./behavior.go) interface
5. start eGo engine with the option durable store using `WithStateStore`
6. call the `DurableStateEntity` method of eGo [engine](./engine.go)
### Event Sourced vs. Durable State Behavior
| Aspect | `EventSourcedBehavior` | `DurableStateBehavior` |
|--------------------------|---------------------------------------------------------------|-------------------------------------------------------|
| **Persistence Model** | Persists events that describe state changes | Persists the full state after each command |
| **State Reconstruction** | Rebuilds state by replaying stored events | Loads the latest persisted state directly |
| **History Tracking** | Full event history is retained | No event history, only latest state is kept |
| **Versioning** | Revision number per event | Version number per full state snapshot |
| **Command Handlers** | Produces one or more events from each command | Produces a new state directly from each command |
| **Event Handlers** | Required to evolve state based on events | Not required (no events are emitted) |
| **Auditability** | High â event log can be replayed for audit or debugging | Low â only the final state is available |
| **Complexity** | Higher â requires modeling both events and state evolution | Lower â simpler, especially for CRUD-style operations |
| **Storage** | Typically event stores (e.g., Kafka, EventStoreDB) | SQL, NoSQL, or any key-value store |
| **Use Cases** | Financial ledgers, domain-driven designs, traceable workflows | Caches, configuration entities, simple aggregates |
| **Recovery** | Via event replay | Via full state rehydration |
| **Serialization** | Serializable and relocatable | Serializable and relocatable |
#### Events Stream
[`DurableStateBehavior`](./behavior.go) full state is pushed to an events stream.
That enables real-time processing of state without having to interact with the state store.
Just use `Subscribe` method of [Engine](./engine.go) and start iterating through the messages and cast every message to
the [DurableState](./protos/ego/v3/ego.proto).
### Publishers
eGo offers the following publisher APIs:
* [EventPublisher](./publisher.go) - publishes `EventSourcedBehavior` events to any streaming platform
* [StatePublisher](./publisher.go) - publishes `DurableStateBehavior` state to any streaming platform
The following streaming connectors are implemented out of the box:
* [Kafka](https://github.com/Tochemey/ego/tree/publisher/kafka/v0.1.0)
* [Pulsar](https://github.com/Tochemey/ego/tree/publisher/pulsar/v0.1.0)
* [NATs](https://github.com/Tochemey/ego/tree/publisher/nats/v0.1.0)
* [Websocket](https://github.com/Tochemey/ego/tree/publisher/websocket/v0.1.0)
## đ Cluster
The cluster mode heavily relies on [Go-Akt](https://github.com/Tochemey/goakt#clustering) clustering. To enable clustering one need to use `WithCluster` option
when creating the eGo engine.
## đ§Ş Testkit
eGo comes bundle with in-memory datastore that can be found in the [testkit](./testkit) package. This can help play with eGo.
## đď¸ Mocks
eGo ships in some [mocks](./mocks) that can help mock the data stores for unit tests purpose.
## đŻ Examples
Check the [examples](./example)
## 𤲠Contribution
kindly follow the instructions in the [contribution doc](./contributing.md)