Micro-Frontends in a Microservice Architecture
Learn about how a microservice architecture based on self-contained systems with clean architecture is a useful compromise of different alternatives.
Join the DZone community and get the full member experience.
Join For FreeArchitecture
The AngularMicroFrontendsAndMicroServices project shows how to create a holiday booking portal, built of three microservices with their own micro-frontends that are integrated into one customer-facing system. The goal of this architecture is to split a system into independent microservices based on bounded contexts that can be developed independently by different teams and with technology independence. For the split of the system into microservices, bounded contexts of Domain Driven Design should be used. The goal is to create microservices with distinct areas of responsibility. Each team can be responsible for one microservice and can develop it in its own rhythm and with its technology stack.
To achieve this architectural vision, the self-contained systems architecture is used. It is based on the idea that the micro-frontends are integrated into a host system that knows only how to call the micro-frontend and a messaging system that enables asynchronous communication between the microservices. That also provides fail safety if one of the microservices fails for a period of time because the other microservices can continue to serve the customers. The asynchronous communication eventually causes consistent behavior that has to be considered in the microservice implementation.
User Perspective
The portal user sees the system like this:
For the user, it is just one system to use.
System Perspective
The payment system needs the following information about the booked flights and hotels:
The payment microservice provides the user access to the hotel booking and flight booking micro-frontends and receives the messages of the booked hotels and flights for the payment process.
Component Perspective
This perspective shows the 3 microservices with their own frontends/backends/databases:
The payment frontend integrates the flight and hotel frontends to show one portal to the user. The flight and hotel backends send the bookings via a queue to the payment backend for billing.
Service Architecture
The payment/flight/hotel services are built with different stacks, but they share a clean architecture.
The clean architecture has 3 rings in the application. The first has all the connections to other systems like databases, rest interfaces, messaging interfaces, frontend, etc., and encapsulates the needed libraries and frameworks for this. The second ring has the DTO to entity mappers to disconnect the interfaces from the internal data structures and the services that implement the use cases. The services work with the entities and have them in the method signatures. The third ring contains the DTOs, entities, and some utilities. In theory, they should not contain anything of the used frameworks and be decoupled to be able to switch ORMs or Restmappers. In practice, that does happen every 1-2 decades if at all, and a complete rewrite of the application is probable in such a case. Because of that, the entities and DTOs do contain framework annotations.
Architecture Goals
This software architecture pursues three goals:
- To keep the system/application maintainable in the long term
- To enable the developers to find the location for an application change quickly
- To enable teams to work independently
The first goal can slow the developers down because writing spaghetti code is faster in the short term. The majority of the developers prefer to work in a reasonable architecture because they see the value for maintenance. The second goal saves time in searching for the right place to implement a change in a system and having the confidence to not break something because of surprising side effects. The third goal is to reduce the communication/coordination efforts between teams. Waiting for other teams to provide a feature for your team to be able to continue or start on a feature is slowing down the process. To mitigate that, the services should be as independent as they can be with a reasonable effort.
Microservices
FlightSelection
The flightSelection
service is built with an Angular frontend and a NestJS backend. The Angular frontend is used to enable a switch to Angular Native Federation if needed. The backend uses TypeORM for entity mapping and PostgreSQL to store the entities. The front end is served in the static directory that contains the compiled Angular front end. To publish the bookings to the payment service the MQTT library is used. The Messaging Service used is ActiveMQ Artemis.
The flightSelection
service has been the proof of concept for the NestJS framework. The first impression of NestJS is that the switch from Spring Boot is easy. Many concepts like injection and repositories are similar and using them on the Node platform worked. Clean architecture was easy to use with TypeScript and NestJS. The MQTT library worked well, too. NestJS on Node is an alternative to Spring Boot for non-compute-intensive applications. Advantages are faster startup time and lower memory footprint.
HotelSelection
The hotelSelection
service is build with an Angular frontend and a Kotlin/Spring Boot backend. The Angular frontend is used to enable a switch to Angular Native Federation if needed. The backend uses the usual REST controllers with services and JPA repositories. To publish the bookings with MQTT, the Eclipse Paho library is used. The Messaging Service used is ActiveMQ Artemis.
The hotelSelection
service has been the proof of concept for the use of Kotlin with Spring Boot. The Kotlin support of Spring Boot is very good: there have been no problems. Kotlin has advantages to Java like better support of immutability, helpful operators, more elegant stream syntax, etc. The difference becomes smaller with newer Java versions like 21. Kotlin is a good alternative if the system is stuck on an older JDK version. It then provides all the new language features. Whether the Kotlin features are worth the switch compared to Java 21 is a question everybody has to decide for themself.
Payment
The payment
service is built with Angular frontend and Java/Spring Boot backend. The Angular frontend is used to enable a switch to Angular Native Federation if needed. The backend uses the usual REST controllers with services and JPA repositories. To receive the bookings with MQTT, the Eclipse Paho library is used and the bookings are stored in the database. The Messaging Service used is ActiveMQ Artemis.
The payment
service frontend integrates the HotelSelection
and FlightSelection
frontends with IFrames. That makes the frontends completely independent. The alternative would be Angular Native Federation. That would create a coupling to the Angular Framework but improve the performance of switching the frontends. The backends of the payment
service and the HotelSelection
service can be used to evaluate the differences between a Kotlin and a Java Spring Boot implementation.
Conclusion
A microservice architecture based on self-contained systems with clean architecture is a useful compromise of different alternatives. It supports the split of a system into several services based on bounded contexts to enable the independent development of the services by different teams. It has a flexible and stable messaging infrastructure that can mitigate problems with one of the services. It presents a single system to the user without strong coupling and it provides a choice of software stack for the development teams. The software stacks used in this project have their advantages and disadvantages. The selection should be made according to project requirements and developer skills.
Opinions expressed by DZone contributors are their own.
Comments