Dependency Injection in ReasonML using Modules
Tags: reason, tutorial, softwaredesignpattern
Published: 2020-01-31 08:23:38

When I wrote my first project in ReasonML I just imported modules from anywhere as that just seemed to be the way things were done. This was fine when writing a simple React frontend, but when I later wrote the more [complex backend for Bouken](https://github.com/RawToast/bouken) it didn't scale and quickly became messy and hard to test.

I was used to using dependency injection – often known as DI – as a pattern from writing Java and Scala, which is a pattern that enables inversion of control. Or more simply put, constructing an object or module from other modules/objects and using the functions provided. 

Now in ReasonML functors – functions from modules to modules – can be used to implement dependency injection. Functors can be used for more advanced techniques, such as creating modules that parameterise other modules; however, this is a good introduction to the module system. When I first used this technique the language server failed to understand what I was doing! Thankfully, that issue been fixed!

Why would you want to use dependency injection? Well, for example, you could have a service that uses another module to make a call to an API:

```reasonml
module MyService = {
  let userAndComments = id => { 
     let user = UserApi.userById(id);
     switch(user) {
       | Some(user) => { 
       let comments = CommentsApi.fetchUserComments(id); 	
       Some({ user.name, comments });
       }
       | None => None
     }
   };
};
```

The code should work fine, but are a few issues with this:

* It can lead to a spiderweb of dependencies.
* It can be hard to unit test, in this example you cannot test the `userAndComments` method without making an HTTP call. In order to test this module, you would require either a mocked server or an integration test.

We can use dependency injection to control what the service receives in order to facilitate easier unit testing. So how would we go about that?

First, let define a type that we can inject. This is a simple type definition for logging output.

```reasonml
module type Logger = {
  let log: string => unit;
};
```

Then we can create a new module that uses the Logger we have defined. In OCaml and Reason a module that is built from other modules is known as a _Functor_.


```reasonml
module GreetingService = (LOG: Logger) => {
  let sayHello = name => LOG.log("Hello " ++ name); 
};
```

Now we can implement a logger and pass it into a `GreetingService`: creating a module from another module.


```reasonml
module ConsoleLogger = {
  let log = loggable => Js.Console.log(log);
};

module ConsoleGreeter = GreetingService(ConsoleLogger);

ConsoleGreeter.sayHello("World");
```

Note that we are free to choose how the `Logger` works – it only has to match the type signature. For example, you could implement an `ErrorLogger` instead:


```reasonml
module ErrorLogger = {
  let log = loggable => Js.Console.error(log);
};
```

Effectively we have taken away control from `GreetingService` to `Logger`. Now `GreetingService` describes what it wants to do, but doesn't actually implement how to do it. This is often referred to as _inversion of control_. 

When writing systems in a modern object-oriented style using programming languages such as Java or Kotlin, this technique is heavily used. Often you will read that you should:

> "prefer composition over inheritance"

...and DI is used to alongside composition to create cleaner more testable code. 

This pattern can be used in ReasonML (and OCaml), by using functors for dependency injection. Lets, go back to the original example to demonstrate this

## Updating the Original Service

Remember this? It's the MyService module from the first example.

```reasonml
module MyService = {
  let userAndComments = id => { 
     let user = UserApi.userById(id);
     switch(user) {
       | Some(user) => { 
       let comments = CommentsApi.fetchUserComments(id); 	
       Some({ user.name, comments });
       }
       | None => None
     }
   };
};
```

We can turn MyService into a _functor_ and construct an instance by passing module types for `UserApi` and `CommentsApi`. 

```reasonml
module MyService = (Users: UserApi, Comments: CommentsApi) => {
  let userAndComments = id => { 
    let user = Users.userById(id);
    switch(user) {
      | Some(user) => { 
      let comments = Comments.fetchUserComments(id); 	
      Some({ user.name, comments });
      }
     | None => None
    }
  };
};

```

Now, this module is easier to unit test. You can pass in stubbed versions of the modules in order to test the flow of the `userAndComments` function.


```reasonml
module StubUserApi = {
  let userById = id => switch(id) {
    | 1 => Some({ name: "Test User"})
    | _ => None
  };
};

module StubCommentsApi = {
  let fetchUserComments = id => 
    { title: "Test Comment", message: "Hello" };
};
```

Now it's easy to test the service using these simple stubbed modules:

```reasonml
module UnderTest = MyService(StubUserApi, StubCommentsApi);

let result = UnderTest.userAndComments(1); // Some({ ...
let anotherResult = Undertest.userAndComment(2); // None

```

I started using this technique [partway through building Bouken](https://github.com/RawToast/bouken). As previously stated, I initially imported modules from anywhere as that seemed to be the way things were done and worked for my frontend logic. Shortly afterwards, the game loop became unwieldy and needed breaking up.

Over time the more complex modules were broken down into smaller modules for easier testing and code management. This eventually turns into a dependency tree of module types. For example, the main game builder is defined as follows:

```reasonml
module CreateGame: (
  Types.GameLoop, 
  Types.World, 
  Types.WorldBuilder
) => (Types.Game) = (
    GL: Types.GameLoop, 
    W: Types.World, 
    WB: Types.WorldBuilder
  ) => { };
  
// Which is built from a
module CreateGameLoop = (
  Pos: Types.Positions, 
  EL: EnemyLoop
) => { };

module CreateEnemyLoop = (
  Pos: Types.Positions, 
  Places: Types.Places, 
  World: World
) => { };

```

Note that I chose to prefix the module names with `CreateX`, as they create a module that matches a module type name. I don't know what the preference is for calling these modules in Reason/OCaml, but it was a long-running debate in Java and C#...


So as demonstrated, you can use Reason's advanced module system for dependency injection. Whilst DI is often mentioned in object-oriented development – you do not need to use objects! Instead, use modules for organising your code and leave objects alone until you absolutely require open types or inheritance. In fact, DI is used in functional programming, often by using the _Reader Monad_ or some construct derived from the _Reader Monad_ such as _RIO_.

Based on a post [initially published at itazura.io](https://itazura.io/modules-for-dependency-injection/)