Skip to content

Commit

Permalink
docs: add sample code to readme
Browse files Browse the repository at this point in the history
  • Loading branch information
Tochemey committed Oct 7, 2023
1 parent 53d2c47 commit 36128c9
Showing 1 changed file with 139 additions and 5 deletions.
144 changes: 139 additions & 5 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# eGo
## eGo

[![build](https://img.shields.io/github/actions/workflow/status/Tochemey/ego/build.yml?branch=main)](https://github.com/Tochemey/ego/actions/workflows/build.yml)
[![codecov](https://codecov.io/gh/Tochemey/ego/branch/main/graph/badge.svg?token=Z5b9gM6Mnt)](https://codecov.io/gh/Tochemey/ego)
Expand All @@ -7,7 +7,7 @@
eGo is a minimal library that help build event-sourcing and CQRS application 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 [goakt](https://github.com/Tochemey/goakt) to scale out and guarantee performant, reliable persistence.

## Features
### Features

- Write Model:
- Commands handler: The command handlers define how to handle each incoming command,
Expand All @@ -26,13 +26,147 @@ Under the hood, ego leverages [goakt](https://github.com/Tochemey/goakt) to scal
- [Cluster Mode](https://github.com/Tochemey/goakt#clustering)
- Examples (check the [examples](./example))

## Installation
### Installation

```bash
go get github.com/tochemey/ego
```

## Contribution
### Sample

```go
package main

import (
"context"
"errors"
"log"
"os"
"os/signal"
"syscall"

"github.com/google/uuid"
"github.com/tochemey/ego"
"github.com/tochemey/ego/eventstore/memory"
samplepb "github.com/tochemey/ego/example/pbs/sample/pb/v1"
"google.golang.org/protobuf/proto"
)

func main() {
// create the go context
ctx := context.Background()
// create the event store
eventStore := memory.NewEventsStore()
// create the ego engine
e := ego.NewEngine("Sample", eventStore)
// start ego engine
_ = e.Start(ctx)
// create a persistence id
entityID := uuid.NewString()
// create an entity behavior with a given id
behavior := NewAccountBehavior(entityID)
// create an entity
entity, _ := ego.NewEntity[*samplepb.Account](ctx, behavior, e)

// send some commands to the pid
var command proto.Message
// create an account
command = &samplepb.CreateAccount{
AccountId: entityID,
AccountBalance: 500.00,
}
// send the command to the actor. Please don't ignore the error in production grid code
account, _, _ := entity.SendCommand(ctx, command)

log.Printf("current balance: %v", account.GetAccountBalance())

// send another command to credit the balance
command = &samplepb.CreditAccount{
AccountId: entityID,
Balance: 250,
}
account, _, _ = entity.SendCommand(ctx, command)
log.Printf("current balance: %v", account.GetAccountBalance())

// capture ctrl+c
interruptSignal := make(chan os.Signal, 1)
signal.Notify(interruptSignal, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
<-interruptSignal

// stop the actor system
_ = e.Stop(ctx)
os.Exit(0)
}

// AccountBehavior implements persistence.Behavior
type AccountBehavior struct {
id string
}

// make sure that AccountBehavior is a true persistence behavior
var _ ego.EntityBehavior[*samplepb.Account] = &AccountBehavior{}

// NewAccountBehavior creates an instance of AccountBehavior
func NewAccountBehavior(id string) *AccountBehavior {
return &AccountBehavior{id: id}
}

// ID returns the id
func (a *AccountBehavior) ID() string {
return a.id
}

// InitialState returns the initial state
func (a *AccountBehavior) InitialState() *samplepb.Account {
return new(samplepb.Account)
}

// HandleCommand handles every command that is sent to the persistent behavior
func (a *AccountBehavior) HandleCommand(_ context.Context, command ego.Command, _ *samplepb.Account) (event ego.Event, err error) {
switch cmd := command.(type) {
case *samplepb.CreateAccount:
// TODO in production grid app validate the command using the prior state
return &samplepb.AccountCreated{
AccountId: cmd.GetAccountId(),
AccountBalance: cmd.GetAccountBalance(),
}, nil

case *samplepb.CreditAccount:
// TODO in production grid app validate the command using the prior state
return &samplepb.AccountCredited{
AccountId: cmd.GetAccountId(),
AccountBalance: cmd.GetBalance(),
}, nil

default:
return nil, errors.New("unhandled command")
}
}

// HandleEvent handles every event emitted
func (a *AccountBehavior) HandleEvent(_ context.Context, event ego.Event, priorState *samplepb.Account) (state *samplepb.Account, err error) {
switch evt := event.(type) {
case *samplepb.AccountCreated:
return &samplepb.Account{
AccountId: evt.GetAccountId(),
AccountBalance: evt.GetAccountBalance(),
}, nil

case *samplepb.AccountCredited:
bal := priorState.GetAccountBalance() + evt.GetAccountBalance()
return &samplepb.Account{
AccountId: evt.GetAccountId(),
AccountBalance: bal,
}, nil

default:
return nil, errors.New("unhandled event")
}
}
```


### Contribution

Contributions are welcome!
The project adheres to [Semantic Versioning](https://semver.org) and [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
Expand All @@ -44,7 +178,7 @@ To contribute please:
- Create a feature branch
- Submit a [pull request](https://help.github.com/articles/using-pull-requests)

### Test & Linter
#### Test & Linter

Prior to submitting a [pull request](https://help.github.com/articles/using-pull-requests), please run:

Expand Down

0 comments on commit 36128c9

Please sign in to comment.