https://github.com/entrlcom/go-event-sourcing
Event Sourcing in Go/Golang
https://github.com/entrlcom/go-event-sourcing
cqrs ddd esdb event-driven event-sourcing eventstore eventstoredb go go-lib go-library golang golang-lib golang-library
Last synced: 10 months ago
JSON representation
Event Sourcing in Go/Golang
- Host: GitHub
- URL: https://github.com/entrlcom/go-event-sourcing
- Owner: entrlcom
- License: mit
- Created: 2024-08-01T08:03:48.000Z (almost 2 years ago)
- Default Branch: main
- Last Pushed: 2024-09-01T08:05:45.000Z (almost 2 years ago)
- Last Synced: 2025-01-13T04:29:30.495Z (over 1 year ago)
- Topics: cqrs, ddd, esdb, event-driven, event-sourcing, eventstore, eventstoredb, go, go-lib, go-library, golang, golang-lib, golang-library
- Language: Go
- Homepage:
- Size: 18.6 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Event Sourcing
## Table of Content
- [Examples](#examples)
- [License](#license)
## Examples
```go
package main
import (
"context"
"encoding/json"
"errors"
"time"
"github.com/EventStore/EventStore-Client-Go/v4/esdb"
"github.com/google/uuid"
aggregate_model "entrlcom.dev/event-sourcing/domain/model/aggregate"
aggregate_id_model "entrlcom.dev/event-sourcing/domain/model/aggregate/id"
event_model "entrlcom.dev/event-sourcing/domain/model/event"
eventstore_repository "entrlcom.dev/event-sourcing/infra/repository/aggregate/eventstore"
)
func main() {
ctx := context.Background()
// EventStore configuration.
eventStoreDBConfiguration, err := esdb.ParseConnectionString("esdb://localhost:2113/?tls=false")
if err != nil {
return
}
// EventStore client.
eventStoreDBClient, err := esdb.NewClient(eventStoreDBConfiguration)
if err != nil {
return
}
// Repository.
repository := eventstore_repository.NewEventStoreAggregateRepository(*eventStoreDBClient)
// New account (aggregate).
account, err := NewAccount(NewAccountID())
if err != nil {
return
}
// Load account (aggregate).
if err = repository.Load(ctx, account._aggregate); err != nil {
if !errors.Is(err, aggregate_model.ErrAggregateNotFound) {
return
}
}
if err = account.ChangeName("A"); err != nil {
return
}
if err = account.ChangeName("B"); err != nil {
return
}
// Save account (aggregate).
if err = repository.Save(ctx, account._aggregate); err != nil {
return
}
if err = account.ChangeName("C"); err != nil {
return
}
if err = account.ChangeName("D"); err != nil {
return
}
// Save account (aggregate).
if err = repository.Save(ctx, account._aggregate); err != nil {
return
}
}
var ErrInvalidAccount = errors.New("invalid account")
const AggregateType = "Account"
type Account struct {
_aggregate *aggregate_model.BaseAggregate
id AccountID
name string
}
func (x *Account) ChangeName(name string) error {
event := NewAccountNameChangedEvent(x.id.String(), name)
data, _ := json.Marshal(event)
e, err := event_model.NewBaseEvent(
AccountNameChangedEventType,
event_model.WithAggregate(
x._aggregate.GetID(),
x._aggregate.GetType(),
x._aggregate.GetVersion(),
),
event_model.WithData(data),
)
if err != nil {
return err
}
if err = x._aggregate.ApplyEvent(&e); err != nil {
return err
}
return nil
}
func NewAccount(id AccountID) (*Account, error) {
x := Account{
_aggregate: nil,
id: id,
name: "",
}
aggregateID, err := aggregate_id_model.NewAggregateIDFromString(id.String())
if err != nil {
return nil, errors.Join(err, ErrInvalidAccount)
}
aggregate, err := aggregate_model.NewBaseAggregate(
aggregateID,
AggregateType,
aggregate_model.WithWhen(func(event event_model.Event) error {
switch event.GetType() {
case AccountNameChangedEventType:
var data AccountNameChangedEvent
if err = json.Unmarshal(event.GetData(), &data); err != nil {
return err
}
x.name = data.Name
return nil
default:
return nil
}
}),
)
if err != nil {
return nil, errors.Join(err, ErrInvalidAccount)
}
x._aggregate = &aggregate
return &x, nil
}
var ErrInvalidAccountID = errors.New("invalid account id")
type AccountID string
func (x AccountID) IsEqualTo(accountID AccountID) bool {
return x == accountID
}
func (x AccountID) String() string {
return string(x)
}
func (x AccountID) Validate() error {
v, err := uuid.Parse(string(x))
if err != nil || v.Variant() != uuid.RFC4122 || v.Version() != 7 {
return ErrInvalidAccountID
}
return nil
}
func NewAccountID() AccountID {
return AccountID(uuid.Must(uuid.NewV7()).String())
}
const AccountNameChangedEventType = "AccountNameChanged"
type AccountNameChangedEvent struct {
AccountID string `json:"account_id,omitempty"`
Name string `json:"name,omitempty"`
}
func NewAccountNameChangedEvent(accountID, name string) AccountNameChangedEvent {
return AccountNameChangedEvent{
AccountID: accountID,
Name: name,
}
}
```
## License
[MIT](https://choosealicense.com/licenses/mit/)