Organising Code with Slices

· 3 min read · Updated March 7, 2026 · intermediate
hanami slices code-organization modularity

In the previous tutorial, we explored persistence with ROM in Hanami. Now we’ll dive into Slices—one of Hanami 2’s most powerful features for organizing code in larger applications.

What are Slices?

Slices are Hanami’s solution for code organization in medium-to-large applications. They provide modular boundaries that group related components together—actions, views, repositories, entities, and services—all working within a specific domain.

Think of slices as mini-applications within your main Hanami app. Each slice:

  • Has its own set of actions
  • Can have its own views and templates
  • Contains domain-specific logic
  • Shares core infrastructure with other slices

Why Use Slices?

As your application grows, dumping everything into the app/ directory becomes unmanageable. Slices solve this by:

  1. Domain boundaries — Group code by feature or domain
  2. Isolation — Each slice can be relatively independent
  3. Team scalability — Different teams can work on different slices
  4. Clear dependencies — Explicit about what each slice needs

Creating a Slice

Generate a new slice using the Hanami CLI:

bundle exec hanami generate slice admin

This creates the following structure:

app/
├── slices/
│   └── admin/
│       ├── actions/
│       ├── views/
│       └── slice.rb

The slice file defines the slice’s configuration:

# app/slices/admin.rb
module Admin
  class Slice < Hanami::Slice
    config.relations = {
      layout: :admin
    }
  end
end

Anatomy of a Slice

A slice consists of several components:

Actions

Actions within a slice live in the slice’s namespace:

# app/slices/admin/actions/dashboard.rb
module Admin
  module Actions
    class Dashboard < Hanami::Action
      def handle(request, response)
        response.render "admin/dashboard", stats: compute_stats
      end
      
      private
      
      def compute_stats
        { users: UserRepository.new.count }
      end
    end
  end
end

Routes for slice actions are defined in config/routes.rb:

# config/routes.rb
Hanami.configure do
  slice :admin, at: "/admin" do
    get "/dashboard", to: "dashboard"
  end
end

Views and Templates

Slice views follow the same pattern as regular views:

# app/slices/admin/views/dashboard.rb
module Admin
  module Views
    class Dashboard < View
      def render
        html "admin/dashboard", stats: stats
      end
    end
  end
end

Repositories and Entities

Slices can have their own repositories and entities:

# app/slices/admin/repositories/user_repository.rb
module Admin
  module Repositories
    class UserRepository < ROM::Repository[:users]
    end
  end
end

Sharing Code Between Slices

Slices can explicitly import shared code from the main app or other slices:

# app/slices/admin.rb
module Admin
  class Slice < Hanami::Slice
    # Import from main app
    import for: :users
    
    # Import from another slice
    slice :billing, import: :invoices
  end
end

You can also import specific components:

# app/slices/admin.rb
module Admin
  class Slice < Hanami::Slice
    # Only import specific repositories
    import Repositories::UserRepository, from: :users
    import Entities::User, from: :users
  end
end

Slice Configuration

Configure slices with their own settings:

# app/slices/admin.rb
module Admin
  class Slice < Hanami::Slice
    config.relations = {
      layout: :admin,
      assets: {
        prefix: "admin-assets"
      }
    }
    
    # Slice-specific middleware
    middleware.use Admin::AuthMiddleware
    
    # Slice-specific services
    register Admin::Services::UserService
  end
end

Practical Example: An Admin Slice

Here’s how a real admin slice might look:

# app/slices/admin/actions/users.rb
module Admin
  module Actions
    module Users
      class Index < Hanami::Action
        def handle(request, response)
          users = user_repo.all
          response.render "admin/users/index", users: users
        end
        
        private
        
        def user_repo
          Admin::Repositories::UserRepository.new
        end
      end
      
      class Create < Hanami::Action
        def handle(request, response)
          user = user_repo.create(request.params[:user])
          response.redirect_to "/admin/users/#{user.id}"
        end
        
        private
        
        def user_repo
          Admin::Repositories::UserRepository.new
        end
      end
    end
  end
end

When to Use Slices

Slices are ideal when:

  • Building multi-tenant applications
  • Creating an admin panel separate from the main app
  • Implementing distinct features with their own domain logic
  • Scaling teams across different features
  • Building APIs for different clients (web, mobile, third-party)

Consider simpler organization if:

  • Your app is small (< 10 models)
  • Features are tightly coupled
  • You’re prototyping

Summary

You’ve learned how Hanami Slices help organize code in larger applications:

  1. Slices provide modular boundaries for grouping domain-specific code
  2. Each slice contains actions, views, repositories, and entities
  3. Explicit imports control dependencies between slices
  4. Slice configuration allows customization per slice

Slices are the key to building maintainable Hanami applications as they grow. Combined with actions, views, and ROM persistence, you now have all the tools needed to build complete Hanami applications.