We often work on building applications where a single application might scale to multiple instances with couple of changes. A good example for this case will be, when you are building an application for multiple countries. TLDR; Different countries might have different requirements i.e different sections or different form fields, but the base application i.e common part should remain same.
This becomes even challenging when the application is already build and in later stages of application lifecycle, it needs to scale to multiple countries.
What are the approaches people follow in the market ?
- Fork & maintain multiple repositories (Hard to believe, but happens)
- Multiple replica copies country-wise in same repo (Doesn’t make sense)
- IF-ELSE-IF-ELSE-IF ladder, hell! (Sprint ending this week, let’s wind it up)
- A/B testing Segmentation (We’re Frontend Developers not Devops)
- and on…
The solution should prevent us from maintaining multiple repositories, prevent DRY (Don’t Repeat Yourself), encourage separation of concerns, doesn’t ship not required code, ease development & deployments which should be done from Frontend.
Mixins & Dynamic Imports to the rescue !!
(Presenting this concept in Vue Conference, Bangalore, India)
Let's continue:
Quick Refresher- Mixins (Vue):
It is a flexible way to distribute reusable functionalities for Vue components, you can even define lifecycle hooks in it
- It can be imported in multiple components
- Mixins can be Overridden in your country specific component code
- Only override the function which need changes over the function imported from Mixin, Vue will manage the merge of both
Dynamic imports (Webpack):
This allows you to split your code into various bundles which can be country specific javascript chunks, loaded on demand or in parallel
How to implement this ?
We will have one parent AppBody in each country folder, it will contain multiple reusable child components like <user-profile-form> required.
<template>
<div>
<user-profile-form></user-profile-form>
<user-password-reset></user-password-reset>
</div>
</template>
The child component will import and override the base mixin app/ feature code. Let below be code of one of the child component of AppBody.vue parent file.
import userInfoMixin from "../../mixins/UserInfoMixin.js"
import Utils from '../../common/Utils';
export default {
mixins: [userInfoMixin], //Base code
methods: {
setTheme(data){
Utils.setTheme('pt-br');
}
},
mounted() {
this.countryCode = "+55";
this.setTheme();
}
};
Now our Directory Structure:
Above we have country specific folders to maintain separation of concerns, respective country developer can’t mess with any other country code. We have to keep above discussed child component in the respective country folder, the base code will go in the common folder.
How to switch countries ?
import userInfoMixin from "../../mixins/UserInfoMixin.js"
import Utils from '../../common/Utils';
export default {
mixins: [userInfoMixin], //Base code
methods: {
setTheme(data){
Utils.setTheme('pt-br');
}
},
mounted() {
this.countryCode = "+55";
this.setTheme();
}
};
The AppBody is the parent component, different for different countries holding the child components which are inheriting from JS & SCSS Mixins
Apart from invoking using query params, you can use webpack variables which will help you in build & deployment process using:
REGION=INDIA npm run build
Running the above command will give us region/country chunks (0,1.. below) which are loaded dynamically (only specific country chunk)after the base app code, which is just app.js 53.3 kb. So in the end, only your country code is loaded. apart from base app.js.
Extending CSS Using Mixins of SCSS:
Now adding SCSS code to our AppBody.vue file.
@import "../../assets/scss/component-styles/userBasicInfo.scss"; //base code
.basic-info {
/*overriding with country specific requirement */
input {
width: 100%;
padding: 10px 0;
border-bottom: solid 1px #e6e6e6;
font-size: 16px;
color: #000000;
border-radius: 0;
}
input[type='date'] {
appearance: none;
background: transparent;
-webkit-appearance: none;
&: : -webkit-inner-spin-button, &: : -webkit-calendar-picker-indicator, &: : -webkit-clear-button {
display: none;
-webkit-appearance: none;
}
}
Now, above methodology ensures the parent code which is same, is not re-written and only the different requirement needs to be written by overriding the parent function in the inheriting JS Function/CSS Class.
The separate country code chunk will be loaded dynamically during rendering after the base app code, Also the build process can be made country wise by passing webpack variables during the deployment, which means one country release will be independent of others.
The above method is one of the possible solutions. If you came across the same requirement, I would love to hear the solution from you.