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