Web development comparison: SpringBoot vs ExpressJS

A simple (and incomplete) comparison between web development using SpringBoot ecosystem and javascript with Express from a java developer point of view.

Goal of this article

This is a not so technical comparison (you can find more specific tech comparison elsewhere), i just want to outline how does it feel to develop web NodeJS applications when you are a Java developer by trade.
So please remember, this article is full of personal opinions.

Before starting, i must do a few premises:

  1. i’m a java software developer. I’ve been developing java based software for at least 15 years
  2. i’m currently learning web javascript development (i’ve been writing frontend’s javascript since 2007, but today server side javascript is another thing)

What am i trying to compare

I would like to highlight some of the differences that are perceived when developing an application based on the NodeJs/ExpressJS stack compared to the one based on SpringBoot.

Why am i comparing these

TLDR; after completing a contracted project, i decided to test another ecosystem to check if it can avoid some of Java mostly criticized points

One of my last customer was a company that was creating a crypto exchange (yes, quite a common thing in 2018, but not so common for an italian based company). They contracted me to join their teams (3 different developing teams) and help them build their platform. I mainly developed microservices for authorization and authentication, core transactions processing and other stuffs like customers’ KYC microservices and code libraries shared among different microservices.

That was a big and interesting project.

But during confrontations with other teams and people i often heard critics toward java web development in favor of python or GO. Some of the critics to Java that the other languages seemed to not suffer of were:

  1. Java code is verbose
  2. Everything is interface-tangled in Java
  3. Memory consumption of Java apps is overwhelming
  4. Same as 3. but talking about disk space
  5. Code development requires much time

(3. and 4. should be considered when spinning hundreds of docker based microservices but i think that this scale problem may be a non-sense since if you have hundreds of microservices running then you should already be earning a lot of money and you can afford also “expensive” instances to support memory-greedy java-applications).

I must be honest, sometimes i think that in 2019 all of the five points above are really reasonable critics so i wanted to experiment during this year i’m taking to develop my self-funded projects, to test some other technologies.

Since i needed to do web development and not necessarily microservices based projects, after a quick look (really quick) at GO i decided to not deepen that language. I think it is a great language (from what i read) but it’s not the right tool for my current projects.

So i looked at python and was starting python but i needed to develop some javascript code because i was going to use puppeteer as a fundamental component for PricePaladin (a tool for price tracking and monitoring) and after reviewing a very good comparison among languages i decided to go with NodeJS.

Comparison

Language

If you are a java developer you’ll find javascript is not so hard to be learned. You’ll surely freeze when you’ll deal with callbacks though. You’ll discover Promises and at the end of the day you’ll use the async-await syntactic sugar that brings everything back to normal.
That said javascript will sound a little bit weird but today’s javascript is definitely OK (as said before, it’s not <= 2015’s javascript). It is simple, quite powerful and concise.
I leave to you all the observations regarding dynamic typing that in my opinion isn’t a big deal.

NodeJS single-threaded

Ok, this is one of the most “shocking” thing for a Java developer. But the shock vanishes after a few moments. You should consider that everything runs on a single thread (on any java web app you have multiple threads) and that callback functions (asynchronous functions) are queued on a queue and executed when it is better to execute them but all the stuffs run on a single thread (the key to NodeJS’ speed and low memory consumption). From a java (but also non-java) developer point of view this means:

  1. don’t run cpu-time intensive code or everything will wait for cpu to be free before executing a new queued function
  2. if something goes wrong and NodeJS crashes then everything crashes: in the case of a web application serving multiple concurrent requests, all the requests crash. You don’t have the isolation that you have with a java web application

The js equivalent to SpringBoot ecosystem: ExpressJS, PassportJS, Sequelize

If we limit the comparison to the MVC web application part only, SpringBoot is definitely fabulous: light and fast, complete and extremely configurable. From this point of view a Java developer does not feel any major shortcomings compared to what the counterpart of ExpressJS offers.

ExpressJS also offers the same potential. One thing that depending on personal taste can be more appreciated or not is that the routing instead of being defined at the level of java annotation is definable at the level of routing files.

More generally, SpringBoot indicates a very precise way on how to organize the code in packages (models, services, controllers) while in Expressjs context there are no such guidelines. Nevertheless, it is possible to re-apply a similar code structure and there are often projects where the code is structured in a similar way to a SpringBoot project.

For the authentication part, SpringSecurity is the “ultimate tool”…but it is also a “beast to tame” if used for some particularly complex cases. The javascript counterpart is PassportJS that is very powerful although less structured and mature. Nevertheless, you have the feeling that it is able to handle the same cases and conditions of SpringSecurity. In any case, the development of common authentication mechanisms such as authentication by JWT tokens or other common auths mechanisms are also widely supported by this framework. The maturity of the SpringSecurity code is still to be achieved by PassportJS, but the perception i have had is that 80% of the features offered by SpringSecurity are implemented in PassportJS too, in a way that is sometimes simpler.

