Engineering Evolution of Tencent Docs Microservice Gateway: Monorepo, pnpm, and Docker Optimizations
Tencent Docs transformed its gateway from a monolithic Node.js service into a four‑service micro‑architecture using a pnpm monorepo, deterministic lockfiles, and a custom Docker context script that eliminates symlinks, pins @grpc/grpc‑js, and produces smaller, reproducible images.
Tencent Docs gateway serves as the traffic entry for the document front‑end and has evolved from a monolithic service to a microservice architecture managed in a Node.js monorepo.
Existing issues include uncontrolled dependency upgrades (e.g., @grpc/grpc-js >1.8.x causing excessive CPU usage), lack of lock‑file usage, and complex Docker image building due to soft/hard links in the workspace.
Engineering analysis identifies four microservices maintained in a single repository, with Yarn + Lerna + npm workspace previously used, leading to dependency promotion problems.
Key considerations:
Need to lock all transitive dependencies.
Docker images must contain a clean node_modules without symlinks.
Solution adopts pnpm workspace, which provides deterministic dependency resolution and hooks (readPackage, afterAllResolved) to enforce specific versions (e.g., pin @grpc/grpc-js to 1.7.3).
Custom Docker context generation script ( .pnpm-context.mjs ) extracts only the required packages, dependencies, and meta files for each service, producing a tar stream with three directories: meta , deps , pkg .
Dockerfile uses multi‑stage build and the --mount=type=cache option, with package-import-method=copy to avoid symlinks.
Soft links are leveraged to keep original file paths while copying files, reducing validation effort.
The .pnpmfile.mjs defines hooks:
// readPackage hook
function readPackage(pkg, context) {
if (pkg && pkg.dependencies && pkg.dependencies['@grpc/grpc-js']) {
const grpcVersion = pkg.dependencies['@grpc/grpc-js'];
if (compareVersion(grpcVersion, '1.7.3') >= 1) {
pkg.dependencies['@grpc/grpc-js'] = '1.7.3';
context.log(`Modifying the @grpc/grpc-js package...`);
}
}
return pkg;
}
// afterAllResolved hook
function afterAllResolved(lockfile, context) {
// ... lock @grpc/grpc-js to 1.7.3 across all packages
return lockfile;
}
module.exports = { hooks: { readPackage, afterAllResolved } };Results: all dependencies are fully locked, ghost dependencies eliminated, Docker images are smaller and deterministic, and the build pipeline is simplified.
Conclusion emphasizes the importance of continuous engineering improvement for backend services.
Tencent Cloud Developer
Official Tencent Cloud community account that brings together developers, shares practical tech insights, and fosters an influential tech exchange community.
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.