GraphQL server using Clean Architecture and Functional Programming — Part 3
This article is the last part of 3 articles.
GraphQL server using Clean Architecture and Functional Programming — Part 1
Part 1 — GraphQL
GraphQL server using Clean Architecture and Functional Programming — Part 2
Part 2 — Clean Architecture
In Part 2 of this article; we’ve written a GraphQL server implemented using the Clean Architecture principles. We have also seen that Clean Architecture can be a bit verbose.
Let’s now rewrite our project using Functional Programming and see what are the benefits.
Why Functional Programming (FP)?
Most of us know what is Functional Programming: It’s an approach of programming that consists of using only functions.
But why? Classes are great and very common. So, why use only functions?
To understand what is a Pure function, I like this explanation by Tommi Kaikkonen
We define a pure function by two criteria:
1. Given the same arguments, the return value will always be the same.
2. Evaluating the function doesn’t result in observable side effects.
In the Clean Architecture, we can expect the functions of the outer layer to be impure as it has, by design, side effects like DB insertion or WebSocket interactions.
Also, as the business logic should be predictable, so we can expect the functions of the inner layer as pure.
Stateless & Immutability
The goal of a class is to retain a state.
You can call a method that will alter this state. This is very useful, but it reduces maintainability.
Would you be able to understand what a method is doing if this one depends on the state which is mutable?
How many times did we write unit tests which actually required calling other methods to work?
How would you unit test this class without having to change signIn method declaration?
You would probably be tempted to pass signInAttempts in the constructor. If so, your class will be stateless and can be converted into a function.
Now, your function is stateless and autonomous. It’s now really easy to write really unit tests.
You can see how the function needs its dependency to work. This means in this state, the dependencies have to be passed from the Controller to the UseCase. This is not that isolated: let’s fix this!
Functions dependency injection
The constructor of a class can be seen as a function.
So, we can simulate the same by having:
- a first function, acting like a constructor, receiving dependencies and returning
- a second function, which is your real function
The function buildSignInUseCase() is used as a constructor that will build an implementation of ISignInUseCase.
The dependencies of your function can be passed using a constructor function to let your function declaration “clean”.
In this example, signInAttempts() may be passed as the function parameter or could be “verified” by a dedicated function. So, let’s do it.
Classes allow implementing multiple interfaces without breaking the Interface Segregation Principle.
But then, a method can call another one.
Again, how could you predict the method result if it depends on another method on which you don’t have control?
In this example, you can’t unit test the method signIn without testing verifyAttemptsCount too.
Now, the function buildSignInUseCase() has no control on the verifySignInAttempts() as it can be replaced by its interfqce IVerifySignInAttempts. In other words, buildSignInUseCase() can be really unit tested as verifySignInAttempts can be mocked.
Let’s take again our example of GraphQL server implemented using Clean Architecture.
The useCase becomes a simple function like this:
Our service was a class with different methods.
It is now 2 distinct functions:
Functional Programming has very good aspects. Correctly used, it can be very easier to unit test, so it could be a good choice if you’re looking for TDD.
In a meantime, Functional Programming is not the most common way to develop a project, and in some cases, it can complexify your code (which was the exact opposite of the intention).
So, should you use Functional Programming?
I recommend testing it first; see with your team if it would fit or not.
Don’t forget you can mix Oriented Object Programming with Functional Programming. For example, you could have only the UseCase written with only Pure functions; then keep a class for the Service.
It’s up to you to determine your project needs and if Functional Programming could bring benefits or not.