How We Built a Multi‑Region H5 Platform with One Codebase and 90% Cost Savings
This article details the design and implementation of a multi‑region H5 platform that uses a single codebase and unified architecture, covering platform UI internationalisation, unified login, three‑layer region storage, environment‑aware configuration, ZooKeeper service discovery, region‑specific DLL builds, and npm private‑registry strategies to achieve seamless deployment across multiple data centres while cutting development effort by up to ninety percent.
Goal
The Wukong platform needed a unified H5 construction solution that could be deployed to any data centre from a single code base, maximize reuse and cut incremental development cost.
Overall Architecture Design
The system is divided into three logical layers – user, service and scheduling – isolated per region. The business‑layer diagram shows the separation, while the functional‑point diagram highlights the three core modules that were refactored: platform, compile service, and npm private‑registry & core libraries.
Module Breakdown
1. Platform Refactor
Internationalisation (Vue + vue‑i18n)
// i18n.js core configuration
const messages = {
zh: { ...zh, ...zhLocale }, // Chinese + Element UI Chinese
en: { ...en, ...enLocale } // English + Element UI English
};
// Region detection based on domain
const domainConfigMap = new Map([
['****.vivo.com.cn', { region: '01', local: 'zh' }],
['****.vivo.com', { region: '02', local: 'en' }],
['in-****.vivo.com', { region: '03', local: 'en' }]
]);vue‑i18n provides mature APIs, pluralisation, date/number formatting, lazy loading and developer‑friendly syntax.
Unified Login
getUucLogin(key, region) => {
const locationUrl = getLocationUrl(region, env);
return `${originMap[region][env]}/#/login?orgfrom=${locationUrl}/project${key}`;
};Three‑Layer Country‑Code Storage
// 1) URL parameters when opening a new tab
goList(projectId) {
const params = { projectId };
const wkCountryInfo = Utils.tools.getCountryInfoParams(); // e.g. { loc: 'AA', lan: 'th_AA', tz: 'REGION/aa' }
const query = { ...params, ...wkCountryInfo };
this.$router.push({ path: '/main', query });
}
// 2) Vuex store persistence
state: {
siteConfig: {
wkCountryInfo: { loc: 'AA', lan: 'th_AA', tz: 'REGION/aa' }
}
}
// 3) LocalStorage for iframe nesting
changeRegion(value) {
const item = this.headerRegion.list.find(v => v.countryCode === value);
const wkCountryInfo = {
loc: item.countryCode,
lan: item.languageCode,
tz: item.timezone
};
localStorage.setItem('__wk_platform_region_info_', JSON.stringify(wkCountryInfo));
}The three‑layer approach guarantees state consistency across tabs, Vuex and LocalStorage, improves user experience and satisfies regional data‑localisation regulations.
2. Compile Service Refactor
Unified Environment Configuration
// server/src/app/extend/context.ts
get env(): Ienv {
return env[process.env.REGION || 'AA'][this.app.config.env];
}Configuration files are organised per region (01, 02, 03) and per environment (local, test, prod).
// Directory layout
server/src/app/util/env/
├── index.ts # entry point
├── 01/
│ ├── index.ts
│ ├── local.ts
│ ├── test.ts
│ └── prod.ts
├── 02/
│ ├── index.ts
│ ├── test.ts
│ └── prod.ts
└── 03/
├── index.ts
├── test.ts
└── prod.tsZooKeeper Service Discovery
// server/src/app.js
const isTestOrPreEnv = process.env.EGG_SERVER_ENV.includes('test') || process.env.EGG_SERVER_ENV.includes('pre');
let group = isTestOrPreEnv ? `${process.env.REGION || 'AA'}-${process.env.EGG_SERVER_ENV}` : process.env.EGG_SERVER_ENV;
const serviceClient = new BeehiveService({
zkhost: ctx.env.zkHost,
pong: true,
services: { siteService: ctx.service.site, dspService: ctx.service.genDsp },
config: c.Config(c.group(group), c.maxTimeout(3 * 60 * 1000))
});Region‑Specific DLL Build
// webpack.dll.config.js (region 01)
const vendors = [
'@vivo/wk-api', // 01‑specific API
'vue-lazyload'
];
module.exports = {
output: {
path: path.join(__dirname, './dll'),
filename: '[name].[hash].js'
}
};
// webpack.dll.02.config.js / webpack.dll.03.config.js (regions 02 & 03)
const vendors = [
'@vivo/asia-wk-api', // 02/03‑specific API
'vue-lazyload'
];
module.exports = {
output: {
path: path.join(__dirname, './dll-02'), // or ./dll-03
filename: '[name].[hash].js'
}
};
// package.json scripts (excerpt)
{
"scripts": {
"dll": "npx webpack --config webpack.dll.config.js",
"dll:01": "npx webpack --config webpack.dll.01.config.js",
"dll:02": "npx webpack --config webpack.dll.02.config.js",
"dll:03": "npx webpack --config webpack.dll.03.config.js",
"start_test_01": "EGG_SERVER_ENV=test REGION=01 yarn dock_start",
"start_prev_01": "EGG_SERVER_ENV=prev REGION=01 yarn dock_start",
"start_prod_01": "EGG_SERVER_ENV=prod_wk REGION=01 yarn dock_start",
"start_test_02": "EGG_SERVER_ENV=test REGION=02 yarn dock_start",
"start_prev_02": "EGG_SERVER_ENV=prev REGION=02 yarn dock_start",
"start_prod_02": "EGG_SERVER_ENV=prod_wk REGION=02 yarn dock_start"
}
}Dynamic DLL Reference in Webpack
// webpack.pkg.config.js (excerpt)
const dllReferencePlugin = config.plugins.find(p => p.constructor.name === 'DllReferencePlugin');
if (dllReferencePlugin && dllReferencePlugin.options) {
const DLL_DIR = wukong.region === '02' ? 'dll-02' : wukong.region === '03' ? 'dll-03' : 'dll';
dllReferencePlugin.options.manifest = require(`./${DLL_DIR}/manifest.json`);
}3. npm Private Registry & Core Libraries
Proxy private registry (region 02)
# verdaccio config (region 02 proxy)
uplinks:
zhan-npm:
url: http://****.vivo.lan:8080
packages:
'**':
proxy: zhan-npmAxios interceptor for region‑aware requests
// request interceptor – dynamic URL routing
axios.interceptors.request.use(config => {
const { region } = app;
if (region === '01') {
config.baseURL = 'https://****.vivo.com.cn';
} else if (region === '02' || region === '03') {
config.baseURL = 'https://****.vivo.com';
}
if (wkCountryInfo.loc && wkCountryInfo.lan && wkCountryInfo.tz) {
const code = `loc=${wkCountryInfo.loc};lan=${wkCountryInfo.lan};tz=${wkCountryInfo.tz}`;
const loc = wkCountryInfo.loc;
config.headers = Object.assign(config.headers, {
'X-I8n-Code': code,
'X-Wukong-Loc': loc
});
}
return config;
});Deployment Flow
Choose the target region and the matching branch.
Build region‑specific DLLs using the appropriate npm script.
Run the Webpack build with the region‑aware configuration.
Deploy the generated assets to the region’s CDN.
Start the service with the correct REGION and EGG_SERVER_ENV environment variables.
Conclusion
The redesign provides a single‑code, multi‑region deployment model that reduces incremental development effort by over 90 %, enables seamless addition of new data centres, and keeps both front‑end and back‑end components region‑aware without adding complexity for developers.
vivo Internet Technology
Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
