At Kingmakers, I helped lead the architectural work required to scale a front-end application that originally served a single brand into a solution capable of supporting multiple brands and multiple languages, while maintaining one shared codebase inside a monorepo.
The brands involved were:
The objective was clear:
Support multiple brands and multiple languages while keeping a single front-end codebase.
This article explains the approach I helped design, the architectural decisions we made, and how we safely rolled the solution out to production.
When I joined the team working on this part of the solution, the setup was simple:
Because of this simplicity, development was fast and predictable.
However, the business required us to expand the solution so that:
This meant we needed to evolve the architecture without compromising maintainability.
Before implementing a solution, I worked with stakeholders to understand:
This early alignment allowed me and the team to design a solution that would scale properly instead of solving only the immediate requirement.
One of the most important conclusions from this discovery phase was:
Brand and language should not be solved using the same mechanism.
They have different operational risks and therefore required different approaches.
The architecture I helped introduce separated brand configuration from language configuration.
Brand needed to be strict and deterministic.
Serving the wrong branding would immediately damage user trust.
Locale needed to be dynamic and flexible, with safe fallbacks when content was unavailable.
This separation allowed us to build a solution that was both safe and scalable.
To support multiple brands from the same codebase, I helped introduce a theme-based architecture.
The idea was simple:
This meant that at build time, the correct theme would be compiled into the application.
As a result we produced two deployments from the same monorepo:
I deliberately chose build-time brand configuration because brand mistakes are highly visible.
If the wrong brand theme appears even briefly, it can undermine customer trust.
By resolving branding at build time, we removed that risk entirely.
Language requirements were different from branding.
Languages need flexibility because the same brand may operate in many countries.
For this reason I helped design the solution so that locale is resolved at runtime.
Locale is derived from the URL path.
For example:
/en-NG
/frThe application reads the locale from the path and loads the appropriate translations.
If a locale is missing or unsupported, we fall back to English.
English was selected because it is widely understood in the markets where the solution operates.
This ensured the user experience always remained functional.
As the solution expanded to support multiple brands, languages, and devices, I introduced a concept we called the Discrimination ID.
The Discrimination ID combines three pieces of runtime context:
This identifier became the single source of truth for request context.
When a request enters the solution, the Discrimination ID is derived from three signals:
| Context | Source |
|---|---|
| Brand | Base URL (betking.com or supersportbet.com) |
| Locale | URL path |
| Device | Request headers / user agent |
These values are combined to form the Discrimination ID.
One of the benefits of introducing the Discrimination ID was simplifying communication between the frontend and backend.
Without it, every API request would need to include parameters such as:
Instead, the frontend can call the same endpoint and the backend automatically understands the context using the Discrimination ID.
A good example is the registration flow.
When the frontend requests location data, such as available cities, the backend uses the Discrimination ID to determine:
This significantly reduced the complexity of frontend requests and made backend behaviour easier to manage.
The Discrimination ID also improved analytics.
Because every event included the same context identifier, we could easily analyse metrics by:
For example we could quickly determine:
The introduction of locale-based routing meant the new solution behaved differently from the original system.
The previous setup did not include locale in the URL.
To avoid disrupting users, I helped design a controlled rollout strategy.
The new solution was deployed to production but only activated when a specific cookie was present.
Routing logic was implemented so that:
if cookie == true
route user to new locale-aware system
else
route user to existing systemThis allowed:
to test the new solution in production using real data without exposing it to all users.
Once the solution proved stable, we began gradually rolling it out.
The rollout process was:
This approach ensured a safe and controlled migration.
The final solution allowed Kingmakers to support:
—all from a single front-end monorepo.
Key benefits included:
Helping design and deliver this solution was particularly rewarding because it combined:
Most importantly, it created a foundation that allows Kingmakers to expand into new markets while maintaining a clean and maintainable front-end architecture.