Matthew Lang avatar

Hanami Slices

A slice is a self-contained sub-application within a Hanami project — its own actions, views, and container, but sharing the same process and deploy unit as the rest of the app. This is Hanami’s answer to “modular monolith”: bounded contexts without splitting into separate services.

Why split into slices

  • A large domain gets hard to navigate as one flat app/ tree — an admin area, a public API, and a customer-facing app can each become a slice instead of all living under one namespace.
  • Each slice gets its own container, so dependencies are scoped: the admin slice doesn’t accidentally reach into public-facing view helpers, and vice versa.
  • Slices can be tested in isolation — booting just the slice under test rather than the whole app speeds up the suite as the app grows.

Directory structure

slices/
  admin/
    actions/
    views/
    templates/
    config/
      routes.rb      # optional, slice-specific routes
      slice.rb        # optional, slice-specific config/imports
  api/
    actions/
    ...
app/                  # the main app, itself effectively the default "slice"

Running hanami generate slice admin scaffolds the directory and registers it — no manual wiring required for the common case.

Routing into a slice

# config/routes.rb (main app)
module MyApp
  class Routes < Hanami::Routes
    slice :admin, at: "/admin" do
      root { "Admin dashboard" }
      resources :users
    end
  end
end

Everything registered inside the slice :admin do ... end block resolves against the admin slice’s own container, not the main app’s.

Cross-slice dependencies

Slices are isolated by default — a slice can’t see another slice’s components unless it’s explicitly imported:

# slices/admin/config/slice.rb
module Admin
  class Slice < Hanami::Slice
    import from: :main, as: :main
  end
end
# slices/admin/actions/users/index.rb
include Deps["main.repositories.user_repo"]

This keeps the dependency graph explicit and visible in one place per slice, rather than discoverable only by reading action bodies.

When not to bother

For a small app with one clear domain, slices are overhead for no benefit — the plain app/ directory is a slice already (the implicit “main” one), and most apps never need to split further. Reach for an explicit slice when a sub-area’s actions/views/dependencies genuinely don’t overlap with the rest — an admin panel and a public marketing site sharing one deploy are the canonical case, not “this controller folder feels big.”