ORM in java has always been, from my point of view, the Achilles heel of java applications. The java standard is roughly Hibernate (despite the various alternatives, however widespread, such as Jooq and MyBatis), while for the JS world related to relational dbs, the most popular library is Sequelize. I’ll dedicate a specific chapter to the comparison between the two.

Hibernate vs Sequelize

TLDR; Hibernate is still the most complete, mature and versatile solution…at a very high cost! Sequelize may cover 90% of your use cases.

I don’t hate Hibernate but surely i don’t love it. It’s over engineered, slow and complicate. It’s like an elephant. And it can do anything with any supported db. On the contrary Sequelize is small and simple but can’t manage all the cases.

Some of the things i discovered through the use of Sequelize:

  • you’ll have a not so hard but definitely not so easy time trying to force snake-case for table’s fields. You can manually specify them one by one (but that is otrageous) or you can use some hack to convert names to snake case. That is a simple solution but it has drawback that it will break the migration command line tool. Anyhow it is unacceptable that all the requests to introduce flexibility in naming convention definition were discarded and ignored.
  • it doesn’t fully support composite key: as clearly stated here, “While it is possible to create composite primary keys in Sequelize […] Sequelize doesn’t currently support composite foreign keys, so there is no way to reference a model/table which has composite primary keys.”. Again, from my point of view this is immaturity.
  • you’ll have to do some nice trick to manage date’s fields due to timezone interpretation (really…that was really unexpected)
  • when adding instances to the fields that are related among two entities (suche as Book and Author for example), entities are immediately saved. This is not a big deal but shows that Sequelize is far less sophisticated than Hibernate which has internal mechanisms to decide when to flush data.

There are also some things that i love in Sequelize like the easiness to create queries at runtime (that’s a breeze, you can compose a json object at runtime and pass it to the query engine). Try to do it creating a JPQL query or consider how overcomplicated it is to do with Criterias. Honestly, using Hibernate and SpringData JPA trying to dynamically filter a query at runtime by some fields is a pain in the ass while it is a really easy task (as it should be with any framework/language) to do in Sequelize.

Another aspect where Sequelize shines respect to Hibernate is when you hit some hard cases and need to do native query: both of them let you execute native queries but honestly it is a lot simpler to convert the results to their models in Sequelize than in SpringData JPA/Hibernate.

And i’m not talking about startup time: introducing hibernate adds seconds to boot time, while Sequelize is quite immediate.

As a final consideration, it results quite obvious that Sequelize is a lot more immature than Hibernate, that Hibernate is capable of doing anything while Sequelize is a lot less abstract and engineered and definitely easier to work with but covers only 90% of the cases: this can be a great advantage especially when you own the db schema and don’t have to adapt to legacy dbs and if you don’t plan to migrate db engine one day (to be honest i’ve seen only one case of db migration in all my life and it was when two banks decided to merge and so it was decided to keep only one IT system and rewrite the code of the abandoned one to the other platform: anyhow there were thousands of store procedures to be rewritten so code portability, in my opinion when talking about ORMs, is a useless feature).

Final considerations

I’m currently using the described javascript stack for my current projects and so far i’m very happy with it. PricePaladin (a tool for price tracking and monitoring) was built using the above mentioned stack and is currently deployed to an inexpensive server due to its low memory footprint.

Working with javascript bring you to a greater level of simplicity. It is ideal for scripting and standard web development but i wouldn’t use it for complex project (except for small dedicated and isolated microservices) nor i would use for numeric applications or applications where numbers counts (like a crypto exchange where Java and its BigDecimal class are perfect for that scope).

Ultimately, the general feeling i have when developing server-side javascript is that everything is a bit simpler and less cumbersome than an equivalent Java development, although i have a strong perception that there is a lack of stability and maturity in respect to the libraries offered in Java (a lack that is real only if certain libraries are really needed for the specific project otherwise it makes no sense).

Another perception is that the development cycle of javascript is a 20% faster: by this i mean that due to more verbose code, more engineered structure of java applications that adhere to classic guidelines and time spent rebuilding the code, you spend a lot more time developing the same features in java than in javascript.

Therefore, where the application does not provide calculations or blocking processing and concerned the development of a classic small web application, i would almost certainly opt for the development with the javascript stack described, while in other cases i would base the application on the SpringBoot stack that on the long run, in my opinion, offers greater maintainability.

Alberto Plebani Written by:

Alberto Plebani is an italian software engineer currently working as a freelance developer. Happy father, happy bushcrafter!

Find him on LinkedIn
comments powered by Disqus