Bended on my knees I raise my hands, Oh Creator of all things! How wonderful is Your orchestration! Small to large all are individuals yet closely connected, though driving their own path as king of their own territory. Still all have consistent similarities from atom to galaxy. And Your grip on this Runtime is so strong that without stopping it You bring new things, impossible innovations which appear as miracle. Some of their science we discover and some of them we still wonder.
From the very beginning of my programming life, I had a wild fascination of designing a dynamic and charismatic runtime where resources can be extendable, expandable, designable, producible, traceable, healable, breed-able and many more on the fly without stopping it. In last 20 years things came like Hot Deployable, Pluggable, Hooks for runtime functionality extension and recently auto scalable for runtime hardware sizing without stopping it. Keeping basic two features intact of a Runtime we can think of designing its dynamism, and those are:
1. Security (Security must not be compromised)
2. Availability (It must be available in any situation)
Loosely Coupled & Distributed Resources
Consistency of #2 becomes more challenging when designing a runtime where resources are distributed in a complex network. If unavailability of a resource in that network makes the whole runtime unavailable than making it distributed will become a curse. Here we feel the importance of keeping resource Loosely Coupled. The love of micro things above monolithic things makes sense when in a runtime those micro things are loosely coupled otherwise it is again a giant and more complex to manage.
Separation of Concerns
If someone asks me what is the one magic trick of designing micro components, I will definitely mention Separation of Concerns Appropriately. What are the basic concerns of a single runtime with distributed resources? If I list basics then it would be:
1. Request Routing
2. Load Balancing
4. Data Presentation (UI/API)
5. Data Preparation (Domain/Service)
6. Data Preservation (Persistence)
Now, if you want to go another level of breakdown of concerns, then for #6 most commonly known concerns are write and read. So, we can have two persistence layers:
1. Read Database
2. Write Database
and #5 is highly dynamic and fully depends on for what domain you are designing the runtime. Which services has the highest traffic, what services will do batch jobs and what is the time limitation to perform a job and from business perspective what functionalities defines a specific service for a specific type to audience and how much independent it should be. Simple separation of concerns in sales domain can be (for example):
1. Order Service (Business)
2. Invoice/Bill Service (Commercial)
3. Payment Service (Accounting)
Similarly, #4 (Frontend) can be also composed in a micro way, basic example of concerns that can be separated are ‘User End’ like Mobile Frontend, Web Frontend, Desktop Frontend, API (for third party app). This is also very dynamic and fully depends of what solution you’re designing and for whom. If it’s a trading industry, common faces can be:
1. Frontend for Supply chain
2. Frontend for POS/Ecommerce
3. Frontend for CRM
4. and many more
Seamlessness for Micro-Frontends
A single runtime must act seamless, user must not feel inconsistency in accessing different kind of features from the same runtime. To provide such seamlessness, basic features that can help:
1. SSO & central IdP
These are basic wrappers one need to present for all the Frontends and services. Oauth2 OpenId Connect (OIDC) is currently a popular way of implementing SSO on a runtime with distributed services. And there are many HTTP proxy who are working nice as proxy/router for accessing multiple frontends under a single domain.
Seamlessness for Micro-Services
Data consistency and avoiding data silos are very important for domain design. Inter-connectivity among services is very important for that. Very important tool for such design is ‘Proxy Pattern’, through proxying services with service clients from another service one can avoid creating copies of same data in different services.
Single Point of Failure
There are some components which are common for many other components to work properly such as a router/gateway. Even for any resource by providing redundant instance/replica we can ensure that. In these cases, the load balancer will ensure load distribution in redundant resources and sticking session with requests.
In multi micro-frontends composing I prefer having each its own router/gateway instead of having a common API Gateway/Router for all the frontends to communicate micro-services, this way it becomes more individual and less dependent on single point.
Discovering Services and Auto-scaling
If we increase a instance/container/node which may contain a frontend or service or router, it must be discoverable automatically in runtime and the load balancer must recognize that automatically.
Independent Release Cycles
Independent Codebase & Team
Most of the time the monstrously large and scary frontend makes the whole product team frustrated as a single change becomes highly costly in terms of time. And usually, stakeholders jump to a new frontend technology and brings a critical shift in the whole thing.
But In the same runtime having the capability of upgrading features independently with dedicated team without breaking or stopping it is a blessing in this competitive market.