B2B Catalog Split Across Websites with Shared Customer Accounts
A distributor runs one Adobe Commerce instance with three websites (retail, wholesale, government) sharing a single global customer account pool. Wholesale has ~180k SKUs and 40k customers; retail has 6k SKUs. Prices are set per website via a custom price attribute plus tier prices. Recently: (1) a wholesale buyer logging into the retail storefront sees wholesale tier prices in the cart, (2) catalog price rules applied to retail leak into wholesale, (3) adding a 4th website is projected to double nightly indexer runtime from 3h to 6h. Product data is authored once and assigned to websites via a shared attribute set. The business wants shared login across all storefronts but strict price isolation.
1. The buyer sees wholesale tier prices on the retail storefront. Diagnose the most likely architectural cause and state how you would verify it before proposing a fix.
Read the worked answer
Tier prices in Commerce are stored globally per product/price scope, not per website by default; the customer group is the usual isolation lever. If the wholesale buyer's account is in a group that has tier prices, and the retail website does not scope customer groups or restrict group-to-website assignment, the tier price will surface anywhere that customer shops.
Verification steps:
- Confirm the customer's
group_idand whethercustomer_grouphastax_class/website scoping. Checkcustomer_group_excluded_website(or equivalent assignment) - if the group is not excluded from the retail website, it is eligible there. - Inspect the quote/cart item price source in
quote_item- is the price coming from tier price (tier_priceindex) or a catalog rule? Checkcatalogrule_product_pricefor that product/website/group. - Reproduce with a controlled account in each group on each website and log the price resolver path.
Root cause is typically that the shared account pool lets a wholesale-group customer authenticate on retail without group scoping. The fix direction: scope customer groups to websites (exclude wholesale group from retail), or use website-scoped price attributes with a price resolver, not global tier prices. Confirm which lever before changing data.
2. Design the price isolation model. Compare per-website price attributes vs. customer-group scoping vs. separate websites with distinct base price scopes, and recommend one.
Read the worked answer
Three levers, each with tradeoffs:
- Website-scoped price attribute (custom attribute with
scope=website): price varies by website, resolved at index time into a per-website price index. Clean isolation, but you must build a price indexer and a price resolver plugin, and every price consumer (cart, quote, catalog, API) must honor it. High build cost, high correctness. - Customer-group scoping: keep base price global, use tier prices and group-website exclusion. Lower build cost, uses native machinery, but tier prices are global per product and group - isolation depends entirely on correct group-to-website assignment. Fragile if accounts move groups.
- Separate websites with distinct base price scopes: heaviest; effectively per-website catalogs. Maximizes isolation but multiplies index work.
Recommendation: website-scoped price attribute + price resolver for the base price, with customer-group exclusion as a defense-in-depth guard. Rationale: the requirement is strict price isolation across websites with shared accounts; group scoping alone cannot guarantee it because a misassigned group leaks prices. The attribute approach makes the website the source of truth. Pair it with automated tests asserting no cross-website price bleed.
3. The 4th website will push nightly indexing from 3h to 6h. Propose an indexing architecture change and identify the biggest risk.
Read the worked answer
Runtime scales roughly linearly with website count because price and catalog rule indexers iterate per website/group. Options:
- Move to partial/on-demand indexing (
Update by Schedulewith mview) so only changed products reindex, instead of full rebuilds. This is the highest-leverage change. - Shard indexers by website across consumers/workers so websites index in parallel rather than serially.
- Reduce index surface: don't index retail-only attributes for wholesale websites; scope attribute sets per website where possible.
Recommendation: switch price/catalogrule indexers to schedule mode with mview, and run per-website indexer processes in parallel via separate cron groups or message queue consumers.
Biggest risk: mview changelog bloat and version drift - if the changelog table grows unbounded or consumers fall behind, you get stale prices (the exact isolation failure you're trying to prevent). Mitigate with changelog cleanup, monitoring on mview_state version lag, and a nightly reconciliation full index as a safety net.