Retail Application Migration: Lessons Learned Moving from On-Prem to Cloud Native
Recently, I came across a sample e-commerce application that demonstrates how to use Next.js, GraphQL engine, PostgreSQL, and a few other frameworks to build a modern web application. The application supports basic e-commerce capabilities such as product inventory and order management, recommendation system, and checkout function. This made me curious as to how much effort it would take to complete a retail application migration from an on-premise to cloud native solution.
The original architecture for this sample app looked like the below diagram. You can start the whole setup in a few minutes following this guide.
My curiosity was so tempting that I forked the project and replaced several components from the original architecture with their cloud native counterparts:
- PostgreSQL to YugabyteDB.
- Hasura on-prem GraphQL engine to Hasura Cloud.
- Local server for the Next.js frontend and backend to Vercel.
The migration didn’t finish overnight but was relatively smooth. Let’s quickly walk through my three main lessons learned.
Lesson 1: PostgreSQL compatibility matters a lot
In a cloud native deployment, I want to have a relational database that scales horizontally and is resilient to failures such as zone- or region-level outages. Even though YugabyteDB is a PostgreSQL-compliant database, I was still skeptical that the retail application migration would go smoothly with no code changes on the SQL side. My skepticism grew even more after seeing that the original DDL script had many stored procedures and triggers that are pretty hard to support in a distributed database.
However, my skepticism turned into joy when the application booted with no issues after I introduced two changes to the Docker compose startup script:
- Asked Docker to launch a YugabyteDB container (comprised of YB-Master and YB-TServer) instead of the Postgres one.
- Updated the database URL in the GraphQL engine container’s settings to the following:
postgresql://yugabyte:yugabyte@yb-tserver:5433/yugabyte
And that’s it! That’s PostgreSQL compatibility in action. The application was running, the data was loaded, and the frontend worked with no issues. So, I prepared a separate docker compose file for those who would like to launch the app on-premises with YugabyteDB. I also moved closer to my original goal – turning the app into a cloud native solution.
Lesson 2: Cloud native services are easy to switch to
This lesson might be just an obvious fact to most of you. But for me, this was a big positive lesson.
To remind you, I intended to transform the application to a cloud native solution by replacing the following components with their cloud native counterparts:
- PostgreSQL/YugabyteDB to YugabyteDB.
- Hasura on-prem GraphQL engine to Hasura Cloud.
- Local server for the Next.js frontend and backend to Vercel.
So, starting with Step 1, I provisioned a 3-node cluster with YugabyteDB Managed (formerly Yugabyte Cloud). The cluster is deployed across several availability zones and can tolerate zone-level outages:
Next, moving to Step 2, I created a Hasura Cloud deployment using the Standard Tier (that scales automatically and supports high availability). I also connected Hasura to my YugabyteDB database:
Lastly, finishing with Step 3, I deployed the Next.js backend and frontend with Vercel. I did this through the Environment Variables setting to connect to my Hasura Cloud GraphQL engine:
That’s it! As a result, my e-commerce application was running on a cloud native stack. The switch to these services was an easy thing:
Lesson 3: Zero code changes are a myth
As you see, the migration was smooth. Most of my time was spent adjusting configuration settings and provisioning cloud native services. But I cannot say this was a “zero-code-changes” type of experience. Personally, I think that that’s just a myth. You still have to be ready to tweak your code to make it work properly or much more efficiently with new software or services.
In my case, I had to change the Next.js logic in a few places, making sure Hasura Cloud’s admin secret is used correctly by the GraphQL clients. I also had to optimize the SQL scripts the app used for initial data loading. For instance, the original SQL script loaded 85,000 products by executing 85,000 individual INSERT statements that translated to 85,000 distributed transactions in YugabyteDB! But that’s just an anti-pattern for distributed databases. Therefore, I dramatically reduced the initial loading time by replacing those 85,000 individual INSERTs/transactions with ~17 INSERTs/transactions with 5,000 values each.
Conclusion
Instead of closing the article with some clever statements and high-level guidance, I would encourage you to experience this retail application migration process from start to finish (except for the code-level changes, that’s done for you!):
- Deploy the app with the initial architecture (PostgreSQL as a database) on your laptop.
- Transition to YugabyteDB but still deploy locally on your laptop. Follow the same instructions as above but ask Docker to start YugabyteDB.
- Deploy the application in a cloud native environment.
If you follow these steps, then the application UI will look as follows:
Got questions? Join the YugabyteDB community Slack channel for a deeper discussion with over 5,500 developers, engineers, and architects.