diff --git a/example/statestore/main.go b/example/statestore/main.go index 21dc37e..f48a30c 100644 --- a/example/statestore/main.go +++ b/example/statestore/main.go @@ -22,121 +22,124 @@ * SOFTWARE. */ - package main - - import ( - "context" - "errors" - "log" - "os" - "os/signal" - "syscall" - "time" - - "github.com/google/uuid" - "google.golang.org/protobuf/proto" - - "github.com/tochemey/ego/v3" - samplepb "github.com/tochemey/ego/v3/example/pbs/sample/pb/v1" - "github.com/tochemey/ego/v3/plugins/statestore/memory" - ) - - func main() { - // create the go context - ctx := context.Background() - // create the event store - durableStore := memory.NewStateStore() - // connect the event store - _ = durableStore.Connect(ctx) - // create the ego engine - engine := ego.NewEngine("Sample", nil, ego.WithStateStore(durableStore)) - // start ego engine - _ = engine.Start(ctx) - // create a persistence id - entityID := uuid.NewString() - // create an entity behavior with a given id - behavior := NewAccountBehavior(entityID) - // create an entity - _ = engine.DurableStateEntity(ctx, behavior) - - // 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 - reply, _, _ := engine.SendCommand(ctx, entityID, command, time.Minute) - account := reply.(*samplepb.Account) - log.Printf("current balance on opening: %v", account.GetAccountBalance()) - - // send another command to credit the balance - command = &samplepb.CreditAccount{ - AccountId: entityID, - Balance: 250, - } - - reply, _, _ = engine.SendCommand(ctx, entityID, command, time.Minute) - account = reply.(*samplepb.Account) - log.Printf("current balance after a credit of 250: %v", account.GetAccountBalance()) - - // capture ctrl+c - interruptSignal := make(chan os.Signal, 1) - signal.Notify(interruptSignal, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) - <-interruptSignal - - // disconnect the event store - _ = durableStore.Disconnect(ctx) - // stop the actor system - _ = engine.Stop(ctx) - os.Exit(0) - } - - // AccountBehavior implements EventSourcedBehavior - type AccountBehavior struct { - id string - } - - // make sure that AccountBehavior is a true persistence behavior - var _ ego.DurableStateBehavior = &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() ego.State { - return ego.State(new(samplepb.Account)) - } - - // HandleCommand handles every command that is sent to the persistent behavior - func (a *AccountBehavior) HandleCommand(_ context.Context, command ego.Command, priorVersion uint64, priorState ego.State) (event ego.State, newVersion uint64, err error) { - switch cmd := command.(type) { - case *samplepb.CreateAccount: - // TODO in production grid app validate the command using the prior state - return &samplepb.Account{ - AccountId: a.id, - AccountBalance: cmd.GetAccountBalance(), - }, priorVersion + 1, nil - - case *samplepb.CreditAccount: - // TODO in production grid app validate the command using the prior state - account := priorState.(*samplepb.Account) - bal := account.GetAccountBalance() + cmd.GetBalance() - - return &samplepb.Account{ - AccountId: a.id, - AccountBalance: bal, - }, priorVersion + 1, nil - - default: - return nil, 1, errors.New("unhandled command") - } - } \ No newline at end of file +package main + +import ( + "context" + "errors" + "log" + "os" + "os/signal" + "syscall" + "time" + + "github.com/google/uuid" + "google.golang.org/protobuf/proto" + + "github.com/tochemey/ego/v3" + samplepb "github.com/tochemey/ego/v3/example/pbs/sample/pb/v1" + "github.com/tochemey/ego/v3/plugins/statestore/memory" +) + +func main() { + // create the go context + ctx := context.Background() + // create the event store + durableStore := memory.NewStateStore() + // connect the event store + _ = durableStore.Connect(ctx) + // create the ego engine + engine := ego.NewEngine("Sample", nil, ego.WithStateStore(durableStore)) + // start ego engine + _ = engine.Start(ctx) + // create a persistence id + entityID := uuid.NewString() + // create an entity behavior with a given id + behavior := NewAccountBehavior(entityID) + // create an entity + _ = engine.DurableStateEntity(ctx, behavior) + + // 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 + reply, _, _ := engine.SendCommand(ctx, entityID, command, time.Minute) + account := reply.(*samplepb.Account) + log.Printf("current balance on opening: %v", account.GetAccountBalance()) + + // send another command to credit the balance + command = &samplepb.CreditAccount{ + AccountId: entityID, + Balance: 250, + } + + reply, _, _ = engine.SendCommand(ctx, entityID, command, time.Minute) + account = reply.(*samplepb.Account) + log.Printf("current balance after a credit of 250: %v", account.GetAccountBalance()) + + // capture ctrl+c + interruptSignal := make(chan os.Signal, 1) + signal.Notify(interruptSignal, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + <-interruptSignal + + // disconnect the event store + _ = durableStore.Disconnect(ctx) + // stop the actor system + _ = engine.Stop(ctx) + os.Exit(0) +} + +// AccountBehavior implements EventSourcedBehavior +type AccountBehavior struct { + id string +} + +// make sure that AccountBehavior is a true persistence behavior +var _ ego.DurableStateBehavior = &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() ego.State { + return ego.State(new(samplepb.Account)) +} + +// HandleCommand handles every command that is sent to the persistent behavior +func (a *AccountBehavior) HandleCommand(_ context.Context, command ego.Command, priorVersion uint64, priorState ego.State) (event ego.State, newVersion uint64, err error) { + switch cmd := command.(type) { + case *samplepb.CreateAccount: + // TODO in production grid app validate the command using the prior state + return &samplepb.Account{ + AccountId: a.id, + AccountBalance: cmd.GetAccountBalance(), + }, priorVersion + 1, nil + + case *samplepb.CreditAccount: + if cmd.GetAccountId() == a.id { + // TODO in production grid app validate the command using the prior state + account := priorState.(*samplepb.Account) + bal := account.GetAccountBalance() + cmd.GetBalance() + + return &samplepb.Account{ + AccountId: a.id, + AccountBalance: bal, + }, priorVersion + 1, nil + } + return nil, 0, errors.New("command sent to the wrong entity") + + default: + return nil, 0, errors.New("unhandled command") + } +}