Hanami: Entities, Repositories, and Representers

Introduction

Hanami is a modern web framework for Ruby that emphasizes simplicity, performance, and maintainability. It follows the principles of Domain-Driven Design (DDD) and separates the concerns of the application into different components. Three crucial components in Hanami are entities, repositories, and representers.

Entities

Definition:

  • Entities are the core objects in the domain model of a Hanami application. They represent business logic and domain-specific rules.

Characteristics:

  • Immutable: Entities in Hanami are immutable by default. This means that once an entity is created, its state cannot be changed.
  • Business Logic: They encapsulate the core business logic and validation rules.
  • Plain Ruby Objects: Entities are plain Ruby objects (POROs) without any dependency on external frameworks or libraries.

Example:

class User < Hanami::Entity
  attributes do
    attribute :id, Types::Int
    attribute :name, Types::String
    attribute :email, Types::String
    attribute :created_at, Types::Time
    attribute :updated_at, Types::Time
  end
 
  def full_name
    "#{first_name} #{last_name}"
  end
end

Use Case:

  • Use entities to represent and enforce the rules and logic of your business domain.

Repositories

Definition:

  • Repositories provide an abstraction layer for data persistence. They handle the interactions with the database, such as querying, inserting, updating, and deleting records.

Characteristics:

  • Data Access Layer: They act as the data access layer, providing a clean separation between the domain logic and database operations.
  • CRUD Operations: Repositories offer methods for Create, Read, Update, and Delete (CRUD) operations.
  • Query Interface: They provide a query interface to retrieve data from the database.

Example:

class UserRepository < Hanami::Repository
  def find_by_email(email)
    users.where(email: email).one
  end
 
  def all_users
    users.to_a
  end
end

Use Case:

  • Use repositories to manage database interactions and keep database-specific logic out of the domain logic.

Representers

Definition:

  • Representers are responsible for defining how entities are serialized and deserialized. They manage the transformation of data between entities and external representations such as JSON or XML.

Characteristics:

  • Serialization/Deserialization: They handle the conversion of entities to and from external formats.
  • Flexible: Representers can be customized to include or exclude certain attributes and to format data as needed.
  • Integration with APIs: Representers are useful for creating APIs where data needs to be exchanged in specific formats.

Example:

class UserRepresenter < Hanami::Representer
  property :id
  property :name
  property :email
  property :created_at
  property :updated_at
end
 
# Usage
user = User.new(id: 1, name: 'John Doe', email: 'john.doe@example.com')
user_representer = UserRepresenter.new(user)
user_representer.to_json

Use Case:

  • Use representers to manage the presentation layer, ensuring that data is properly formatted when interacting with external systems or APIs.

Summary

In Hanami, entities, repositories, and representers play distinct but complementary roles in the architecture of an application:

  • Entities: Represent the core business logic and domain rules. They are immutable and encapsulate domain-specific behavior.
  • Repositories: Serve as the data access layer, providing methods to interact with the database. They abstract the persistence layer and offer CRUD operations.
  • Representers: Handle the serialization and deserialization of entities, managing the transformation of data for external representations such as JSON or XML.

By separating these concerns, Hanami ensures a clean, maintainable codebase that adheres to the principles of Domain-Driven Design and promotes a clear separation of concerns.