B2B & B2C architecture
Evolve runs B2B and B2C commerce from the same backend. A single set of services handles both models, so you can build a site that is B2C-only, B2B-only, or (increasingly common) a combination of both. B2B functionality layers on top of the standard commerce model, adding business units, associates with roles, quote workflows, and shared shopping lists. The path through the code depends on whether the authenticated user has an active business context.
How it builds on StoreContext
Section titled “How it builds on StoreContext”Every request in Evolve carries a StoreContext containing a store key, locale, and currency that scope the request to the right store. For B2C customers this is all that’s needed: the StoreContext determines which assortment, prices, and content they see.
B2B adds a second layer. When a user belongs to a business unit, Evolve attaches a BusinessContext to their session. The BusinessContext can override the store key from the StoreContext with the business unit’s own store, giving B2B customers store-specific pricing and assortments without requiring a separate deployment.
BusinessContext
Section titled “BusinessContext”When a B2B user logs in, Evolve loads their business units and selects a default. The active business unit and its associated store are stored in the user’s session token as the BusinessContext:
type BusinessContext = { businessUnitId: string; businessUnitKey: string; storeKey?: string; storeId?: string; channelId?: string;};The BusinessContext affects the entire request:
- Store key override: the business unit’s store replaces the default StoreContext store key, giving B2B customers store-specific assortments and pricing
- Distribution channel: the business unit’s channel enables B2B-specific pricing through commercetools price channels
- API scoping: operations route through the Associate API instead of
the customer
meendpoint
Users with access to multiple business units can switch between them using
the setActiveBusinessContext mutation:
mutation { setActiveBusinessContext( businessUnitId: "acme-corp" storeKey: "b2b-store" ) { success }}Dual-path routing
Section titled “Dual-path routing”Every operation that can be performed in both B2C and B2B contexts uses a dual-path pattern. The service checks whether the user has an active business context and routes accordingly:
graph TD;
request["Incoming request"]
check{"Has BusinessContext?"}
b2c["Customer API<br/>me endpoint"]
b2b["Associate API<br/>scoped to business unit"]
backend["commercetools"]
request --> check
check -- "No (B2C)" --> b2c
check -- "Yes (B2B)" --> b2b
b2c --> backend
b2b --> backend
- B2C path: uses the customer’s own OAuth token via the
meAPI. Operations are scoped to the individual customer. - B2B path: uses system credentials via the Associate API, scoped to the associate’s ID and business unit key. This enforces the commerce backend’s built-in permission model for associates.
This pattern applies to orders, shopping lists, quotes, carts, and other operations that differ between B2C and B2B.
Business units
Section titled “Business units”A business unit represents a company or organizational entity. It contains associates (users), addresses, and is linked to one or more stores.
type BusinessUnit { id: ID! name: String! stores: [Store!] addresses(page: Int, pageSize: Int): AddressConnection! billingAddress: Address associates(page: Int, pageSize: Int): BusinessUnitAssociateConnection! availableRoles: [BusinessUnitRole!]! orders(page: Int, pageSize: Int): OrderResult shoppingLists(page: Int, pageSize: Int): ShoppingListResult quoteRequests(page: Int, pageSize: Int): QuoteRequestResult quotes(page: Int, pageSize: Int): QuoteResult}The BusinessUnit type is a federated entity extended by multiple
services. The account service owns the core fields (name, stores,
addresses, associates), while the order, quote, and shopping list services
extend it with their respective fields using GraphQL Federation.
Associates and roles
Section titled “Associates and roles”Associates are users linked to a business unit. Each associate has one or more roles that determine their permissions:
type BusinessUnitAssociate { id: ID! email: EmailAddress! givenName: String familyName: String roles: [BusinessUnitRole!]!}Associates can be managed through mutations:
businessUnitAssociateInvite: invite a new associate by email with specific rolesbusinessUnitAssociateRolesUpdate: change an associate’s rolesbusinessUnitAssociateRemove: remove an associate from the business unit
Quotes
Section titled “Quotes”The quote workflow allows B2B customers to request pricing for a cart before placing an order.
graph LR;
cart["Cart"]
request["Quote Request"]
quote["Quote"]
order["Order"]
cart -- "quoteRequestCreate" --> request
request -- "Seller responds" --> quote
quote -- "Customer accepts" --> order
A quote request is created from an existing cart:
mutation { quoteRequestCreate( id: "cart-id:cart-version" args: { comment: "Requesting volume discount" purchaseOrderNumber: "PO-2024-001" } ) { quoteRequest { id { id } status } }}Quote requests and quotes are accessed through the BusinessUnit type:
query { businessUnit(id: "acme-corp") { quoteRequests(page: 1, pageSize: 10) { results { id { id } status comment lineItems { name, quantity, price { gross { centAmount } } } } } }}Shopping lists
Section titled “Shopping lists”Shopping lists work for both B2C and B2B, but with different scoping. B2C customers have a single implicit shopping list. B2B users can create multiple named shopping lists that are shared within their business unit.
type ShoppingList { id: ID! name: String! description: String items: [ShoppingListLineItem!]!}In a B2B context, shopping lists are accessed through the business unit:
query { businessUnit(id: "acme-corp") { shoppingLists(page: 1, pageSize: 10) { results { id, name, items { variant { sku }, quantity } } } }}Authentication flow
Section titled “Authentication flow”Authentication (login and registration) is identical for B2C and B2B customers. See Authentication & tokens for the full token system. The differentiation happens after login:
- The user logs in with the standard
customerLoginmutation - The account service loads the user’s business units
- If business units exist, the first one is selected as the default business context
- The business context (business unit key + store key) is stored in the session token
- On subsequent requests, services detect the business context and route operations through the Associate API
The Customer type exposes B2B fields for customers with business units:
type Customer { # ... standard fields ... businessUnit(id: ID): BusinessUnit businessUnits: [BusinessUnit!]}The frontend uses businessUnits to determine whether to show the B2C or
B2B account experience.

