modernice / goes

goes is an event-sourcing framework for Go.
https://goes.modernice.dev
Apache License 2.0
134 stars 12 forks source link

Add `handler.Before()`, `handler.After()`, and `handler.WithLock()` options #154

Open bounoable opened 1 year ago

bounoable commented 1 year ago

Package: github.com/modernice/goes/event/handler

handler.Before and handler.After

Note: need to think about function signatures for Before and After options. Receiving multiple events in the callbacks only makes sense if the Debounce option will be implemented (#153)

package example

func example(bus event.Bus) {
  h := handler.New(
    bus,

    handler.Before(func(_ context.Context, events []event.Event) error {
      log.Printf("Handling events:\n%v", events)
      return nil
    }),

    handler.After(func(_ context.Context, r handler.Result) error {
      log.Printf("Failed to handle events:\n%v", r.Events)
      log.Printf("Failed event: %v", r.Failed)
      log.Printf("Error: %v", f.Err)

      // either return an error so it will be returned in the handler's error channel
      return fmt.Errorf("wrapped error: %w", f.Err)

      // or return nil to not report the error in the error channel
      return nil
    }),
  )
}

handler.WithLock and handler.WithReadLock

handler.WithLock and handler.WithReadLock are wrappers for handler.Before() and handler.After() to lock/unlock a Mutex (or some type with Lock + Unlock or RLock + RUnlock methods) before/after handling events:

package example

func example1(bus event.Bus) {
  var mux sync.Mutex

  h := handler.New(bus, handler.WithLock(&mux))
}

func example2(bus event.Bus) {
  var mux sync.RWMutex

  h := handler.New(bus, handler.WithReadLock(&mux))
}

func example3(bus event.Bus) {
  var mux sync.RWMutex

  h := handler.New(bus, handler.WithLock(&mux))
}

WithLock implementation:

package handler

type Mutex interface {
  Lock()
  Unlock()
}

func WithLock(l Mutex) Option {
  return func(h *Handler) {
    Before(func(context.Context, []event.Event) error {
      l.Lock()
      return nil
    })(h)

    After(func(context.Context, handler.Result) error {
      l.Unlock()
      return err
    })(h)
  }
}