Giter VIP home page Giter VIP logo

go-cqrs's Introduction

Go-CQRS

wercker status

This is an experimental library that tries to bring the concepts from CQRS to Go. It currently focusses on adding event sourcing. Event sourcing ensures that all changes to the application state are stored as a sequence of events. Not just can we query these events, we can also use these events to reconstruct past and current state.

Event sourcing example

Here are the examples of the main concepts.

Changes to an object are captured by events

// Create a new domain object
user := domain.NewUser("pjvds")
c.Assert(user.Username, Equals, "pjvds")

// We created a new user, and this is
// represented by an event.
c.Assert(len(user.Events()), Equals, 1)

// Change the username of the user
user.ChangeUsername("wwwouter")
c.Assert(user.Username, Equals, "wwwouter")

// We changed the username, and this is
// also represented by an event.
c.Assert(len(user.Events()), Equals, 2)

An object state can be rebuild from history

// The id of our event source that we will rebuild from history.
sourceId, _ := sourcing.ParseEventSourceId("0791d279-664d-458e-bf60-567ade140832")

// The full history for the User domain object
history := []sourcing.Event{
    // It was first created
    events.UserCreated{
        Username: "pjvds",
    },
    // Then the username was changed
    events.UsernameChanged{
        OldUsername: "pjvds",
        NewUsername: "wwwouter",
    },
}

// Create a new User domain object from history
user := domain.NewUserFromHistory(sourceId, history)

// It should not have the initial state.
c.Assert(user.Username, Not(Equals), "pjvds")

// It should have the latest state.
c.Assert(user.Username, Equals, "wwwouter")

Sourcing object

An sourcing object has three main concepts: state, command methods and event handlers.

State

An objects starts with a struct that holds the state of an object. In this case a simple User object that has a single field Username. Not the comment that we will never update this username directly.

// Holds the state of our user. Note that
// state like Username is not updated directly!
type User struct {
    // Embed event source funtionality.
    sourcing.EventSource

    Username string
}

Ctor

The User has two constructor methods. One to create a new User and one that creates a User based on history. The first creates a User and applies the fact that this happend. The later creates a User and replays history to build the state.

// Creates an new User object.
func NewUser(username string) *User {
    // Create a new user object
    user := new(User)
    user.EventSource = sourcing.CreateNew(user)

    // Apply the fact that the user was created.
    user.Apply(events.UserCreated{
        Username: username,
    })

    return user
}

// Creates an new User object and builds the state from the history.
func NewUserFromHistory(history []sourcing.EventEnvelope) *User {
    var user = new(User)
    user.EventSource = sourcing.AttachFromHistory(user, history)

    return user
}

Command method

The User has only one exported fields called Username. The state of an User object, or any other sourced object, should never be updated directly. Rather they are updated via methods that capture the intention of what should be done. These methods are called command methods. These methods validate whether the state change can happen, and if so, they create an event that represents this change and the event is applied. This means the event is recorded and send to the event handler on the object itself where it will update it's internal state.

// Change the username to a new name.
func (user *User) ChangeUsername(username string) error {
    // Validate username
    if lenght := len(username); lenght < 3 || lenght > 20 {
        return errors.New("invalid username lenght")
    }

    // Raise the fact that the username is changed.
    user.sourcer.Apply(events.UsernameChanged{
        OldUsername: user.Username,
        NewUsername: username,
    })

    return nil
}

Event handler

The user.Apply() call registers that the event is happened and calls an event handling method on the User object that updates the state occordingly.

// Update the User state for an UserCreated event.
func (user *User) HandleUserCreated(e events.UserCreated) {
    user.Username = e.Username
}

// Update the User state for an UsernameChanged event.
func (user *User) HandleUsernameChanged(e events.UsernameChanged) {
    user.Username = e.NewUsername
}

See the User.go source to see all the details.

Bitdeli Badge

go-cqrs's People

Contributors

bitdeli-chef avatar jcutrono avatar pjvds avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.