Coding a solution is a little bit like cooking, you have to add the right ingredients (and many recipes require many), also you need to use them in the right amount, and also anybody would agree that using the right time for cooking is essential, now how about the way in which you cook?
I remember my grandmother used to criticize a lot to our neighbor, which was a woman that use to cook in very nasty ways, by that I mean like kneading the pizza by passing it through his arms including her very hairy souks…. yes, even worse, you would need to see how it looked her kitchen which was visible through the window at the second floor. Quite a traumatic thing for a little kid if you can imagine, flour thrown everywhere, broken eggs in the floor and even some dirty diapers in her sofa. I really don’t know what kind of morbidly curious kid I was to watch all that sad spectacle through the window and not going to the nearest bathroom to throw up my breakfast. And then the smell, Oh my goodness, the smell…
guts, human brains, anyone?
Well, this is not an article that I had planned for the deep web, so probably it would be better idea not to give you more details about my neighbor. What I can definitely share with you though, is that I have met many developers that when writing their code it would seem like if they absolutely would be hating their creation and btw their lives. Look what I am talking about…
Interview Question: What this code actually do?
you have 30 seconds and I am watching you as some sort of strange insect…
No doubt, books have been written about the topic of Clean Code, but even when many agree to the basic same principles of separation of concerns, have everything well commented, having code divided in nice chunks that do a single thing; there are some other areas where people doesn’t seem to agree upon that easily, for example, should we use linters and formatters all the time? should we rely and depend on them? How to write functional code in a way that is easy to read? What about OO code? Do we want to write a file in a way and another file use some different indentation rules or casing for the variables? How important is to be consistent and predictable when writing a software solution?
The Answer is ABSOLUTELY NOT! You can write the way you want!, change the things you want!, make it as complex as possible as long as it runs fast enough and it compiles! there is absolutely no problem! right? Well, let’s take a closer look at how your co-workers and ultimately you will look when working with code like that in 6 months…
I want my mommy!!!!
So here is my humble opinion on how code should be organized in a Node/Express project and how I normally do it. This is just a first take, almost a draft, so please don’t be too hard in the comments, call it, the ‘beta’ version if you will But here are a few things I have found…
Structure for a Medium Size Project
Following the principles of “Divide and Conquer” it is important to divide the project
in discrete parts from the beginning, the approach that has worked the best for me
with express is to have a Server file, an App file called by the Server which in turn
calls a series of routes (in its respective folder) which in turn calls controllers which
in turn call services. There is also a specialized folder to configure imported
middlewares or create new ones as needed.
Other Folders as Model folders are necessary which should include enums,
interfaces and model classes including DTO’s as well.
The DTO and Mapping pattern is widely used in the .NET world and I believe it has a
very solid case in Express as we don’t always want to return entities to our clients
and at the same time we want to have perfectly documented the structures of all
messages and that can later be included in a Swagger documentation.
In a bigger project other rules apply as microservices may be necessary and scaling
is a crucial factor in order to provide support for many users.
This is the structure I recommend for a monolithic Express Application,
Microservices requires a whole different approach and discussion that goes
beyond the scope of this article.
App Class Separated from Server
Separation of Concerns is maybe one of the most
important patterns in software development.
When doing a Node/Express application it is very
common to see the application (routes, middlewares,
etc.) and server connection configured within the same
file. And in fact, as our API grows, we find that the
application class and also the server logic tend to grow
in complexity. It is a better practice to keep them apart.
Keep Middleware config away from App Class
Another common trait in Node/Express developers is to
configure all middlewares within the main application
class. Again, this works well for small projects, but as
the project starts to grow, and new components need
to be added, it is important not only to control where
they are declared but in which order, therefore it makes
sense to put them in their own singleton classes (for
most cases) and call them from there, that way the
application class can focus more in the order in which
they are declared and we eliminate clutter, pretty much
like using extension methods for Startup.cs in .NET Core.
Also this way you can set which middlewares execute
before and after the Controller Action call in an easier way.
Keep Routes away from App Class
It is also truth that the same thing happens with routes, they should be separated from app code and declarated in their respective files. Every entity should get its own route declaration file, I usually wrap each one into a singleton (as a first step for a small to mid size app), in this way, it is easier to move later into microservices if that was the case or add more complexity without cludging the main application class.
Please notice that the call to the controller method doesn’t have parenthesis at the end, this is because we will do some optimizations in the controller constructor.
Optimizing Controller Calls
Again you will find divisions in code making the code both, predictable and easier to read and maintain.
Please notice the use of custom error/http codes middleware which allows to write cleaner code, very similar to the one in ASP.NET Core, as it is easier to say next(ApiMessage.Ok) than passing a 200 http code that if not cryptic for the one that knows, it adds up to makes things less readable, and in code these kind of things pile up real quick, so why not keep them at a minimum?
Middlewares and Validators
Define your own middlewares can be tedious and I am an enemy of reinventing the wheel, however in certain cases creating a middleware is necessary to make things work exactly the way you (or even better, the client) expect it. This is a very simple code that uses a simple error model to make errors more readable. I have quite a few, but this is safe to share due to NDA’s and other legal limitations. Other middlewares use validators like the extract from the class below.
Simple code to call and retrieve Service Data
It’s been well said that controller logic should never handle what should be done in a service, and here is precisely where creating and configuring middlewares pays off, the resulting code is clean and simple.
There will be some cases where data extracted from the
http body request needs extensive validation and even
using validation classes may be not enough to keep the code short and clean, but I believe the developer should do everything in his power to keep code clean and responsabilities clearly divided so that the code is short and readable. Also without falling in the opposite effect of creating neverending call stacks that are as hard to debug as a 3k lines of code file is.
Somebody said that a 300 lines file is the maximum to be considered clean code if all other good practices are followed. I believe based in my experience that the number should be around 500 for complex controllers.
Service Code: Accessing SQL Server
And then, there is this belief that Node and Express are best suited to be used with MongoDB, Firebase or even MySQL, but that SQL Server doesn’t play well with Node (I heard of it and didn’t believe it). The truth resulted to be somewhere in the middle.
The code at the right uses the mssql/msnodesqlv8 driver, definitely a very degraded experience from using Sequelize, but for some reason I am not allowed to mention we ended up using this driver. In my experience this driver lacks many features, but allows for other nice ones like passing T-SQL directly to the server and get it executed, in favor of it I can say that if you need a quick solution it works, although not always as advertised.
There are things I quite honestly don’t like of this code, and I added it here because it is part of the same project I am using to illustrate the rest of this booklet, with more time I would add a decent logging system and would probably change to Sequelize with a better ORM support.
Final Result? Delicious and Componentized Ravioli! 😉