We’ve seen it a million times. Old platforms, created by dozens of developers over the course of as many years, with legacy code going back generations. The software works — but barely. And it’s badly in need of an overhaul.
But here’s the catch: how can a business start a digital transformation on a mission-critical platform without disrupting operations?
We’ve worked on cases like this several times, transforming Java platforms (also .Net, Rails, you name it) to modern architecture. And trust me: it is possible. Check this link with a lot of high profile case studies. This is a more down to earth article, with a more concrete example, after the one I wrote some time ago. It can be a good reference though.
Why change platform architecture?
- You can’t publish an update without introducing lag, risks, and even messier code.
- You want to move faster, have a good looking site, and improve your UX, but you don’t know where to start.
- You have to upgrade your stack and embrace newer technologies or face becoming obsolete.
Before we start to change platform architecture, we have to understand the current state of the system. So let me guess — there’s a good chance that your architecture looks like this:
There’s a good chance that you’ve been working on this platform for a long time, and for one of the reasons stated above, you HAVE to change. You likely have no tests, you don’t want to break anything, and you have no idea where to start. If this is your situation, this article is for you.
What we want
First, let’s get clear on objectives. Our goal is to have an architecture that accomplishes the following:
- Mobile ready: We can work on mobile apps and easily connect them to our business logic.
- Maintainable: We can make changes in the business logic without introducing bugs or causing any issues. All changes will be reflected across all platforms (web, mobile, 3rd parties).
- Automated: We can easily write automated tests, integrate CI servers, and pave the road towards a CD environment.
- Clean: Clean HTML and CSS. We’ll have a well-structured, semantic and maintainable UI.
A microservices oriented architecture could easily accomplish all these goals. But again, we don’t want to disrupt day-to-day business operations, and we can’t just rewrite it from scratch. We need a realistic process, and that means assuming that a large percentage of the current code will still drive your business logic. We have to take baby steps towards a clear goal on the horizon.
So let’s take it one step at a time.
Step 1 — Lay out the foundation
First, some background. We’re not reinventing the wheel here. Facebook did the same thing across their entire ecosystem. It is possible.
The power of ReactJS is that it’s a library, not a framework. This lets us work modular, migrating one component at a time until we can (eventually) achieve complete separation of UI and APIs, setting up the stage for a modern architecture. For more background on ReactJS, check out Pluralsight.
The first thing we need to do is identify a self-contained component of the app. This could be a new feature, either something easy or a pain point. You can write this new component using ReactJS. But then the tricky part: how do we integrate a new ReactJS component into an existing platform when its UI is rendered in the server and sent as an HTTP response?
We have two options.
Technique 1: rendering full pages
If the component that we are creating is a full page, we can create an entirely new ReactJS site with its own URL and separate it from Spring UI. For this, we need a reverse proxy, which can be configured using Apache, Nginx, etc.
We can configure our shiny new ReactJS site running on Node.js or just a static Amazon S3 bucket. We need a URL pattern to decide if a specific page is coming from Spring or ReactJS side. In the example, we used “/ui/*”, so everything following the pattern “www.yoursite.com/ui/*” will be using ReactJS. Here’s an example using Nginx.
The long-term goal is moving everything to ReactJS and cutting the umbilical cord to the Spring framework UI layer. This will eliminate the need for the reverse proxy, but again, let’s take it one step at a time.
As a side note, the reason to run a node server is just for SEO purposes.
Technique 2: rendering specific components
In some cases, rendering a full page isn’t an option. Sometimes our pages are too complex and we want to start by migrating only specific components in specific pages. For this, we have to do two things:
a. Include a placeholder in the UI generated by Spring framework.
Let’s say that we want to render a list of clients using ReactJS. Then we have to include a placeholder div (or section or table). Something like:
<div data-react-class='name-of-my-component'"' data-react-props='...'> </div>
You can use the ReactDOM class for this.
We can pass information using props, but ideally, each component should fetch all the data from the backend.
We’re rendering ReactJS. Now what?
At this point, we have a ReactJS component being rendered, either as a full page or smaller components inside an existing one. Now we have to connect it with an API.
We can create this API using our language of choice. I’m assuming that we have a Java team already, so let’s use Java. Ideally, we will be creating a beautiful, semantic RESTful JSON API. This API can use the services layer of our existing code.
This is when we have to be realistic. Building everything from scratch usually isn’t practical, and we have a lot of business logic that we can’t lose. So let’s use it: leverage this opportunity to create well-designed automated tests using your API.
Here’s what this will look like:
Step 2 — Iterate and Move Away from Legacy Code
As you go through the process of identifying components, writing them using ReactJS, and building the endpoints for that, a few things can happen. Most of them good things.
We have to keep strong semantics in our APIs. If we identify that we are building an entirely new module, we can create a new API. This is a step in the direction of a microservices approach. As we scale, these new APIs can be built using other languages or techniques, connect to different kinds of data modules (like a NoSQL database), or serve particular needs. We have to keep clear documentation for each API ( I recommend Apiary ) and use our existing business logic (services layer) whenever necessary.
Also, we’ll be able to create mobile apps and connect them to the same APIs. As a bonus point, we can use ReactNative and reuse some of our existing ReactJS components.
Here’s what it’ll start to look like after these changes.
Step 3 — Deprecate Spring framework presentation layer
I’m not saying that this can be done quickly. However, this is a natural step if we are consistent.
Nowadays, software moves fast. If we are running quickly enough with agile methodologies, we’ll be able to achieve an architecture that accomplishes all of the goals we initially outlined much sooner than we’d expect.
Now, we’ll have a web app 100% built using ReactJS, and we are cutting down the dependency to the presentation layer of Spring. We have all our data exposed via Restful services, and as an example, the diagram contains a new API built using a different language (Go) and a NoSQL database (Redis).
For simplicity, some things are omitted. Any API may or may not access the DBMS, depending on the needs. Different ReactJS components will use different endpoints and different APIs. It’s OK if a component uses more than one API, but if it happens too much, it probably means that your API architecture needs review.
Summary and a Few Extra Tips
Going through this process, you have a fantastic chance to rebuild your scattered HTML and CSS.
Always remember to build semantic, well-structured HTML. It takes longer, but it’s always, always worth it. If you don’t feel confident with CSS, use a CSS framework like Bootstrap or Grid. Keep it simple, keep it responsive, and follow standards. Here you can find the latest standards that we apply for CSS. Never underestimate how important it is to write high-quality CSS.
Remember to test your ReactJS components, and if possible, write end-to-end tests. Document and test every single web service and be mindful when you create a new API or environment. Keep it simple, refactor, and iterate.
Hit Me Up
I hope you found this article helpful, and if you have any questions on the finer points of an architecture overhaul, there’s nothing I love more than discussing the processes of digital transformation.
Even if it’s just a quick question, I’m here to talk — feel free to drop me a note at firstname.lastname@example.org.