GithubHelp home page GithubHelp logo

looplab / eventhorizon Goto Github PK

View Code? Open in Web Editor NEW
1.5K 47.0 195.0 2.34 MB

Event Sourcing for Go!

License: Apache License 2.0

Go 99.68% Makefile 0.32%
go cqrs ddd event-horizon event-sourcing domain-driven-design google-cloud aws eventsourcing entity

eventhorizon's People

Contributors

apterf avatar balboah avatar dependabot[bot] avatar dhogborg avatar dtravin avatar gjongenelen avatar gongdo avatar janhalfar avatar jefflinse avatar jensrantil avatar jkralik avatar johnroesler avatar jonwarghed avatar klowdo avatar marcwickenden avatar mavogel avatar maxbreida avatar maxekman avatar mbucc avatar mcarriere avatar protango avatar qzio avatar sdenham avatar semenovdl avatar superchalupa avatar terraskye avatar totemcaf avatar wr4thon avatar zdraganov avatar zoer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

eventhorizon's Issues

Projections

There should be an interface for projections, maybe like this:

type Projection interface{
    Project(Event, Model) (Model, error)
}

Event is the event to be projected and Model is a model struct created by a model factory with the latest DB model is unmarshaled into it (or a blank model if it doesn't yet exist in the DB). It returns a new model with the event projected onto it which will be saved to the DB. If the returned Model is nil it should be deleted.

There should also be a ProjectionManager or similar that maps events to projections and does the heavy lifting around each Projection.

[commandbus] Command bus scaling

Scaling out for command side would require sort of a sharding solution with a coordinator to load balance nodes properly. In AKKA it works based on a hashcode of the aggregate ID like this hash(id) mod NumberOfNodes.

[aggregatestore/events] aggregate version handling

I can't help to think the version handling in the event store is wrong. (using the mongodb implementation as example)
If I understand the code correctly it only makes sure that the aggregate has not changed from when it is read

    for _, event := range events {
        // Get an existing aggregate, if any.
        var existing []mongoAggregateRecord
        err := sess.DB(s.db).C("events").FindId(event.AggregateID().String()).
            Select(bson.M{"version": 1}).Limit(1).All(&existing)

until it is written

            // Increment record version before inserting.
            r.Version = existing[0].Version + 1

            // Increment aggregate version on insert of new event record, and
            // only insert if version of aggregate is matching (ie not changed
            // since the query above).
            err = sess.DB(s.db).C("events").Update(
                bson.M{
                    "_id":     event.AggregateID().String(),
                    "version": existing[0].Version,
                },
                bson.M{
                    "$push": bson.M{"events": r},
                    "$inc":  bson.M{"version": 1},
                },

The purpose of the version check should be to make sure that the event is applied (saved) to the correct version of the aggregate when the event was first created. That is, the aggregate version that should be validated is the one that was instantiated when the command is applied and the event(s) is created.

A solution would be to add a Version attribute to the eventhorizon.Event which holds the aggregate version which the event should be applied to. This can then be validated when storing the event.

Hope it makes sense, can be a bit hairy to explain :)

[eventstore] Research the use of encryption

Ideas:

  • Authenticate all events to prevent tampering.
  • Use a HMAC-like algorithm to break all future events after a tampered event.
  • Encrypt events so that only the owners of the events have access.

[projection] Projections are not durable

Imagine your projection is down and several commands reach to the command side and result into some events persisted into event store. Once the projection is up again, new events will not be collected.

Refactor Event to EventData and EventRecord to Event

This would make the event contain the metadata instead the event record, and the specific event data would be what is domain specific. This reduces a lot of extra code for domain events (for implementing the current Event interface) and also creates a better way to handle metadata that is the same for every event.

[projector] Projections are not consistent

The order of the events coming to projection is implementation dependent. There must be a guarantee that projection will receive eventN, eventN+1 etc consequently just as they appear on the event stream.

Why eventStore do not use plant event table?

As I see, Eventhorizon store the struct ( maybe I can call it aggregate table ) in the eventStore, likes: {aggregateId: string, events: [] object}, not the simple event table,like {aggregateId: string, event: object }.
This makes some events need to update the record of the eventStore, and I think this is not safe with race condition.
Why not just do add operation for the eventStore?
Maybe I am wrong. Appreciate for read and answer my question.

[api] Interface for ID instead of type

Hi !
Today we use snowflake as UUID, stored as uint64 and transmitted as string in base64.
Do you think is possible to change UUID to an interface? This way we'll be able to implement our custom UUID.
Please let me know what do you think, if you want I can help with this change

Namespaces

  • Set some value in the context, for example eh.WithNamespace().
  • Use different databases.
  • Use different event buses.

checkCommand returns missing field for boolean fields which is set to false

Hi I ran into a problem when trying to create a Todo API using your framework.

I'm trying to send the following command

type UpdateTodo struct {
    TodoID string
    Name   string
    Done   bool
}

id := eventhorizon.NewUUID()
cmd := UpdateTodo{TodoID: id, Name: "Todo 1", Done: false}

but in doing so it seems to complain about the field Done is missing and I think it is checkCommand method that is creating this problem for me.

here is demo of the issue as well https://play.golang.org/p/IRzcOGIAgS

Are you recommending sending boolean values in another way? this problmen probably holds true for integers as well. pretty much any default values.

Separate event publisher

There should be a subscriber/observer interface for instead of having this mixed with the EventBus interface. The Redis and GCP implementations would be implementations of the new interface instead of the EventBus.

Document how to run tests in README (or other document)

Discovered this project. Looked interesting. Checked out the code, but can't seem to get the tests to run. Partially based on wercker.yml:

➜  eventhorizon git:(master) go get golang.org/x/tools/cmd/cover
➜  eventhorizon git:(master) go get github.com/axw/gocov/gocov
➜  eventhorizon git:(master) go get github.com/mattn/goveralls
➜  eventhorizon git:(master) go build ./...
testing/checkers.go:20:2: cannot find package "gopkg.in/check.v1" in any of:
    /usr/local/homebrew/Cellar/go/1.2/libexec/src/pkg/gopkg.in/check.v1 (from $GOROOT)
    /Users/jens/Development/src/external_projects/eventhorizon/src/gopkg.in/check.v1 (from $GOPATH)
➜  eventhorizon git:(master)

[projector] Handle events in version order

When a burst of events is handled by a projector of the same model/aggregate id, all events will now wait for the correct minversion of the model at the same time. This causes the events to have almost the same backoff delay and will time out events with higher minversion waits easily.
This also causes unnecessary db lookups for higher minversion events.

A proposed solution:
Write a middleware or a type of event handler that can have an internal sorted queue for all incoming events (fan-in -> single out). This fan-in event handler should collect all events for all events that goes to the same model id and namespace and then always handle the event with the least minversion synchronously before continuing to the next event the least minversion.

Move creation of aggregates into EventStore

Instead of:

func (d *DelegateDispatcher) handleCommand(handlerType reflect.Type, command Command) error {
    // Create aggregate from its type
    aggregate := d.createAggregate(command.AggregateID(), handlerType)

    // Load aggregate events
    events, _ := d.eventStore.Load(aggregate.AggregateID())
    aggregate.ApplyEvents(events)

    ...

it will look like this:

func (d *DelegateDispatcher) handleCommand(handlerType reflect.Type, command Command) error {
    // Load aggregate
    aggregate, err := d.eventStore.LoadAggregate(aggregate.AggregateID())

    ...

Handler registration question

Seems like registering handlers by event type could be fragile. I'm thinking of the case when you add an attribute to an event and then you want to replay old events in the store.

Do you see any benefits to using the object type as the key over a simple event name + version number combination? If you used (event_name,version) then you could keep old and new handlers registered and you wouldn't have to change the event type. (Also, from what I have read reflection is really slow in golang.)

I wonder how the .NET guys handle changes in event and command attributes.

Use EventMatcher when registering event handlers

An example:

// EventBus is an interface defining an event bus for distributing events.
type EventBus interface {
	...

	// AddHandler adds a handler for an event.
	AddHandler(EventHandler, EventMatcher)

	...
}
// EventMatcher is used to match events, used when adding event handlers.
type EventMatcher interface {
	MatchesEvent(EventType) bool
}

// AnyEvent is an EventMatcher that matches any event.
type AnyEvent struct{}

// MatchesEvent imlpements the MatchesEvent method of the EventMatcher interface.
func (m AnyEvent) MatchesEvent(e EventType) bool {
	return true
}

// OneEvent is an EventMatcher that matches a single event.
type OneEvent struct {
	Event EventType
}

// MatchesEvent imlpements the MatchesEvent method of the EventMatcher interface.
func (m OneEvent) MatchesEvent(e EventType) bool {
	return m.Event == e
}

// ManyEvents is an EventMatcher that matches if the event is in the slice.
type ManyEvents []EventType

// MatchesEvent imlpements the MatchesEvent method of the EventMatcher interface.
func (m OneEvent) MatchesEvent(e EventType) bool {
	// return true if e is in the slice.
}

Read model version support

Add initial support for fetching a min versions of models in the read model. This is to be able to fetch a model with a version after a certain event has been applied.

[api] Let command handlers publish what they can handle

This would be used to automatically add the handler for the commands that they can handle instead or SetHandler multiple times.

The CommandBus would then get a AddHandler instead instead of a SetHandler because of the new semantics.

Sagas

Sagas should be implemented as an EventHandler.

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.