CQRS & Event-Sourcing Framework for Go.

Overview

goes - Event-Sourcing Framework

Go Reference Test

goes is a collection of interfaces, tools, and backend implementations that allow you to write event-sourced applicatios in Go.

If you have any questions or feedback, feel free to open an issue or start a discussion.

Getting Started

Installation

goes is not yet versioned because the API still changes too often. Install using a specific commit hash and make sure to install nested modules with /...

go get github.com/modernice/goes/[email protected]<commit-hash>

Examples

Introduction

This documentation assumes knowledge of CQRS, event-sourcing, and other related concepts. Please make yourself familiar with these before reading further.

Features

Components

goes provides incrementally adoptable components that together form a complete framework for building event-sourced applications. Read a component's README for a guide on how to use it.

Backends

Event Bus

Event Store

Contributing

TBD

License

Apache License, Version 2.0

Issues
  • Review projection APIs

    Review projection APIs

    This issue will determine if the different projection APIs are even necessary to solve common problems or if there are better solutions to these problems that don't require these APIs.

    HistoryDependent

    The HistoryDependent API is used by projection jobs to determine which events a projection needs to properly update itself. If a projection implements HistoryDependent, it can hint to a projection job that it requires the full history of the events that are configured in the job, instead of just the (published) events that triggered the job.

    ProgressAware

    The ProgressAware API is usually used by persistent projections that are stored in a database. When a projection job is triggered, the application fetches the current projection state from the database, applies the events on it and saves it back to the database. A ProgressAware projection keeps track of the last applied event in terms of the event time, to ensure that no event is applied twice to the projection.

    opened by bounoable 3
  • Automated lookups

    Automated lookups

    package user
    
    const Aggregate = "user"
    const NameLookup = "name"
    const EmailLookup = "email"
    
    const CreatedEvent = "user.created"
    
    type CreatedData struct {
      Name string
      Email string
    }
    
    func (evt Created) ProvideLookup(p lookup.Provider)  {
      p.Provide(Aggregate, NameLookup, evt.Name)
      p.Provide(Aggregate, EmailLookup, evt.Email)
    }
    
    // somewhere else
    
    var ctx context.Context
    
    var store event.Store
    l := lookup.New(store, Created) // create lookup from events in event store
    
    errs, err := l.Run(ctx) // run the lookup projection
    
    var aggregateID uuid.UUID
    
    userID, err := l.Lookup(ctx, Aggregate, EmailLookup, "[email protected]")
    
    • only allows string values or use interface{}?
    • reverse lookups?
    opened by bounoable 2
  • Projection debounce wait cap

    Projection debounce wait cap

    Problem

    Currently, the Debounce(time.Duration) option of the continuous schedule has no cap on the total wait time before a projection job is triggered. Application that continuously publish a lot of events could cause infinite buffering of projection events.

    Proposal

    Add a DebounceCap(time.Duration) option that specifies the wait cap. Use a sensible duration as the default (5s maybe?).

    opened by bounoable 1
  • Add `aggregate.IsConsistencyError`

    Add `aggregate.IsConsistencyError`

    MongoDB event store implements its own VersionError and a IsConsistencyError() function. Aggregates that implement repository.Retryer must use the MongoDB-specific function if the MongoDB event store is used. There should be a single aggregate.IsConsistencyError function to check for consistency errors. Implementation-specific errors can then provide a function to tell the aggregate.IsConsistencyError() function if the error is a consistency error.

    package mongo
    
    type VersionError struct { ... }
    
    func (err VersionError) IsConsistencyError() bool {
      return true
    }
    
    package example
    
    func example() {
      var err mongo.VersionError
      aggregate.IsConsistencyError(err) == true
    
      var err aggregate.ConsistencyError
      aggregate.IsConsistencyError(err) == true
    }
    
    opened by bounoable 1
  • Human-readable command errors

    Human-readable command errors

    Provide a method to return a human-readable error from a command handler back to the command dispatcher when using the dispatch.Synchronous option.

    Command handler in some service:

    package example
    
    func handleCommands(ctx context.Context, bus command.Bus) <-chan error {
        h := command.NewHandler()
    
        return h.MustHandle(ctx, "foo-cmd", func(ctx command.Context) error {
            cmd := ctx.Command()
    
            err := errors.New("mock error") // some error returned by domain layer
    
            return command.Fail(err, "Something went wrong while handling %q command.", cmd.Name())
        })
    }
    
    func main() {
        var bus command.Bus
    
        errs := handleCommands(context.TODO(), bus)
    
        for err := range errs {
            var cmdErr *command.Error
            errors.As(err, &cmdErr) == true
    
            err.Error() == `Something went wrong while handling "foo-cmd" command.`
            cmdErr.Unwrap().Error() == "mock error"
        }
    }
    

    Command dispatcher in another service:

    package example
    
    func dispatchCommand(ctx context.Context, bus command.Bus) {
        cmd := command.New("foo-cmd", ...)
    
        err := bus.Dispatch(ctx, cmd, dispatch.Synchronous())
    
        err.Error() == `Something went wrong while handling "foo-cmd" command.`
    
        var cmdErr *command.Error
        errors.As(err, &cmdErr) == true
    
        cmdErr.Unwrap().Error() == "mock error"
    }
    
    opened by bounoable 1
  • chore(deps): bump github.com/jackc/pgx/v4 from 4.16.1 to 4.17.0

    chore(deps): bump github.com/jackc/pgx/v4 from 4.16.1 to 4.17.0

    Bumps github.com/jackc/pgx/v4 from 4.16.1 to 4.17.0.

    Changelog

    Sourced from github.com/jackc/pgx/v4's changelog.

    4.17.0 (August 6, 2022)

    • Upgrade pgconn to v1.13.0
    • Upgrade pgproto3 to v2.3.1
    • Upgrade pgtype to v1.12.0
    • Allow background pool connections to continue even if cause is canceled (James Hartig)
    • Add LoggerFunc (Gabor Szabad)
    • pgxpool: health check should avoid going below minConns (James Hartig)
    • Add pgxpool.Conn.Hijack()
    • Logging improvements (Stepan Rabotkin)
    Commits
    • 5768a0c Update changelog
    • 7ce634d Ensure there is a timeout for background pool connections
    • f3e04b2 Go 1.19 go fmt
    • 7ad36f3 Upgrade dependencies
    • 3cb9953 pgxpool: Make BeginTx success case clearer
    • 91c9e84 Ignore cancellation in puddle constructor
    • 88079de Update issue templates
    • cb5ddcd Update issue templates
    • 3961954 Add logger func wrapper
    • a814153 pgxpool: health check should avoid going below minConns
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 0
  • chore(deps): bump go.mongodb.org/mongo-driver from 1.10.0 to 1.10.1

    chore(deps): bump go.mongodb.org/mongo-driver from 1.10.0 to 1.10.1

    Bumps go.mongodb.org/mongo-driver from 1.10.0 to 1.10.1.

    Commits
    • 9c62b3f Update version to v1.10.1
    • 06579a7 GODRIVER-2489 createPipelineOptionsDoc: return err (#1036)
    • bdfc953 update for libmongocrypt 1.5.2 (#1037)
    • 51ecabd GODRIVER-2489 Return error if createPipelineOptionsDoc() fails (#1035)
    • e6f6f26 GODRIVER-2494 Skip StaleShardVersion resume test on 6.1+. (#1030)
    • e095f2d GODRIVER-2495 Undeprecate legacy timeouts. (#1026)
    • 18da11c Update version to v1.10.1-prerelease
    • 5d004cc Update version to v1.11.0-prerelease
    • See full diff in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 0
  • chore(deps): bump google.golang.org/protobuf from 1.28.0 to 1.28.1

    chore(deps): bump google.golang.org/protobuf from 1.28.0 to 1.28.1

    Bumps google.golang.org/protobuf from 1.28.0 to 1.28.1.

    Release notes

    Sourced from google.golang.org/protobuf's releases.

    v1.28.1

    This release contains protoc-gen-go binaries for arm64.

    Notable changes since v1.28.0:

    • CL/418677: internal/impl: improve MessageInfo.New performance
    • CL/411377: proto: short-circuit Equal when inputs are identical
    • CL/419714: all: Add prebuild binaries for arm64
    Commits
    • 6875c3d all: release v1.28.1
    • 881da6e all: Add prebuild binaries for arm64
    • 2a74a0e A+C: delete AUTHORS and CONTRIBUTORS
    • de9682a internal/impl: improve MessageInfo.New performance
    • b0a9446 all: reformat with go1.19 gofmt
    • c1bbc5d all: make integration test work on darwin/arm64
    • 5f429f7 proto: fix compilation failure in tests
    • fc44d00 proto: use reflect.Ptr for backward compatibility
    • 380c339 proto: short-circuit Equal when inputs are identical
    • 784c482 all: remove shorthand import aliases
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 0
  • chore(deps): bump go.mongodb.org/mongo-driver from 1.9.1 to 1.10.0

    chore(deps): bump go.mongodb.org/mongo-driver from 1.9.1 to 1.10.0

    Bumps go.mongodb.org/mongo-driver from 1.9.1 to 1.10.0.

    Release notes

    Sourced from go.mongodb.org/mongo-driver's releases.

    MongoDB Go Driver 1.10.0

    The MongoDB Go Driver Team is pleased to release version 1.10.0 of the official Go driver.

    Release Notes

    This release supports several new features introduced in MongoDB v6.0, including the following notable changes.

    Queryable Encryption Support

    This release includes new options to AutoEncryptionOpts and EncryptOpts to support Queryable Encryption. Queryable Encryption support requires MongoDB server 6.0 or newer, and libmongocrypt 1.5.0 or newer.

    Automatic Encryption Shared Library

    Add support for the new encryption helper, crypt_shared, referred to as the Shared Library. The shared library replacesmongocryptd and does not require spawning a new process.

    Key Management API Operations

    Add ClientEncryption entity operations for Key Management API with the purpose of

    • creating data keys
    • rewrapping data keys
    • deleting data keys
    • adding and removing alternative names to data keys
    • getting data keys

    Patch for Decoding Empty Types

    A patch is implemented for defaulting data to either primitive.M or primitive.D when decoding empty types.

    Encoding Atypical Map Key Types

    Allow encoding atypical map key types for data that can unmarshal into a textual representation of itself, i.e. TextMarshaler and TextUnmarshaler.

    Improvement of Full Document Request

    Add the ability to request the full document both before and after an update in Change Stream events.

    PRN and UUID Generation Refactoring

    Refactor the pseudo-random number and UUID generation to improve performance and reduce conflict.

    Reducing memory consumption when compressing wire messages

    Refactor the Zstd compression to reduce memory consumption.

    Provisional API for Timeout

    The new Timeout client option can be used to set a default context timeout for every operation sent through that client. SetTimeout represents unstable, provisional API; the behavior of the driver when a Timeout is specified is subject to change.

    Troubleshooting Documentation

    The Go Driver team will start supporting a docs/common-issues.md file for troubleshooting frequently encountered issues.


    For a full list of tickets included in this release, please see the links below:

    ... (truncated)

    Commits
    • 9bbe96c GODRIVER-2464 Add delay in RTT monitor test so Windows can measure a non-zero...
    • 3b7e3eb Update version to v1.10.0
    • 03207bb GODRIVER-2287 Add section to troubleshooting FAQ per driver with top SEO resu...
    • 063989d Use correct variable name for Python path in AWS auth tests. (#1019)
    • d86e0aa GODRIVER-2464 Add timeout for RTT monitor hello operations. (#994)
    • 6f2489e GODRIVER-2480 create branching for windows KMS venv (#1010)
    • b659a83 GODRIVER-449 Correctly unescape usernames and passwords in connection strings...
    • d6ab9dd GODRIVER-2449 document Queryable Encryption API as Public Technical Preview (...
    • 68ea5c1 GODRIVER-2465 update test session to correctly use explicit session (#1013)
    • 5259d80 GODRIVER-2473 Remove ClientEncryption.createKey() in favor of createDataKey()...
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 0
  • chore(deps): bump google.golang.org/grpc from 1.47.0 to 1.48.0

    chore(deps): bump google.golang.org/grpc from 1.47.0 to 1.48.0

    Bumps google.golang.org/grpc from 1.47.0 to 1.48.0.

    Release notes

    Sourced from google.golang.org/grpc's releases.

    Release 1.48.0

    Bug Fixes

    • xds/priority: fix bug that could prevent higher priorities from receiving config updates (#5417)
    • RLS load balancer: don't propagate the status code returned on control plane RPCs to data plane RPCs (#5400)

    New Features

    • stats: add support for multiple stats handlers in a single client or server (#5347)
    • gcp/observability: add experimental OpenCensus tracing/metrics support (#5372)
    • xds: enable aggregate and logical DNS clusters by default (#5380)
    • credentials/google (for xds): support xdstp C2P cluster names (#5399)
    Commits
    • 6417495 Change version to 1.48.0 (#5482)
    • 5770b1d xds: drop localities with zero weight at the xdsClient layer (#5476)
    • 423cd8e interop: update proto to make vet happy (#5475)
    • c9b16c8 transport: remove unused bufWriter.onFlush() (#5464)
    • 755bf5a fix typo in the binary log (#5467)
    • 15739b5 health: split imports into healthpb and healthgrpc (#5466)
    • c075d20 interop client: provide new flag, --soak_min_time_ms_between_rpcs (#5421)
    • 4b75005 clusterresolver: merge P(p)arseConfig functions (#5462)
    • d883f3d test/xds: fail only when state changes to something other than READY and IDLE...
    • c6ee1c7 xdsclient: only include nodeID in error strings, not the whole nodeProto (#5461)
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 0
  • chore(deps): bump github.com/spf13/cobra from 1.4.0 to 1.5.0

    chore(deps): bump github.com/spf13/cobra from 1.4.0 to 1.5.0

    Bumps github.com/spf13/cobra from 1.4.0 to 1.5.0.

    Release notes

    Sourced from github.com/spf13/cobra's releases.

    v1.5.0

    Spring 2022 Release 🌥️

    Hello everyone! Welcome to another release of cobra. Completions continue to get better and better. This release adds a few really cool new features. We also continue to patch versions of our dependencies as they become available via dependabot. Happy coding!

    Active help 👐🏼

    Shout out to @​marckhouzam for a big value add: Active Help spf13/cobra#1482. With active help, a program can provide some inline warnings or hints for users as they hit tab. Now, your CLIs can be even more intuitive to use!

    Currently active help is only supported for bash V2 and zsh. Marc wrote a whole guide on how to do this, so make sure to give it a good read to learn how you can add this to your cobra code! https://github.com/spf13/cobra/blob/master/active_help.md

    Group flags 🧑🏼‍🤝‍🧑🏼

    Cobra now has the ability to mark flags as required or exclusive as a group. Shout out to our newest maintainer @​johnSchnake for this! spf13/cobra#1654 Let's say you have a username flag that MUST be partnered with a password flag. Well, now, you can enforce those as being required together:

    rootCmd.Flags().StringVarP(&u, "username", "u", "", "Username (required if password is set)")
    rootCmd.Flags().StringVarP(&pw, "password", "p", "", "Password (required if username is set)")
    rootCmd.MarkFlagsRequiredTogether("username", "password")
    

    Flags may also be marked as "mutally exclusive" with the MarkFlagsMutuallyExclusive(string, string ... ) command API. Refer to our user guide documentation for further info!

    Completions 👀

    Documentation 📝

    ... (truncated)

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 0
  • Query Cache

    Query Cache

    Implement a flexible cache for event queries.

    Features

    • Automatic cache key based on the query filters
    • Time-based and manual expiry
    • Optional cache storage

    Examples

    Basic usage

    package example
    
    import (
      "github.com/modernice/goes/event"
      "github.com/modernice/goes/event/query"
    )
    
    func example(store event.Store, q event.Query) {
      cache := query.NewCache(store)
    
      result, err := cache.Result(context.TODO(), q)
      // handle err
    
      if result.Cached {
        log.Println("cache returned cached result")
      } else {
        log.Println("cache executed query")
      }
    
      events, err := streams.Drain(context.TODO(), result.Events, result.Errs)
      // handle err
    
      // alternatively
    
      str, errs, err := cache.Run(context.TODO(), q)
      // handle err
    
      events, err := streams.Drain(context.TODO(), str, errs)
    }
    
    opened by bounoable 1
  • [contrib/auth] Add `QueryClient.Roles()` method

    [contrib/auth] Add `QueryClient.Roles()` method

    Allow querying of user roles:

    package example
    
    func example(client auth.QueryClient) {
      userID := uuid.New()
      roleIDs, roleNames, err := client.RolesOf(context.TODO(), userID)
    }
    
    enhancement 
    opened by bounoable 0
  • Projection finalizers

    Projection finalizers

    Currently, projection finalization is implemented like this:

    package example
    
    type Foo struct { ... }
    
    func (*Foo) ApplyEvent(event.Event) {}
    
    func (f *Foo) finalize(ctx context.Context, dep SomeDependency) error {
      // do stuff
      if err := dep.Do(ctx, "..."); err != nil {
        return err
      }
      // do more stuff
      return nil
    }
    
    func example(s projection.Schedule) {
      var dep SomeDependency
    
      s.Subscribe(context.TODO(), func(ctx projection.Job) error {
        refs, errs, err := ctx.Aggregates(ctx)
        if err != nil {
          return fmt.Errorf("extract aggregates: %w", err)
        }
    
        return streams.Walk(ctx, func(ref aggregate.Ref) error {
          foo := NewFoo(ref.ID)
    
          if err := ctx.Apply(ctx, foo); err != nil {
            return err
          }
    
          return foo.finalize(ctx, dep)
        }, refs, errs)
      })
    }
    

    Finalization is done for each individual projection after the job applies its events. A nice addition would be if finalization could be batched and deferred to the end of the projection job, like this:

    package example
    
    func example(s projection.Schedule) {
      var dep SomeDependency
    
      s.Subscribe(context.TODO(), func(ctx projection.Job) error {
        refs, errs, err := ctx.Aggregates(ctx)
        if err != nil {
          return fmt.Errorf("extract aggregates: %w", err)
        }
    
        return streams.Walk(ctx, func(ref aggregate.Ref) error {
          foo := NewFoo(ref.ID)
    
          if err := ctx.Apply(ctx, foo); err != nil {
            return err
          }
    
          return ctx.Defer(func() error {
            // this call will be deferred to after this projection update
            return foo.finalize(ctx, dep)
          })
        }, refs, errs)
      })
    }
    
    enhancement 
    opened by bounoable 0
  • Concurrent command handling

    Concurrent command handling

    Add options to enable concurrent command handling.

    Standalone command handler

    package example
    
    func example(bus command.Bus) {
      h := command.NewHandler(bus)
    
      h.Handle(context.TODO(), "foo", func(ctx command.Context) error { ... }, command.Workers(4))
    }
    

    Aggregate-based command handler

    package example
    
    type Foo struct {
      *aggregate.Base
      *handler.BaseHandler
    }
    
    func NewFoo(id uuid.UUID) *Foo { ... }
    
    func example(bus command.Bus, repo aggregate.Repository) {
      h := handler.New(NewFoo, repo, bus)
    
      h.Handle(context.TODO(), command.Workers(4))
    }
    
    enhancement 
    opened by bounoable 0
  • How to use `SoftRestorer`?

    How to use `SoftRestorer`?

    Problem

    When the event stream of an aggregate contains a SoftDeleter event, the aggregate can neither be queried nor fetched from the aggregate repository. How can an aggregate be restored if it cannot be fetched to raise the SoftRestorer event?

    Example

    package example
    
    type RestoredEvent struct {}
    
    func (RestoredEvent) SoftRestore() bool { return true }
    
    func example(repo aggregate.Repository) {
      var foo aggregate.Aggregate // soft-deleted aggregate
    
      if err := repo.Fetch(context.TODO(), foo); err != nil {
        // fails with repository.ErrDeleted
      }
    
      // we want to do this
      aggregate.Next(foo, "restored", RestoredEvent{})
      repo.Save(context.TODO(), foo)
    }
    

    Proposal – context.Context API

    The repository package could provide a "hidden" API using context.Context.WithValue() to disable soft-deletion checks:

    package example
    
    func example(repo aggregate.Repository) {
      var foo aggregate.Aggregate
    
      ctx := repository.WithSoftDeleted(context.TODO())
    
      repo.Fetch(ctx, foo)
      aggregate.Next(foo, "restored", RestoredEvent{})
      repo.Save(context.TODO(), foo)
    }
    

    Drawbacks

    • hiding options behind a Context is considered bad design
    opened by bounoable 2
  • Query Optimizer

    Query Optimizer

    An idea that came to my mind:

    package example
    
    func example(q1, q2 event.Query) {
      if query.IsSupersetOf(q2, q1) {
        log.Println("first query would return all events that the second query would return, and possibly more")
      }
    }
    

    If this could be implemented, projection jobs could further optimize queries for each individual projection. When a job applies itself onto multiple projections, it can check if a query that ran previously is a superset of the current query, and if so, just return the cached result from the previous query.

    A query q1 is a superset of another query q2 if each of q1's filters/constraints is either less or equally restricting than q2's.

    performance 
    opened by bounoable 0
Owner
modernice.design
modernice.design
Go gRPC Kafka CQRS microservices with tracing

Golang CQRS Kafka gRPC Postgresql MongoDB Redis microservices example ?? ??‍?? Full list what has been used: Kafka as messages broker gRPC Go implemen

Alexander 91 Jul 28, 2022
Build event-driven and event streaming applications with ease

Commander ?? Commander is Go library for writing event-driven applications. Enabling event sourcing, RPC over messages, SAGA's, bidirectional streamin

Jeroen Rinzema 60 Jun 13, 2022
Event-planning-go - GRAPHQL Project for Event Planning

About The Project GRAPHQL Project for Event Planning Building the project with l

Muhamad Hilmi Hibatullah 2 Mar 13, 2022
:incoming_envelope: A fast Message/Event Hub using publish/subscribe pattern with support for topics like* rabbitMQ exchanges for Go applications

Hub ?? A fast enough Event Hub for go applications using publish/subscribe with support patterns on topics like rabbitMQ exchanges. Table of Contents

Leandro Lugaresi 118 Aug 2, 2022
Easy to use distributed event bus similar to Kafka

chukcha Easy to use distributed event bus similar to Kafka. The event bus is designed to be used as a persistent intermediate storage buffer for any k

Yuriy Nasretdinov 73 Jul 28, 2022
Simple, high-performance event streaming broker

Styx Styx is a simple and high-performance event streaming broker. It aims to provide teams of all sizes with a simple to operate, disk-persisted publ

Dataptive 46 Jul 26, 2022
Discrete-event simulation in Go using goroutines

SimGo SimGo is a discrete event simulation framework for Go. It is similar to SimPy and aims to be easy to set up and use. Processes are defined as si

Felix Schütz 23 Jul 19, 2022
Simple synchronous event pub-sub package for Golang

event-go Simple synchronous event pub-sub package for Golang This is a Go language package for publishing/subscribing domain events. This is useful to

itchyny 19 Jun 16, 2022
pubsub controller using kafka and base on sarama. Easy controll flow for actions streamming, event driven.

Psub helper for create system using kafka to streaming and events driven base. Install go get github.com/teng231/psub have 3 env variables for config

Te Nguyen 5 May 29, 2022
POC of an event-driven Go implementation

Event Driven example in Golang This POC shows an example of event-driven architecture with a working domain event broker, an event producer and a cons

Fede Barcelona 0 Nov 2, 2021
Go library to build event driven applications easily using AMQP as a backend.

event Go library to build event driven applications easily using AMQP as a backend. Publisher package main import ( "github.com/creekorful/event" "

Aloïs Micard 0 Dec 5, 2021
Example Golang Event-Driven with kafka Microservices Choreography

Microservices Choreography A demonstration for event sourcing using Go and Kafka example Microservices Choreography. To run this project: Install Go I

Muhammad Nasrul 0 Dec 2, 2021
A basic event queue (and publisher/subscriber) in go

queue A basic event queue (and publisher/subscriber) in go. Installation go get github.com/jimjibone/queue Queue Usage Queue is a channel-based FIFO q

James Reuss 0 Dec 17, 2021
Basic Event Streaming - Fundamentals of Kafka Studies (BESt-FunKS)

Apache Kafka My study repo for Apache Kafka. Based on this tutorial. Contents Overview Key Terms Event Topic Producer Consumer Partition Getting Start

João Saraceni 2 Mar 2, 2022
A lightweight event collection system.

Honeypot A self-contained, multi-protocol streaming event collection system with ambitions to be as boring as benthos. Honeypot is primarily built for

silverton 39 Aug 3, 2022
socket.io library for golang, a realtime application framework.

go-socket.io go-socket.io is library an implementation of Socket.IO in Golang, which is a realtime application framework. Current this library support

Googol Lee 4.7k Jul 31, 2022
:notes: Minimalist websocket framework for Go

melody ?? Minimalist websocket framework for Go. Melody is websocket framework based on github.com/gorilla/websocket that abstracts away the tedious p

Ola 2.5k Aug 8, 2022
Kafka, Beanstalkd, Pulsar Pub/Sub framework

go-queue Kafka, Beanstalkd, Pulsar Pub/Sub framework.

chenquan 3 Jul 26, 2022
Idiomatic Event Sourcing in Go

Event Sourcing for Go Idiomatic library to help you build Event Sourced application in Go. Please note The library is currently under development and

eventually 75 Jul 13, 2022
A pluggable backend API that enforces the Event Sourcing Pattern for persisting & broadcasting application state changes

A pluggable "Application State Gateway" that enforces the Event Sourcing Pattern for securely persisting & broadcasting application state ch

Coleman Word 28 May 4, 2022
A pluggable backend API that enforces the Event Sourcing Pattern for persisting & broadcasting application state changes

A pluggable "Application State Gateway" that enforces the Event Sourcing Pattern for securely persisting & broadcasting application state changes

null 28 May 4, 2022
This example showcases an event-sourced CQRS system based on github.com/romshark/eventlog

Eventlog Example This example is showcasing an eventually consistent, fault-tolerant, event sourced system following the CQRS (Command-Query-Responsib

Roman Sharkov 3 Mar 13, 2022
Govent is an event bus framework for DDD event source implement

Govent is an event bus framework for DDD event source implement. Govent can also solve the package circular dependency problem.

Michaelyn 2 Jan 28, 2022
Go Server/API boilerplate using best practices DDD CQRS ES gRPC

Go Server/API boilerplate using best practices DDD CQRS ES gRPC

Rafał Lorenz 646 Aug 11, 2022
Go gRPC Kafka CQRS microservices with tracing

Golang CQRS Kafka gRPC Postgresql MongoDB Redis microservices example ?? ??‍?? Full list what has been used: Kafka as messages broker gRPC Go implemen

Alexander 91 Jul 28, 2022
Go-gin-ddd-cqrs - Clean api rest with Go, Gin and GORM

GOLANG API REST Clean api rest with Go, Gin and GORM. Clean Architecture with DD

Juan Cantón Rodríguez 12 Jul 25, 2022
Build event-driven and event streaming applications with ease

Commander ?? Commander is Go library for writing event-driven applications. Enabling event sourcing, RPC over messages, SAGA's, bidirectional streamin

Jeroen Rinzema 60 Jun 13, 2022
KEDA is a Kubernetes-based Event Driven Autoscaling component. It provides event driven scale for any container running in Kubernetes

Kubernetes-based Event Driven Autoscaling KEDA allows for fine-grained autoscaling (including to/from zero) for event driven Kubernetes workloads. KED

KEDA 5.3k Aug 11, 2022
gevent imply go-event which tries to make event handling easier.

gevent imply go-event which tries to make event handling easier. What does gevent want to do Async execute jobs safely without too many go routines. S

null 7 Nov 10, 2021