How to use dependency injection pattern in Elixir?

Hi all

I want to make my codes scalable and decide to use dependency injection pattern.

How to use the concept of dependency injection in elixir?

THanks

2 Likes

Hey @kostonstyle could you provide a more practical example of what you want to do? Two things you may be interested in are:

  1. Module module, which has functions like concat which let you dynamically build module namespaces.

  2. The Kernel.apply/3 function which let’s yous dynamically call function passing their module and function name as an atom

1 Like

For example, to write a testable module that make an http request to the server. In test, I need a fake server.

If you are looking for full fledged DI framework that will instantiate dependencies recursively based on say behaviors it does not exist.

But on the other hand if you can live with something simple as passing a module name as default argument to a function, and providing custom module name in tests that will work.

2 Likes

Yes, that is what I mean. The problem is only the dynamic typing. How can I ensure, when someone use the function, that will pass the right module?

I’ve find this https://www.djm.org.uk/posts/writing-extensible-elixir-with-behaviours-adapters-pluggable-backends/ article, but do not know, if it is the right way.

1 Like

Dependency injection is bad pattern used in OO languages to overcome weakness of these languages.
DI frameworks are very dangerous (they do many things under which you don’t have control of)
For this reason I will never use Angular :smiley:
In Java the example of DI is Spring Framework

Dependencies are not good, they are bad! They should be things that we try to avoid wherever possible.
https://dzone.com/articles/dependency-injection-makes

Functional languages don’t need DI.
In functional languages you just compose functions.
For testing you just mock function like GitHub - jjh42/mock: Mocking library for Elixir language

5 Likes

Could you show me an example?

I was just trying to google something like that for you. Yes, behaviors is the fairly simple way to go and it’ll ensure the interface used in tests actually matches the one used in production (although the implementation differs).

And @mkunikow a lot of bad things can be said about mocks as well. My usual biggest concern is that you build the system that works in tests with mocks, but there can be mismatch between your mock’s interface and actual interface that you’ll miss and learn about it the hard way. Maintaining mocks, making sure everything’s mocked properly is also a considerable effort. It simply can be easier to build your own “null object” and pass it as a DI, or function parameter call it like you want, rather than trying to stub everything out.

@kostonstyle and you know about this, right: https://github.com/BlakeWilliams/pact
?

Not sure what is so unpredictable about injecting based on interface or in case of Elixir say based on the behavior.

I like the concept you mentioned earlier, passing module to the function parameter.
But how can I ensure, that user pass the right module as function parameter, that contains the function?

May I solve the problem with protocol?

Generally speaking, this question comes up a lot when people are coming from OO languages. @mkunikow makes a valid point that you don’t need it. The solution is fairly simple especially when it comes to testing. If you take the following steps, you won’t need to mock your functions that are touching the outside world.

  1. Keep your impure functions separate from your pure functions. If you have a function that touches a database, calls a webservice, gets the date, etc. Or anything that touches the outside world it should be put in a separate module/function and there should be only one place in your program where it’s called and that data gets passed to your pure functions.

  2. Your unit test will only need the stubbed data that would have come from an impure function like the data that would come from a webservice or a database.

The only time mocking is necessary is when you are calling impure functions from pure functions and they are intertwined (this is true even in OO languages). However, if you keep your impure functions separate from your pure functions then there is no need to have DI or a mocking framework when unit testing.

4 Likes

If it calls an impure function, it can’t be a pure function. :smile:

4 Likes

I know, I was referring to that’s the reason why it’s used in OO languages in the first place :slight_smile:

Throwing another article out here: Mocks and explicit contracts.

It presents several ways to “inject” dependencies to your functions, specifically by 1) defining the dependency module on your config, 2) passing the dependency’s handler function as the caller params, or 3) same as 2 but passing the dependency module altogether.

These three approaches allow you to use mock dependencies for your tests.

My initial guess is to use a combination of behaviors and typespecs.

This article echoes José’s Plataformatec article I shared above on dependency injection methods, so I think you can use that as a base. The only thing it left out is for you to validate if a module has adopted a specific behavior. I’m not sure at the specifics on how to do that, though.

1 Like

If a module fails to fully implement a behaviour, the compiler will warn about it…

1 Like

I’m surprised this topic didn’t spark any discussions around GenStage?

Personally, I’ve been using a pattern similar to Pact, but I have an item in the TODO list to properly learn and understand GenStage to decouple my system. Granted, it won’t solve your synchronous usecases, but I believe it’s worth a mention.

Article - example in F#

3 Likes

There is one line in Jose’s blog post that is intriguing:

Finally, you could also make the dependency a data structure and define the contract with a protocol.

Can anyone illustrate this for me?