← All studies

ARCHITECTURE STUDY · Seams

Where the boundaries go in a multi-brand platform

This is a self-directed design study. It is not work delivered for a client, and it contains no client names or client numbers. It is grounded in the kind of platform I work on every day: Salesforce Commerce Cloud, running around a hundred storefronts for many brands and markets. The examples use Commerce Cloud mechanics. The reasoning applies to any large commerce platform.

Why this matters

A group with many brands and many countries usually wants one platform, not twenty. The question is how much each brand is allowed to differ. Get that wrong and you either cannot serve a local need, or you end up with a hundred separate sites that only look like one platform.

The decision — Build every brand and market on one shared base. Let each one change only what it truly needs, as a thin layer on top. Never edit the shared code itself.

What it gives you — A new brand or market launches as configuration instead of a rebuild. One shared fix reaches every storefront. The platform can keep taking updates.

The risk it removes — The estate slowly splitting into a hundred custom sites that cannot share a fix or a feature.

In one sentence: a multi-brand platform is a bet about where change happens. Put the things that rarely change and are shared on one side of the boundary. Put the things that vary by brand or market on the other side. Then a new brand is a configuration job, not a copy of the whole codebase.


The problem

When one platform serves many brands across many countries, the hard part is not any single feature. The hard part is the boundaries: which behaviour is shared and owned by one central team, and which behaviour each brand or market is allowed to change.

There are two common ways to get this wrong.

The work is placing the boundary so you land between these two.

The question that decides everything

For any piece of behaviour, ask one question: does this vary by brand or market, and who owns that difference?

That question decides which side of the boundary it belongs on.

In Commerce Cloud this is made concrete by the cartridge path. A cartridge is a folder of code. The cartridge path is an ordered list of those folders for one site. When the platform needs a file, it walks the list in order and uses the first match it finds. So a folder earlier in the list can replace a file from a folder later in the list. This is called an override: you change behaviour without editing the original file.

Here are the layers. The shared base is the widest because most behaviour lives there. Each layer above it should hold less.

Layered platform as a pyramid A pyramid split into four tiers: a wide shared core at the base, then brand, then market, narrowing to a thin site-specific tip at the top. core / base shared · changes rarely brand identity · rules market site most behaviour lives here the thin, specific tail

The width of each tier is the point: the shared base carries most of the behaviour, and each layer above it holds only what genuinely differs.

The estate I work on has more layers than this — global, zone, brand, country, then site — but the idea is the same at any depth. A site is the shared base, unless a brand layer changes something, unless a market layer changes that, unless the site itself does. Most of any site comes from the shared base. The higher up you go, the less should live there.

The design: share the base, override it, never edit it

Two rules do most of the work.

First, keep the shared code untouched. The platform's own base code, and any code bought from a partner, is never edited in place. If you edit it, you cannot take the next update without losing your changes. Over a few years this is how a platform becomes impossible to upgrade.

Second, put differences in thin layers above it. A brand or market layer sits earlier in the cartridge path and replaces only the specific files that differ. Commerce Cloud gives safe ways to do this: you can call the original version of a file from your own version and add to it, rather than copying it wholesale.

The same boundary runs through the data, not just the code:

The pattern is the same in both cases. Name what is stable and share it. Keep what varies on the other side of the boundary, where it cannot break anyone else.

The judgement this really needs: knowing when a brand has stopped being a variation and has become its own product. If a brand's changes start to outnumber the shared behaviour they sit on, or if brand-specific logic starts appearing in the shared core, the honest answer is not one more exception. It is to give that brand its own boundary. Forcing two very different things to share one base is how a platform becomes slow to change.

Options considered

OptionDecisionReasoning
Shared base with thin brand and market layersChosenThis costs the most in design effort. You have to decide what is shared and hold that line. In return, brands move independently, a new market is mostly configuration, and one fix reaches everyone. The shared base stays updatable.
Copy the base for each brandRejectedQuickest for the second brand. Very expensive by the twentieth. Every shared fix has to be applied many times, and the copies drift apart.
Edit the shared base for each brand's needsRejectedThe tempting shortcut. It works until the next platform update, which it quietly blocks. Hand edits to shared code are the main reason platforms get stuck on old versions.
One strict template with no real overridesRejectedLooks consistent, but the first market with a genuine legal or commercial need forces an exception. Being strict does not prevent differences. It only pushes them out of sight.
A separate front end for each brandRejected in this contextGives each team the most freedom. You pay for it by rebuilding shared concerns such as login, payment, and consent for every brand. Right when brands are genuinely different products. Wrong when they are variations of one.

The rejected options fail for the same underlying reason. They avoid deciding where difference is allowed, so difference appears anyway, in places nobody planned.

What I would watch in production

What it gives you

The mechanism is not the interesting part. The interesting part is where you put the line, and the discipline to keep genuinely different things on different sides of it.


Related decisions: ADR-04 (use versioned API contracts instead of sharing a database) applies the same idea to system integration: refuse the hidden dependency and pay the cost of a clear interface. ADR-03 (handle order events after checkout, not during it) draws another boundary, this time in time rather than in structure.