Dependency injection in Julia


Wondering if anybody found a good way to implement dependency injection in Julia. I have many situations where I use “adapters”: take for instance a Database module which uses various adapters (MySQL, SQLite, Postgres, etc).

And what I usually end up with is:

  • a const in the module global scope which references the adapter
  • a lot of eval statements in the line of:
if haskey(SearchLight.config.db_config_settings, "adapter") && SearchLight.config.db_config_settings["adapter"] != nothing
  db_adapter = Symbol(SearchLight.config.db_config_settings["adapter"] * "DatabaseAdapter")

  Core.eval(@__MODULE__, :(using .$db_adapter))
  Core.eval(@__MODULE__, :(const DatabaseAdapter = $db_adapter))
  Core.eval(@__MODULE__, :(export DatabaseAdapter))
  const DatabaseAdapter = Nothing

Looks like a code smell to me.

I think a nice solution would be to have a mechanism to pass arguments when using a module, in the line of:
using Database(adapter = MySQLAdapter)


I think a more Julian approach would be to utilize a kind of DataBaseAdapter interface and rely on the caller to call setup appropriately; something like:

module Adapters

abstract type Adapter end

"Subtype must define `connect!(x::MyAdapter)` to satisfy interface"
function connect! end

"Subtype must define `query(x::MyAdapter)` to satisfy interface"
function query end


module AdapterAs

import Adapters

struct AdapterA

Adapters.connect!(::AdapterA) = # ...
Adapters.query(::AdapterA) = # ...


module App

import Adapters

const ADAPTER = Ref{Adapter}()

function setadapter!(x::Adapter)
    ADAPTER[] = x

function query(sql::String)


With this kind of setup, you leave it to the driving application or script to call import AdapterAs and App.setadapter!, or potentially import AdapterBs if there was an alternative adapter they wished to use.

  1. Naming is a bit hacky. I would try to slim it.
  2. I would try to factorize by parametrizing as mush as i can (see below)
  3. otherwise not a bad pattern
# parametrizable with (conf section=="DB")

if haskey(FOO.config.db, "driver") && FOO.config.db["driver"] != nothing        # todo parametrize config section "db"
    driver = Symbol(FOO.config.db["driver"] * "DbDriver")                       # parametrize julia type suffix

    include("db/$(driver).jl")                                                 # parametrize directory
    Core.eval(@__MODULE__, :(using .$driver))
    Core.eval(@__MODULE__, :(const DbDriver = $driver))                         # parametrize julia type suffix (see previous one)
    Core.eval(@__MODULE__, :(export DbDriver))                                  # idem
    const DbDriver = Nothing                                                    # idem


Another reason for reviving DBAPI? :wink: