View Source Dependable (dependable v0.1.0)

Lightweight dependency injection using application config.

Dependable works by making defined behaviours callable by name. Dependable will do the lookup of the implementation module for you and invoke it. This makes it easier to see behaviour in application code as well as getting auto complete of the functions defined in the behaviour.

As an example, say we have the need for sending emails in our application. We might define a behaviour such as:

defmodule MyApp.EmailProvider do
  @callback send(MyApp.Email.t()) :: {:ok, MyApp.Email.t()} | {:error, MyApp.EmailSendError.t()}
end

Now we can define an implementation for this module.

defmodule Infra.FancyEmailProvider do
  @behaviour MyApp.EmailProvider

  @impl MyApp.EmailProvider
  def send(%MyApp.Email{} = email) do
    # ...implementation details ...
  end
end

Next we can use application environment to configure what email provider is used. For this example we can imagine that in prod envs we would want the FancyEmailProvider but in testing environments we would want to inject a Mox mock. For the key name in configuration we can use the name of the behaviour itself which is the default lookup key used by Dependable.

# in config.exs
config :my_app, MyApp.EmailProvider, Infra.FancyEmailProvider

# in test.exs
# this mock would have to be defined in some tool such Mox
config :my_app, MyApp.EmailProvider, MyApp.EmailProvider.Mock

Lastly, we can now call into the behaviour in our application logic.

defmodule MyApp.Onboarding do
  def onboard_customer(params) do
    params
      |> MyApp.Email.new!()
      |> MyApp.EmailProvider.send()
  end
end

Summary

Functions

Lookup the implementor of a given behaviour raising if one cannot be found.

Functions

Link to this function

impl!(otp_app, lookup_key)

View Source

Lookup the implementor of a given behaviour raising if one cannot be found.