In this article, we'll be looking at how Cubite decoupled the Open edX frontend by using Strapi and Nextjs and brought the joy back to their developers.
Open edx is a great open-source learning management system, but it's also an all-inclusive monolith. As such, there are some significant drawbacks to using this platform for your project needs. One of the biggest pain points comes when making small changes. Such as changing text on landing pages or updating the logo can take a good amount of the developer's time due to needing code modifications and recompiling assets from servers with every little change made in place.
We've created content types in Strapi for all of our UI components and stored their frontend data there. Some content gets created manually by CMS admins, while some are automatically generated when there's a new build on the frontend. We can now build our frontend using Next.js by fetching UI data from Strapi APIs. It's now possible to make changes in the UI without any technical skills, and our administration team can do it right from Strapi dashboard and when their changes are done with a click of a button it shows up in our platform.
Before we start, let's answer some key questions to make sure we're on the right track.
A monolithic system is a software system with one code base and all the functionality included.
In recent years, with the rise of microservices and containerization, having a monolith system can turn into a nightmare for software development teams. For example, in the case of oOpen edX we have a Python/Django app in which all features are stuffed together. We never know what would break when refactoring some parts of the application or adding new ones.
If one piece breaks, we need to wait for the full or partial system to be deployed again via Ansible. This can take hours, even days if a big migration is involved.
A monolithic application will slow down the development process. We already mentioned that deploying the system can take hours or days. In this case, every change in the code takes a lot of time and requires more effort than usual to be deployed on production servers.
On top of all this, if we want to release a new feature, it is not possible to test it in a sandbox environment and roll back if we encounter any issue. We have to wait for the deployment process to finish (compile assets, migrations, deploy, etc.)
Decoupling is when you separate an application into two or more parts that don't depend on each other. Each part has its own codebase.
A system architecture is said to be "decoupled" if each of its components may evolve independently of the others. The term decoupling generally refers to the removal of direct dependencies between two modules or components. In this sense, "backend" and "frontend" are loosely coupled.
In our case, we're trying to decouple the Frontend from the backend part of Open edX.
Why?
The biggest benefit, you get by doing it, is the ability to update your front-end without touching the Server-side code. With this method, development becomes way faster and easier for core developers. When a new frontend developer joins the team, he or she can start working with minimum onboarding.
A decoupled system distributes the work of developing a web app between your client-side and server teams. The net result is that you have a faster feedback loop when building features. It also allows you to change the look and feel of your app without needing to rebuild it each time.
The decoupled architecture can give you a few advantages:
High performance
Better design, better onboarding & maintenance for front-end developers
Developers are no longer blocked by slow backend development cycles
Faster shipping and iteration time overall.
Easily scale the application by dividing it into multiple microservices.
Choose a client-side framework like Next.js
Create APIs with Strapi
Build APIs in your Backend if needed
Use these APIs to build your Frontend
What we lose by decoupling a monolith?
There are some downsides to this approach:
It requires more initial work to set up the front-end and APIs, as well as understanding how they will interact with each other on a technical level
Teams must be cross-trained to maintain API quality and consistency
It can take time to identify which parts of your monolith are safe to decouple
Ok, enough with technical details, let's go through a quick example. As an example, I will show you how the frontend development looks before and after decoupling, we will also take a quick look at the site performance.
As I mentioned earlier, the project we are working on is called Open edX, which is an open-source learning management system. When you install it, you get something like the following out of the box.
I know it doesn't look too good, and the worst part comes when you want to start working on UI changes. Having a development environment means having the monolith up and running in your local machine. The minimum hardware requirement is at least 4G-6G of RAM to have a smooth development environment.
Ok, let's say we want to replace the logo placeholder with our actual logo.
Here are the steps you need to follow:
Make changes in your code repo
Pull them in the server
Run python paver command to compile assets (takes around 10-30 minutes)
Restart manually LMS services
Wait for 2-5 minutes till the services are up and running
Check if the change applied to your site
If you don't see your change, repeat steps 3 to 6 again openedx-coupled
This took longer than what you see in the video.
Here is the lighthouse report.
Looks pretty bad 😅
Now let's go to the fun part. If you are curious how we decoupled Open edX theming, we used existing APIs like courses APIs and implemented new ones like registration and enrollment. The reason behind that is we want to create some records in Strapi automatically based on what we have in Open edX to keep our LMS as a single source of truth. So, for example, our Next.js frontend makes a call to Open edX courses API and creates or updates courses in Strapi, so we don't need to manually create them there, and we have a single source of truth.
We created all the frontend elements as 'content type' in Strapi. We have, for example, navbar, hero, footer, course cards, tutors content types in Strapi and you can even change the site color globally from Strapi with one click.
In Next.js, we have all of our React components, like a hero, navbar, course, etc. and they are styled with Tailwindcss. Our site is pre-rendered using SSG and hosted on Vercel to bring a blazing fast experience to our users. In our Next.js app, we have a GraphQL client that makes calls to Strapi, fetches data and during the build time we construct components based on what we receive from Strapi.
Here is the new lighthouse report
Also, the Vercel build time was pretty quick. 1m1s
By decoupling the frontend, we could integrate our platform with Auth0 and Stripe pretty easily. If we wanted to do this with the previous design, it would take a longer time for development as well as be more expensive to maintain because of how often Open edX updates its source code on a regular basis. Fortunately, since these integrations live in our frontend now, all that's required is making sure the APIs we talked about earlier, still work. Something much easier than maintaining compatibility across different systems!
Decoupling your monolith can bring a new life to your team. It allows faster iteration, less coupling between backend and frontend, better scalability and maintainability of the system. All this without slowing down the development process of adding new features for your users. We found Strapi and Nextjs a great mix to achieve decoupling for our use cases and greatly contribute to the maintainability, scalability, and performance of the frontend application.
Cubite is an education technology company that believes in the power of learning. Our mission is to create a student-focused learning platform that inspires, empowers, and engages students in order to accelerate their academic and professional success.