modernice / goes

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

How to use `SoftRestorer`? #40

Open bounoable opened 2 years ago

bounoable commented 2 years ago

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

bounoable commented 2 years ago

Proposal – "Prepare" method

The repository.Repository type could provide a Prepare() method that "prepares" the next query/fetch.

package example

func example(repo *repository.Repository) {
  var foo aggregate.Aggregate

  repo.Prepare(repository.WithSoftDeleted())
  repo.Fetch(ctx, foo)
  aggregate.Next(foo, "restored", RestoredEvent{})
  repo.Save(context.TODO(), foo)
}

Drawbacks

bounoable commented 2 years ago

Proposal – Add specialized methods

The repository.Repository type could provide a FetchDeleted() and a QueryDeleted() method.

package example
func example(repo *repository.Repository) {
  var foo aggregate.Aggregate

  repo.FetchDeleted(ctx, foo)
  aggregate.Next(foo, "restored", RestoredEvent{})
  repo.Save(context.TODO(), foo)
}

Drawbacks

olpie101 commented 11 months ago

Could variadic options not be added to the repository.Fetch method? It shouldn't be backwards incompatible since all previous calls would not include any extra arguments. This would bring it in line with aggregate stream implementation.

Proposal - Use variadic options

The repository.Fetch method could provide a repository.WithSoftDeleted(true) option

package example
func example(repo *repository.Repository) {
  var foo aggregate.Aggregate // aggregate whose latest event was a SoftDelete => true

  repo.Fetch(ctx, foo, repository.WithSoftDeleted(true))
  aggregate.Next(foo, "restored", RestoredEvent{})
  repo.Save(context.TODO(), foo)
}