Frontend architecture case study:
school management system
This is the first part of the School Management System series. You can read the next part here
Intro
Micro front-ends are something Iβve worked with in production for a few projects and so far it has been a great developer experience for me. I wanted to share my take on it with yβall! Hope youβll enjoy πΈ.
Why?
There are excellent references that explain the history of how micro front-ends entered the software architecture world such as Building Micro-Frontends by Luca Mezzalira π. It also helps to get familiar with microservices since they are designed to solve similar problems. β¨
To summarize, I would say that:
Micro front-ends were invented to scale up a frontend architecture while decoupling its different parts.
There is some time where a frontend becomes either weighted down by legacy code or way too huge and releases get significantly slowed down. This architecture then came to the rescue! π
How?
Basically, there are two things required:
- a Β« shell Β» server to assemble the independent parts and serve them
- a way to know where to fetch each independent frontend
Of course, this is only the basic structure; there are other elements to consider:
Developer Experience
- Communication between teams
- Automations
- Dependency management
- Testing
Technical Concerns
- Microfrontends boundaries
- User interactions and events between microfrontends
- Routing
- State management
- Internationalization
- Authentication
Product Quality
- Observability
- Analytics
- Error handling
- Visual coherence and uniformity
- Testing
Learn with an example
Let's imagine a basic school management app. There are going to be multiple portals (teacher's, student's and administration's) that will cover many features to serve different customers needs:
Teachersβ console
- Communicate with students π‘
- Publish learning material π
- Receive student's deliverables and grade students π
- Review class analytics (attendance, performance) π
- See HR info (pay, insurance, contracts) π§ββοΈ
- Communicate with tech support and administration π‘
Studentsβ console
- Download learning material π
- See courses and deadlines π
- Pay for classes π¦
- Send homework π
- Receive grades π
- Read announcements π‘
Administrationβs console
- Manage the global school's calendar π‘
- Pay the teachers and other personel π¦
- Make announcements π‘
- Manage students bills and payments π¦
- Communicate with the personel and students π‘
- See school's analytics π
- Manage legal and accounting π§ββοΈ
Division
We could divide the microfrontends by portal (i.e. one team per console); this is also called "vertical division". However, it would mean that each team will have to implement similar features, like a email system. Instead, we'll have each team specialize in technical concerns that will span over many features; we call that "horizontal division":
The commmunications π‘ team will know how to implement an emailing/chat system, a calendar and a news dashboard. The analytics π team will know all about business intelligence and know what to analyze for whom. Finances π¦ team will learn to implement payment processing solutions and bill/receipt generation. HR π§ββοΈ team will know how to produce documents and put e-signatures on it. Class π team will know how to manage learning materials, deliverables (pdfs, quizzes, etc.), and grades. Finally, a shell π team makes sure every micro frontend are well integrated by implementing the routing, error handling, authentication and other mechanisms. Ideally there would be a QA team to implement integrated tests to make sure everything works seamlessly.
Routing
I choose client-side routing managed in the shell app, because of its flexibility. Depending on who's logged in, the routing will be adaptable in order to optimize the client-side cache before we rely on the CDN's cache.
Rendering
I will use CSR for the shell app since this is an highly interactive non-SEO critical app. The app will be served on a CDN and the browser cache will reduce future load times. This decision reduces the complexity of setting up a server, simply composing through assets endpoints.
This kind of decision is a complex subject and deserves a whole another discussion, so I would recommend reading about it π.
Organizing the code
The shell app will be a monorepo. It will expose multiple libraries and versions for each. This will decouple each part while reducing the complexity for the shell team to manage multiple small repos.
For each microfrontend team, I prefer a polyrepo strategy to promote independence between teams. Each microfrontend is free to use their own version of a dependency, ideally updating these often.
Since SEO is not critical, initial loading times can be a little bit longer when versions differ. This is a tradeoff that I am accepting.
There are ways to mitigate the negatives of using a polyrepo:
- Have a discoverability system so everyone knows where to find each project
- Code sharing via dependency injection
- Observability, logging, design system, etc.
- Central governance about best practices and contracts between microfrontends
- Well established communication flows (slack channels, roadmaps, READMEs, typings, versioning, git, documentation)