Thinking out architecture for Node.js microservices

calendar_today Posted 1 year ago · 4 minute-read · Technology

I’ve used Node and Express a lot in the past few years, espe­cial­ly when build­ing self-con­tained ser­vices that I can lat­er inte­grate with larg­er appli­ca­tions made in oth­er lan­guages.

Until not too long ago, I did­n’t real­ly think of a Node project struc­ture for my apps, I just did every­thing inside of the Express call­back for each route. The tasks my micro-ser­vices had to do weren’t com­plex at all, so I did­n’t real­ly have a hard time main­tain­ing them (swap­ping libraries in and out, chang­ing depen­den­cies and con­fig­u­ra­tion, mod­i­fy­ing busi­ness log­ic). It was just ‘all there’.

I’ve recent­ly start­ed a new project that I’m build­ing pret­ty much all in Node and React, in order to achieve a 100% JavaScript code­base, which requires a much more robust app struc­ture in order to make devel­op­ment more pro­duc­tive and improve main­tain­abil­i­ty and scal­a­bil­i­ty.

This meant a few things:

  • The app struc­ture has to be lay­ered. This ensures that the busi­ness log­ic is prop­er­ly sep­a­rat­ed from oth­er log­ic, for exam­ple, data access log­ic. Con­tin­ue read­ing to see what lay­ers I chose.

  • Depen­den­cies have to be inject­ed into each and every mod­ule instead of using Node’s famous “require()” every­where. This makes it easy to swap depen­den­cies, change imple­men­ta­tions, and pre-con­fig­ure them before the mod­ule gets to use them. Depen­den­cy injec­tion can be achieved with the fac­to­ry pat­tern. This would also make every app mod­ule eas­i­ly testable.

  • Express should­n’t have all the con­trol. Express is good at receiv­ing and serv­ing HTTP requests, noth­ing more. Your app is what’s going to be good at car­ry­ing out your busi­ness-relat­ed tasks. To achieve this, it’s impor­tant that we use inter­face adapters.

  • Use inter­face adapters to inter­face with exter­nal depen­den­cies. In the future, we might not like how a cer­tain depen­den­cy car­ries out some task, so we’ll want to cre­ate a cus­tom imple­men­ta­tion our­selves. With adapters, we can change our depen­den­cies and/or imple­men­ta­tions with­out affect­ing the rest of our app’s code.

  • Pre­fer fat­ty enti­ties to dumb, thin enti­ties. Enti­ties (like Shop­ping Cart, User, Prod­uct etc.) should know best how to be them­selves. Fat­ty enti­ties are those that instead of just hav­ing data­base fields in them, get­ters and set­ters, they also include val­i­da­tion log­ic (busi­ness rules) relat­ed to the enti­ty. This helps keep our busi­ness log­ic con­tained in our enti­ty instead of it being all over the place.

  • Use func­tions instead of class­es. Node devel­op­ment shines most­ly when using a func­tion­al approach to build an appli­ca­tion. This is most­ly a mat­ter of pref­er­ence, and this is my pref­er­ence.

Bob Mar­t­in’s Clean Archi­tec­ture is what inspired me to build my Node apps this way:

Image result for clean architecture
“Clean Archi­tec­ture” by Robert C. Mar­tin

Clean Archi­tec­ture is a way to sep­a­rate con­cerns that makes your apps scal­able, main­tain­able and testable.

In Clean Archi­tec­ture, we have:

  • Enti­ties, that include busi­ness rules relat­ed to the enti­ty.
  • Use cas­es, appli­ca­tion-spe­cif­ic busi­ness rules. What your appli­ca­tion can do. i.e. list prod­ucts, add item to shop­ping cart, apply a dis­count.
  • Inter­face adapters, to inter­face with our depen­den­cies to not become tight­ly-cou­pled to them, and adapt the result­ing data into a for­mat that our appli­ca­tion can best under­stand. i.e. a Mon­goDB inter­face adapter imple­ment­ed as repos­i­to­ries.
  • Exter­nal dri­vers and frame­works. A REST API client, a Mon­goDB Node.js dri­ver, Express…

The depen­den­cy flow goes from exter­nal dri­vers to enti­ties, as you can see in the chart — this means that inner lay­ers can­not depend on out­er lay­ers.

It also means that mod­i­fi­ca­tions made in the inner lay­ers will “rip­ple” back to the out­er lay­ers. This is why the busi­ness rules are in the two inner­most cir­cles — they are less like­ly to change over time.

In my next post on this top­ic, I’ll go into more detail about the tech­ni­cal aspects of imple­ment­ing clean archi­tec­ture in Node.js, what project struc­ture I fol­low, with code exam­ples and prob­a­bly a GitHub com­pan­ion repo.

Just want­ed to share where my mind­set is at right now! 😊

– Kedi