| name | implement-di |
| license | MIT |
| type | atomic |
| description | Inject dependencies through Hanami's `auto_inject` using `Deps["provider_key"]` — never call `Hanami.app["key"]` outside of providers, dependencies must be pre-registered by a provider using a descriptive dot-namespaced snake_case key, inject via constructor (no custom `initialize`), pass `instance_double` test stubs through `.new(keyword:)` in specs, validate resolution to catch `Dry::Container::Error` on unregistered keys, and access dependencies by their last key segment — for example `Deps["repos.user_repo"]` → call `user_repo`. Use when adding DI to actions, operations, or repositories. Trigger words: dependency injection, DI, auto_inject, Deps, inject, dry-system, constructor injection, test with DI.
|
| metadata | {"version":"1.0.0","user-invocable":"true"} |
Implementing Dependency Injection
Inject dependencies through the constructor using Hanami's auto_inject — never call the container directly.
Quick Reference
- Pattern:
include Deps["provider_key"] in actions, operations, or repositories.
- Testing: Pass test doubles through the constructor in specs.
- Providers: Dependencies must be registered by a provider first.
- Rule: Never call
Hanami.app["key"] outside of providers.
HARD-GATE
DO NOT call the container directly (`Hanami.app["key"]`) outside of providers.
DO NOT use global state or class-level constants for dependencies.
DO inject dependencies through the constructor — use `include Deps[...]`.
Core Process
- Verify the provider — ensure the dependency is registered in a provider under a descriptive key.
- Add injection in the consuming class:
module Api
module Actions
module Users
class Create < Api::Action
include Deps["operations.users.create_user"]
def handle(req, res)
result = create_user.call(req.params.to_h)
end
end
end
end
end
- For operations, inject repositories and other operations:
module Users
class CreateUser < Dry::Operation
include Deps["repositories.user_repo", "operations.notifications.send_welcome"]
def call(input)
end
end
end
- For repositories, inject ROM if needed:
module Api
module Repositories
class UserRepo < ROM::Repository[:users]
end
end
end
- Validate resolution — after adding the injection, confirm the key resolves correctly:
bundle exec hanami console
If the key is unregistered, dry-system raises Dry::Container::Error: Nothing registered with the key. Check that the provider file exists, the key matches exactly (dot-namespaced, snake_case), and auto_registration covers the file path.
- Test with stubs — inject test doubles through the constructor:
RSpec.describe Api::Actions::Users::Create do
let(:create_user) { instance_double(Users::CreateUser, call: Dry::Monads::Success(user)) }
let(:action) { described_class.new(create_user:) }
it "calls the operation with request params" do
result = action.call({ "user" => { "email" => "test@example.com" } })
expect(create_user).to have_received(:call).with(hash_including("user"))
end
end
For testing DI patterns, see TESTING_DI.md.
Integration
| Skill | When to chain |
|---|
| configure-providers | Register the dependency before injecting it |
| load-context | Discover existing DI conventions before adding new ones |
| hanami-setup | Part of the project onboarding workflow |