How to use `SoftRestorer`?
Opened this issue · 3 comments
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
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 ofaggregate.Repository
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 ofaggregate.Repository
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)
}