A lightweight, flexible dependency injection framework for Elixir.

Elixir Hex.pm Documentation Elixir CI License Hex.pm Download Total

About

Ginject is a thoughtfully designed dependency injection framework for Elixir applications that embraces the philosophy of explicit over implicit. It provides a clean, declarative way to manage dependencies while maintaining the flexibility and power that Elixir developers expect.

Why Ginject?

  • Elixir-centric Design: Built from the ground up with Elixir's patterns and practices in mind
  • Compile-time Resolution: Catch configuration issues early with compile-time dependency resolution
  • Flexible Strategy System: Easily adapt to different dependency resolution needs through configurable strategies
  • Testing First: First-class support for testing with mock implementations and strategy swapping
  • Zero Runtime Overhead: All the dependency wiring happens at compile time
  • Safety: Leverages Elixir behaviours for interface definitions

Installation

Add ginject to your list of dependencies in mix.exs:

def deps do
  [
    {:ginject, "~> 0.1.0"}
  ]
end

Usage

Basic Usage

There are two ways to inject services into a module:

Using the use directive with :services option

defmodule YourApp.UserController do
  use Ginject, services: [
    YourApp.UserService,
    {YourApp.AuthService, as: Auth}
  ]

  def create_user(params) do
    with {:ok, user} <- UserService.create(params),
         {:ok, token} <- Auth.generate_token(user) do
      {:ok, %{user: user, token: token}}
    end
  end
end

Using the service macro

defmodule YourApp.UserController do
  use Ginject

  service YourApp.UserService
  service YourApp.AuthService, as: Auth

  def create_user(params) do
    with {:ok, user} <- UserService.create(params),
         {:ok, token} <- Auth.generate_token(user) do
      {:ok, %{user: user, token: token}}
    end
  end
end

Configuration

Configure Ginject in your application config:

# config/config.exs
config :ginject, Ginject.DI,
  strategy: Ginject.Strategy.BehaviourAsDefault,
  services: [
    {YourApp.UserController, [
      %{service: YourApp.UserService, impl: YourApp.UserService.Impl},
      %{service: YourApp.AuthService, impl: YourApp.AuthService.Impl}
    ]}
  ]

Best Practices

1. Define Service Interfaces Using Behaviours

defmodule YourApp.UserService do
  @callback create(params :: map()) :: {:ok, User.t()} | {:error, term()}
  @callback update(id :: integer(), params :: map()) :: {:ok, User.t()} | {:error, term()}
end

2. Keep Service Implementations Isolated

defmodule YourApp.UserService.Impl do
  @behaviour YourApp.UserService
  
  @impl true
  def create(params) do
    # Implementation
  end

  @impl true
  def update(id, params) do
    # Implementation
  end
end

3. Use Meaningful Aliases

Choose clear and concise aliases that make the code more readable:

# Good
service UserManagement.AuthenticationService, as: Auth
service UserManagement.AuthorizationService, as: Permissions

# Avoid
service UserManagement.AuthenticationService, as: A
service UserManagement.AuthorizationService, as: Auth2

Testing

Add Hammox dependency in your mix.exs:

def deps do
  [
    ...,
    {:hammox, "~> 0.7", only: :test}
  ]
end

Use the builtin Mox strategy:

# config/test.exs
config :ginject, Ginject.Di, strategy: Ginject.Strategy.Mox

While in your test use:

defmodule MyTest do
  use ExUnit.Case
  use Ginject.Test

  test "..." do
    mock = get_injected_service(MyModule, MyService)

    expect(mock, :function_name, fn ->
      # assertions
    end)
  end
end

See Mox or Hammox documentation to learn how to set expectations.

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Make sure to:

  • Write tests for new features
  • Update documentation as needed
  • Follow the existing coding style
  • Write good commit messages

License

This project is licensed under the MIT License - see the LICENSE file for details.

Credits

Created and maintained by Gigitsu.