Class: Hanami::Repository

Inherits:
ROM::Repository::Root
  • Object
show all
Defined in:
gems/gems/hanami-model-1.1.0/lib/hanami/repository.rb

Overview

Mediates between the entities and the persistence layer, by offering an API to query and execute commands on a database.

By default, a repository is named after an entity, by appending the Repository suffix to the entity class name.

A repository is storage independent. All the queries and commands are delegated to the current adapter.

This architecture has several advantages:

  • Applications depend on an abstract API, instead of low level details (Dependency Inversion principle)

  • Applications depend on a stable API, that doesn't change if the storage changes

  • Developers can postpone storage decisions

  • Isolates the persistence logic at a low level

Hanami::Model is shipped with one adapter:

  • SqlAdapter

All the queries and commands are private. This decision forces developers to define intention revealing API, instead of leaking storage API details outside of a repository.

Examples:

require 'hanami/model'

class Article < Hanami::Entity
end

# valid
class ArticleRepository < Hanami::Repository
end

# not valid for Article
class PostRepository < Hanami::Repository
end
require 'hanami/model'

# This is bad for several reasons:
#
#  * The caller has an intimate knowledge of the internal mechanisms
#      of the Repository.
#
#  * The caller works on several levels of abstraction.
#
#  * It doesn't express a clear intent, it's just a chain of methods.
#
#  * The caller can't be easily tested in isolation.
#
#  * If we change the storage, we are forced to change the code of the
#    caller(s).

ArticleRepository.new.where(author_id: 23).order(:published_at).limit(8)

# This is a huge improvement:
#
#  * The caller doesn't know how the repository fetches the entities.
#
#  * The caller works on a single level of abstraction.
#    It doesn't even know about records, only works with entities.
#
#  * It expresses a clear intent.
#
#  * The caller can be easily tested in isolation.
#    It's just a matter of stubbing this method.
#
#  * If we change the storage, the callers aren't affected.

ArticleRepository.new.most_recent_by_author(author)

class ArticleRepository < Hanami::Repository
  def most_recent_by_author(author, limit = 8)
    articles.
      where(author_id: author.id).
        order(:published_at).
        limit(limit)
  end
end

See Also:

Since:

  • 0.1.0