Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use SoftRestorer? #40

Open
bounoable opened this issue Apr 16, 2022 · 3 comments
Open

How to use SoftRestorer? #40

bounoable opened this issue Apr 16, 2022 · 3 comments

Comments

@bounoable
Copy link
Contributor

bounoable commented Apr 16, 2022

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
@bounoable
Copy link
Contributor Author

bounoable commented Apr 21, 2022

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

  • clunky to use
  • not concurrency-safe
  • requires users to depend on *repository.Repository instead of aggregate.Repository

@bounoable
Copy link
Contributor Author

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

  • requires users to depend on *repository.Repository instead of aggregate.Repository

@olpie101
Copy link
Contributor

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)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants