In the summer of 2015 I started building a web application for a friend who owns a small courier company, named Smart Express.
Smart Express picks up packages at a specified locations and delivers them to other locations. Additional services and options aside, prices are based on distance, weight and time window.
So given the right tools, customers can easily calculate what the fee will be upfront.
My friend had a web application that enabled customers to book the pickup and delivery of packages online. However, this software – written in PHP – was on the edge of collapsing: it had become so slow that customers preferred to use the phone for their orders, or take their business elsewhere.
To make matters worse, the software got hacked and was infected by a virus:
To lend my friend a hand I decided to build a new application for Smart Express that would be fast, responsive and have a modern look and feel. A fun challenge and a good learning experience for me.
In August 2016 the first version of the new site went live.
The philosophy of the site is transparency and simplicity. Visitors find a calculator directly on the homepage to calculate the price of a delivery. If they like the price, they can register or login and book.
The only thing site visitors have to do is to provide an estimated weight of the package and enter two postal codes The distance (and fee) is then calculated between 3 points: the pickup point (‘ophaallocatie’ in graphic below), the delivery point (‘bezorglocatie’) and a reference point (‘referentielocatie’). The latter is the center of the region of Leiden because Smart Express is based in Leiden.
Sure, sometimes the courier car is further away from the next pickup point, sometimes it is closer. On average it is in the center.
Distance = d1 + d2 + d3
To implement the web application and to learn something new, I chose technologies that were quite new at that time. A walkthrough? Here we go:
This framework was hot in 2015, but just two years later it has been superseded by Angular 2+. And now there are other good alternatives like Polymer, React, Vue.js, Aurelia and more.
So far I have had no time to migrate the AngularJS client to a more recent framework. But, on a personal note, I have to say that I still don’t see what’s wrong with the old AngularJS stuff.
Personally, I also do favor ECMAScript 6+ over TypeScript.
Bootstrap enabled me to quickly create a modern and responsive layout that has an acceptable design. If someday I renew the frontend, then I probably will consider Google’s Material Design components, like: material.angular.io.
Polymer gives Material Design components out of the box.
First versions of the server side code were based on Dropwizard. But I replaced that with Spring Boot (which implements some features of Dropwizard). The server side Java application exposes a couple of REST-ful services and is packaged – together with a Tomcat server – into an executable JAR file.
This makes deployment quite simple for me: just run the JAR file. I have no need for a Docker container.
JSON Web Token
For security I use JSON Web Token (JWT). REST-services are stateless by definition. There is no server side session. JWT makes it possible to send a token back to the client after successful login. This token is an encrypted JSON string that contains info like the roles of the user, and more.
The token is stored in the client and sent as request header with each service call. The token can have an expiration timestamp.
Finally, the customer and booking data is persisted in a NoSQL database, namely Neo4J. This is a graph database that can handle a lot of data. It focuses on relationships, has a cool query language, named Cypher, and can be installed very easily by just unpacking a ZIP file.
Spring Data Neo4J makes it even more easy to store and retrieve data into and from the database.
This tech stack, the above mentioned, enabled me to create a web application with a minimal amount of available time.
However, more time than expected I had to spend on the client side validation and all kind of user messages. The frontend validators have been implemented as Angular directives. They are reusable as HTML attribute, which in itself is great.
Also the backend price calculation service was more complex than I expected in advance. Distance and weight are variables of the price formula, but there are quite some constants too, like additions based on weight category above a distance threshold.
I chose to implement the constants as configuration settings. The idea is that an administration web page will offer self service management of the application configuration.
Okay, now that the included technologies are covered, let’s take a quick stroll through the APIs and libraries involved?
Google Maps API
This API calculates the distances in km.
Used to validate postal codes and lookup address info. The postal code is a core element and Achilles heel, because the distance and price calculation depend on it. In production it happened that Google Maps could not find the postal code or found a wrong address (for instance in USA instead of The Netherlands).
A solution is to add more address elements to Google Maps API like city and country to prevent mistakes. Still the chance remains that Google Maps API can not find the address due to some mysterious quirk. In that case, the customer has the option to continue the booking without price calculation. The invoice will be created manually.
Java Mail API
To mail the bill of lading (vrachtbrief) to the customer and to the back office. The couriers receive the booking through email on their mobile phones.
The messages in the email are created by means of FreeMarker templates.
The customer receives the bill of lading as PDF file, attached to the email. The customer attaches the PDF to the package before it is picked up by the courier.
Messages are put in a queue and picked up asynchronously by Spring Scheduling in order to mail them.
To protect against robots.
Good to go
We’re almost there. The website and Angular module are hosted at versio.nl. The Java backend application is deployed on a RaspberryPI 2 and the database runs on a RaspberryPI 3.
Both machines have 1 GB memory. Surprisingly the app is quite fast. But I have to admit that I spent quite some time on performance optimization to squeeze the maximum speed out of it.
There have been not many incidents and the costs are very low. However in order to improve availability, the backend will be deployed in the cloud later. After all, it is not quite professional when the RaspberryPI’s are accidentally unplugged at the same moment a new customer wants to register…
Something we need to address, because unlike Smart Express, Murphy is never far away.
Check it out? -> smart-express.nl