Open blaggacao opened 3 years ago
I'm playing with the idea of first (above the line) vs second (blow the line)
// Repository knows how to persist the service's aggregate
type Repository interface {
// Add knows how to create an initialized instance of an aggregate
// it expects an initialized instance be return from an add funcion or nil to bail out
Add(ctx context.Context,f func() (*account.Account)) (uuid.UUID, error)
// Add knows how to remove an identifiable instance of an aggregate
// it also returns a copy to a remove funtion in order to bail out
Rem(ctx context.Context, i Identifiable, f func(a account.Account) bool) error
// Update knows how to update an identifiable instance of an aggregate
Update(ctx context.Context, i Identifiable, f func(a *account.Account) bool) error
}
// Identifiable can be identified by the Repository
type Identifiable interface {
// Identifer knows how to identify an object
Identifier() uuid.UUID
}
// Store knows how to read and write an entity
type Store interface {
// Load knows how to load an entity
Load(ctx context.Context, uuid uuid.UUID) (a *account.Account, err error)
// Save knows how to save an entity
Save(ctx context.Context, a *account.Account) error
}
// Identifiable can be identified so that the store can load it
type Identifiable interface {
// Identifer knows how to identify an object
Identifier() uuid.UUID
}
The latter approach has several benefits in my eyes:
// cave: pseudo-code ...
func(h *DoSomethingHandler) Handle(ctx context.Context, ds DoSomething) error {
old, err := Load(ctx, ds.Identifier())
new := old.Copy()
...
ok := h.pol.Can(ctx, ds, "DoSomething", new) // check policy
if err = h.handle(ctx, new); err != nil { // handle domain logic
return errwrap.Wrap(ErrInDomain, err)
}
if err = Save(ctx, new); err != nil { // nil to delete
return errwrap.Wrap(ErrDuringSave, err)
}
...
err = Publish(ctx, &SomethingDone{...})
if err != nil {
Save(ctx, old)
}
}
While continuing to work on
ddd-gen
(example) I had a couple of ideas / made a couple of choices worth sharing here:Add
andRemove
as well, so that the domain does not need to know about the behaviour of the repository interface (that only works on an aggregate)Identifiable
s to the repository so that the auto generated code does not need to know too much about the commands data structures, while still verifying identifiability of the object (domain aggregate) on which the command should be executed. → Repo Interface