Practical structure for a Node.js Microservice
So, there are a ton of best practices, guides, blogposts about a project’s structure in Node.js, React, and basically every other framework.
Then why should I use this structure?
Well, you don’t have to, but I’ve stumbled across many structures but this specific one seems to be touching all my points for a good project structure:
Why do I need a good and stable structure at all?
That’s a great question. A robust project structure will help you and your team maintain reusable, clean and readable pieces of code, add new features without messing the existing code, and maintain the current codebase.
Here it is, partially!
A microservice, is a small unit of code that’s supposed to have a single purpose, and this structure will help you pretty much copy & paste services and let you write the logic itself whilst keeping all the goodies around it.
So let’s dive in! 🏊
1. Entry point & Wiring
So arguably the most important part of a service is it’s entry point. pretty much tells the story. I like to wire everything from database connection, monitoring, third party APIs etc. to basically show every framework-y part of the service, and initialize the server.
Next up is the framework folder
This folder contains every (possible) reusable code infrastructure that you might end up using in other microservices. Things like environment variables handling, initialization of database, monitoring connections, middlewares, metrics, authentication & more.
3. Server — Routes & Controllers
The controllers — may it be Express or any other framework, you’ll want to separate the “infra” routes from the API itself e.g isAlive and metrics routes.
Another issue is versioning your API — v1,v2, and so on to keep track of the latest API and backwards compatibility.
Services represent the core logic of the microservice, that the API Controllers use to manipulate data, and handle different use cases and external clients requests.
logic.ts in this example is mostly for cases where your microservice isn’t an API, but a worker or a cronjob of some kind, and this file will handle the main logic points of the service, while it uses other “services” to execute.
Services will also contain the third party clients, or clients that are communicating with other microservices.
The third category of services will be functions that are doing data manipulation, parsers, validations, etc.
The tests are pretty self-explanatory — have a tests directory up to the top, and keep a good separation between types of tests (unit, blackbox, integration) although it may have a shared package.json or configurations at the top tests folder.
Everything that you could use at the template of your services — Dockerfile, linters, configurations, etc. resides at the very top of the app.
Also, we haven’t talked about the types.ts file that handles all the types, enums, interfaces of the app, or does it? If the types.ts file gets bigger and bigger as the Microservice grows, you may want to have a separate types.ts file for the server folder, and services folder
Wrapping up 👋
Your project’s structure is not something that’ll stay static — the whole deal is to dynamically change and evolve as we grow, and understand which way works better for you and your team.
Here are a couple of tips that are related to your project’s structure after it’s development:
- Keep it consistent across your services.
- Try to make it simple, clean, and modular.
- Maintain and adjust your structure along the way.
- Update your tooling across your architecture.
- Be open-minded and try new styles to evolve.