Leaky abstraction
An example of the maxim that all abstractions are leaky.
type Book struct {
author String
}
func (b *Book) Validate() error {
return nil
}
DB layer
func (d *DB) UpdateBook(b *Book, newAuthor String) error {
b.author = newAuthor
if err := b.Validate(); err != nil {
return fmt.errorf("bad validation")
}
if err := d.Update(b); err != nil {
return fmt.errorf("Server error")
}
return nil
}
...
API layer
func (s *Service) UpdateBook(ID Int, newAuthor String) error {
// The semantics here is that if it is a validation error,
// let the user know (e.g. 400). If it is a server error.
// Hide it from the user (e.g. 500).
// This is where the abstraction leaks, assuming we don't
// want to do string matching.
err := errorRemapper(s.db.UpdateBook(s.db.GetBook(ID), newAuthor))
// Either a complex errorRemapper is required or the UpdateBook
// needs to return the necessary HTTP/API error
}