At Ohpen our main stack is .Net and we love it :) ! It serves well to create performant web apis with many endpoints. When I started there we were developing serverless apis (apis supported by Apigateway and Lambda in AWS) and some migration was required. At that point, discussions started about different languages and how they compare.
In my case, I strongly advocate for interpreted languages like NodeJs and Python. They are easy to start with and are lightweight by definition (greatly reducing cold-starts). This is great but the team wasn’t sure that NodeJs had the capabilities from .Net in terms of structuring the code. In fact, one of our main concerns was how to manage dependency-injection with NodeJs (even some of us said that it couldn’t be done :O! Fools …). Here we have a list of the most common sentences that were said and my response to them:
NodeJs allows you to sum bananas with apples:
So? What is the issue there? Javascript is not a strongly typed language like C#. In my opinion, this is beneficial when you are isolating (and deploying) your code as functions. Also, as a developer you should properly test your code so you are sure that you are not “summing bananas with apples”!
When the complexity of the project increases, NodeJs fails to provide structure:
Hmmm, … maybe. But, do we need “complex” projects now that we are embracing serverless? Why do we need our projects to be complex? In my opinion, writting our bussiness logic as functions should be the least resistance path with the least amount of code.
OOP is the way to go. Functional programming doesn’t allow proper dependency injection… because there is only functions, so no abstractions.
First, although NodeJs has a strong functional component, you can still code using OOP practices. In fact, you can go with both approaches! And my favorite is FP since allows for the least amount of code.
At that point I realized that we were so used to only work with OOP in .Net and the team saw javascript+FP (Functional-Programming) as something to be scared about. So, I decided to prove myself that NodeJs was (more than) capable of managing dependency injection as good as .Net.
The comparison
I decided to build an API in three different ways:
Asp.Net core API with C# using OOP.
Code the same API using NodeJs+Express and OOP.
Again, using NodeJs+Express and FP.
Asp.Net core
The project in .Net is quite straight-forward. Has a DotnetController.cs (https://github.com/EduardBargues/content-functional-dependency-injection-in-nodejs/blob/main/dotnet/DotnetController.cs) with a couple of dependencies injected using the constructor.
DotnetController.cs: IService as dependency injected in the constructor. Standard approach in Asp.Net.
The same happens for the service. It has some dependencies that are injected in the constructor and Asp.Net core takes care to manage them once registered in your Startup.cs (https://github.com/EduardBargues/content-functional-dependency-injection-in-nodejs/blob/main/dotnet/Startup.cs) class.
Startup.cs: Registers the necessary abstractions+classes in the ConfigureServices method.
Asp.Net core abstracts the burden to instantiate and inject implementations on each constructor.
OOP NodeJs
There are many equivalent packages in NodeJs equivalent to Asp.Net but I decided to go with the simplest approach in NodeJs. This is how it looks:
The closest thing to the controller in Express is the index.js (https://github.com/EduardBargues/content-functional-dependency-injection-in-nodejs/blob/main/nodejs-di/index.js) file. Here you see a single endpoint routed to the Api object using the getUser method.
index.js: Express app and Api injection using constructor.
For the sake of completeness, let me show you also the api.js (https://github.com/EduardBargues/content-functional-dependency-injection-in-nodejs/blob/main/nodejs-di/api.js) and service.js (https://github.com/EduardBargues/content-functional-dependency-injection-in-nodejs/blob/main/nodejs-di/service.js) files.
api.js: Api class with a constructor where everything is initialized.
service.js: Service class with a constructor that requires some dependencies.
As you can see, the Api class is playing the role of the Startup.cs from Asp.Net core. In the Api constructor, everything is injected and all dependencies are setup. Then each class receives it’s dependencies in the constructor and uses them inside it’s methods.
FP NodeJs
So far, we’ve seen OOP using both Asp.Net core and NodeJs, but what about FP? Let’s give it a go!
The approach I want to follow is:
Each *.js file will export a single function that receives some dependencies. This function, when invoked, will provide the actual set of functions that contain the implementation.
Okay, that sounds weird … Let’s see it with examples :) ! First, the index.js (https://github.com/EduardBargues/content-functional-dependency-injection-in-nodejs/blob/main/nodejs-functional-di/index.js) hasn’t changed much. The only important difference is how the api.js module is consumed. Notice that no constructor is being called. Instead, the api.js module exposes a function and it is simply executed to obtain an json object with a getUserHandler method.
FP index.js: There is no longer a constructor being called.
The api.js file (https://github.com/EduardBargues/content-functional-dependency-injection-in-nodejs/blob/main/nodejs-functional-di/api.js) only exposes a single function with no dependencies (no inputs) and returns a specific implementation in the form of a json object with one method getUserHandler.
FP api.js: A single “api” function exposes the actual api implementation.
The service.js (https://github.com/EduardBargues/content-functional-dependency-injection-in-nodejs/blob/main/nodejs-functional-di/service.js) file follows the same approach: one single function that receives it’s dependencies and returns the set of functions with the actual implementation.
FP service.js: receives repository, emailSender and log objects as dependencies.
Conclusions
So, we developed a REST api following both OOP and FP with C# and NodeJs. If you check the repository and the whole code, you might (would love you don’t :P!) agree with me on the following conclusions:
C# vs NodeJs
NodeJs/Javascript allows to add the same business value with less lines of code compared to .Net/C#.
C# includes interfaces which is something interpreted based languages lack. This gives explicit consistency in large projects where many developers are involved since forces them to “code against interfaces, not implementations”.
C#, together with Asp.Net core, gives a great out of the box solution for dependency injection in OOP but lacks the equivalent on FP (as far as I know).
NodeJs is much more flexible. This can be seen as an advantage and disadvantage since due to the dynamic type nature of javascript one can do great things or break them easily.
OOP vs FP
OOP is the way most developers are comfortable with.
In my opinion, OOP comes with a lot of problems and provides great solutions for each one of them. But, wouldn’t be better to simply not having those problems :)?
FP pushes the “less-code” paradigm and allows to have the same functional api with less lines of code.
FP allows you to focus on what really matter. Also, provides an (not intuitive at first) easy way to make your code expressive and elegant.
Source: Medium - Eduard Bargues
The Tech Platform
Commentaires