Technological evolution of the OV-fiets platform
Federico Navarro Polo
Read more by Federico Navarro Polo
The OV-fiets (public transport bike) is part of the NS (Dutch Railways) and facilitates the traveler’s last mile. Before the coronavirus, the bikes were used by more than half a million people. The digital systems couldn’t keep up with the OV-fiets’ exponential growth over the past few years. So, NS teamed up with INFO to develop a new digital platform.
With this sustainable and future-proof digital solution, we enabled NS to raise their game in the bike-sharing market. In this blog, we share the technical choices and architectural decisions we made to build this platform.
The OV-fiets platform was recently nominated for a Dutch Interactive Award (DIA) in the category Digital Transformation.
The starting point of our journey
The original NS OV-fiets platform consisted of multiple services independently managed by different parties. It was troublesome to work with and didn’t offer NS an easy way to extract the information they needed from it. INFO stepped in to help identify the pain points, guide the uniformisation of the platform and enhance its user experience for NS employees. The main goal was to provide the right information in the right way to help NS manage the OV-fiets platform.
Technical vision for a bright new future
To transform the original platform, we chose an architectural approach based on microservices. Let’s look at the main reasons that steered this decision:
- Simplify the architecture, by defining clear boundaries, responsibilities, and interactions between services
- Increase reliability and fault tolerance, by isolating services in a way that the platform keeps performing even when some services are unavailable
- Improve scalability, by focusing on services that might need additional resources or experience more growth over time
- Faster delivery time, by combining agile development with smaller, more controlled deployments, therefore reducing risks
- Technology flexibility, by allowing us to adopt different technology stacks individually per microservice.
The new digital platform was designed with a clear goal in mind: to keep functionality providers independent of clients. This distinction enabled us to apply best practices on a case by case basis, while giving us great flexibility in terms of connectivity.
Change, but don’t break anything
Once the goal was clear and the technical vision was decided, we had to take another challenge into account: backwards compatibility. Migrating such a big platform, with its complexities and dependent processes and interactions, to a microservice-based architecture is not an easy feat. Especially when not all parties have the need or the resources to go along at the same pace. This meant we had to ensure that after the migration data and flows in the new platform were compliant and behaved the same as before.
When it comes to backwards compatibility, it’s extremely important to be critical and analytical. We challenge existing procedures and agree on what should remain and what can be safely changed. This helps ensuring the migration only carries what’s needed, without unnecessary baggage. But while that helps, it’s not enough.
In situations where the system needs to act exactly the same as before, the approach depends on the components’ lifespan. For components with a long expected lifespan, the creation of services that act as interfaces or adapters is the way to go. For components that can be phased out quickly, techniques like API versioning and feature switches help develop and improve the platform while allowing the coexistence of elements soon to be phased out.
More services, more communication, more challenges
Over time, we added new services to the platform. The evolution of the domain model introduced new challenges as more and more unplanned interactions among components started to pop up. When working with a modifying API based on microservices, it’s important to determine whether all side effects of an API call can be processed by one unique module or depend on others, and how that fits within non-functional requirements like the response time. The challenge arises when you need to respond both quickly and certain, while dealing with data that might be inconsistent. You can’t eat the cake while keeping it at the same time; but you can slice it. API operations might need to be split further into tasks that rely on 100% guaranteed data and tasks that can be delayed in time without impacting user experience.
The OV-fiets smart lock
One significant development in recent years was the introduction of OV-fiets Connect (OVFC), a completely new way to provide bike renting services to customers. It allows customers to rent a bike by putting their own OV chipcard on the lock, removing the need a key. This new product introduces new ways of interacting with the platform, while also introducing numerous variations to many processes compared to the traditional OV-fiets. As a result, it’s justifiable to dedicate new components to take care of these new processes. However, while the user experience is different, the interface between the hardware and the platform and the technical implementation is different, from a domain point of view, there are still a lot of similarities between the legacy product and the OVFC product. After all, a bike is a bike.
For the NS employee, there was the need to aggregate data from those two heterogeneous sources and present them in the same familiar way to allow retrieving information and be able to manage the fleet and service the customer.
For the NS customer, there was the need to use both services in a fully compatible way without making customer’s life unnecessarily difficult.
As developers, we were challenged to keep data consistent between different modules, without impacting latency time of operations.
This combined was the reason to introduce event streaming in the architecture. This way, any number of microservices can simply subscribe to specific events in a given domain and react accordingly and independently, while ensuring the data is consistent.
Was a microservices architecture the best choice?
We believe a microservices architecture was the best choice. Overall, we’ve seen positive results. We’ve definitely benefited from numerous advantages advocated by microservice architectures:
- We’re able to scale easily and individually per environment (DTAP) and per microservice
- Deployment cycles have diminished drastically
- We can dedicate more resources individually per microservice, allowing us to manage resources more efficiently
- Reliability has increased, even with the continuous growth of NS OV-fiets transactions
- We leverage different technologies based on the needs of each microservice. While we favor the Java & Spring Boot stack, we also use standard Java, React, .NET and even standard C, where these technologies are a better fit.
On the other hand, there are also aspects that could increase overhead significantly. For instance, looking at team size, this approach introduces extra complexity which does not pay off in terms of splitting teams per microservice.
Future-proof digital landscape
The digital landscape of NS OV-fiets is now modern, scalable, and future-proof. The digital platform enabled OV-fiets to grow from 4.2 to 5.2 million rides in 2019 and together we ensure that future demand is facilitated smoothly.
“With this digital transformation, we have left legacy systems behind and transformed into a future-proof platform. Now we can continue to innovate together with INFO to offer our travelers the best possible OV-fiets experience” – Marise Bezema, Manager Services NS-Stations
The best stories about innovation?
Sign up for our newsletter!
Please leave your name and email below