Skip to content

ZALANDO 2023-06-25

Read original ↗

Zalando — Context Based Experience in Zalando

Summary

Zalando Engineering describes the context-based experience mechanism introduced in 2022 to support selective-distribution brand requirements in the Fashion Store. The problem: certain distribution brands require distinct visual treatment — white packshot backgrounds instead of Zalando's default grey, hidden from search unless explicitly requested, different theming — and the same product must render differently depending on customer context (e.g. the same outfit card gets a grey background in the normal catalog and a white background when the customer is inside the brand's elevated experience). The solution generalises beyond brand-specific requirements: an Experience is a named bundle of (policies, selection rules), resolved once at root-entity time by the domain backend that owns the customer's entry point (Fashion Store API → Catalog / Product / Search backend), stashed in the Rendering Engine request state, and then propagated to every child micro-frontend which re-queries FSA with the resolved experience so downstream backends (e.g. Product backend serving catalog cards) can apply the right policies (Source: sources/2023-06-25-zalando-context-based-experience-in-zalando).

This completes the Interface Framework architecture's story about how personalised context flows through a micro-frontend tree without each Renderer re-deriving it — the complement to Part 1's Entity tree and Part 2's recursive Entity resolution: a small request-scoped piece of state is picked once and read many times.

Key takeaways

  1. An Experience is (policies + selection rules). "Experiences are simply a collection of policies that we need to apply, and a list of selection rules" (Source: sources/2023-06-25-zalando-context-based-experience-in-zalando). A policy is presentation configuration the Experience wants applied (e.g. THEME, PRODUCT__FLAGS__HIDE_SALE: true); a selection rule (selection_metadata) declares the criteria that enable the Experience (e.g. experience_brands = ["BRANDNAME"]). See concepts/experience-zalando-if, concepts/presentation-policy, concepts/selection-metadata.

  2. Customer intent triggers selection — not product identity. "The criteria for enabling an experience are based on explicit customer intent. For instance, searching for the retailer name or one of its brands will enable the elevated experience. Viewing their product details page will also enable it." The same product renders differently depending on how the customer arrived — in an agnostic catalog browse it keeps default styling for consistency; entered via brand search or PDP it gets the elevated treatment. See concepts/customer-intent-experience-selection.

  3. Intent-to-experience rules are owned by the domain backend closest to the request, not a global config. "These intentions are identified by our backend systems with specific business domain rules, i.e. the Search backend will have different rules from the Product backend." Search backend inspects the query; Catalog backend inspects active filters; Product backend inspects the entity being viewed. No central rules engine. See concepts/customer-intent-experience-selection.

  4. Two-step flow: resolve at root, propagate to children. The Experience Resolution step happens "very early during the root entity resolution" — the Rendering Engine asks FSA for the root entity, FSA queries the owning Catalog/Search/Product backend, that backend picks the best-matching Experience via selection_metadata, and the resolved experience name is "stored in the Rendering Engine request state". Then in the child-renderer step, each micro-frontend queries FSA independently — "only this time the query will use the previously resolved experience" — so downstream backends (e.g. Product backend for product cards) can apply the right policies without re-deriving context. See concepts/root-entity-experience-resolution and patterns/request-state-propagated-experience.

  5. Fallback experiences solve conflict resolution. "How to decide which experience to choose when two brands belong to different experiences? Thinking about the right use cases to support the business needs, whilst keeping simplicity is the key. Our approach to solving some cases is to define Fallback experiences, to be able to catch these use-cases." When multiple candidates match, the rule set falls back to a designated Experience rather than trying to merge policies. See concepts/fallback-experience and patterns/fallback-rule-for-experience-conflict.

  6. Same Entity, same Renderer, different Policies — the fourth axis of the IF substrate. Part 1 introduced the Entity tree. Part 2 introduced the recursive walk. The Experience mechanism adds request-scoped presentation context as an orthogonal dimension — Renderers don't change, Entities don't change, the Entity→Renderer map doesn't change; only the Policies applied to each backend call change. Keeps the Entity→Renderer mapping pure while making it context-sensitive.

  7. Experiences include a visibility policy, not just theming. The example explicitly lists PRODUCT__FLAGS__HIDE_SALE: true and notes that "some brands are only to be shown in the product catalog when the brand or its products are explicitly requested to be shown by specific search queries or via catalog filters". An Experience can therefore hide a product from general browse — the policy surface is catalog admission, not just presentation. See concepts/presentation-policy.

Systems touched

  • systems/zalando-rendering-engine — the Rendering Engine's request state now holds a resolved-experience slot; the RE passes it as a query parameter on every child-renderer GraphQL call against FSA.
  • systems/zalando-interface-framework — the Experience mechanism is an IF-wide architectural feature, not a page-specific hack; any Renderer's behaviour can vary by Experience via the Policies its FSA query receives.
  • systems/zalando-fashion-store-api — FSA receives the experience name and propagates it to the domain backends that own the data for each Renderer's query.
  • systems/zalando-graphql-ubff — the unified GraphQL schema is where experience-awareness is plumbed; this is the customer-facing instance of the UBFF architecture carrying request-scoped policy context.

Concepts and patterns

New concepts

  • concepts/experience-zalando-iffirst-class definition of an Experience as (policies, selection rules) in Zalando's Interface Framework. Canonical JSON shape: {id, name, policies[], selection_metadata[]}.
  • concepts/customer-intent-experience-selection — selection driven by customer intent inferred from the entry surface (search query / brand filter / PDP), with domain-backend- specific rules rather than a central rules engine.
  • concepts/selection-metadata — the explicit data shape that declares "what input makes this Experience eligible". Typed (e.g. brand_code) with a value list; acts as a declarative predicate evaluated by the domain backend.
  • concepts/fallback-experience — designated default Experience that resolves multi-match conflicts without per-request policy merging.
  • concepts/presentation-policy — a named, valued directive (THEME, PRODUCT__FLAGS__HIDE_SALE) the domain backend reads and applies when building its response. Policies are per-Experience, not per-page.
  • concepts/root-entity-experience-resolution — the specific IF step where the Experience is computed: root-entity resolution is the fork in the walk where context-classification happens once and is available to everything downstream.

New patterns

  • patterns/request-state-propagated-experience — resolve request-scoped presentation context at one designated entry step, store it in the aggregator's request state, and have every downstream fan-out re-query the aggregator with the resolved context as an explicit parameter. Avoids each micro-frontend rederiving context (expensive duplication + risk of divergence) and avoids a middleware that rewrites every backend call (couples aggregator to policy knowledge).
  • patterns/fallback-rule-for-experience-conflict — when multiple request-scoped contexts match, don't merge; resolve to a named fallback context. Preserves policy coherence at the cost of selecting a single owner.

Touched concepts / patterns

  • concepts/entity-based-page-composition — the Experience mechanism is orthogonal to the Entity tree; Entities describe structure, Experience describes context.
  • concepts/micro-frontends — explicitly framed against Zalando's micro-frontend architecture: "Zalando has many microservices, and even our Frontend's architecture is based on micro frontends. We defined the general data structure to understand the experience, but how can we orchestrate it across Zalando's ecosystem?".
  • patterns/entity-to-renderer-mapping — Experience adds a parallel axis; the Entity→Renderer map is unchanged, but the Policies consumed by each Renderer's backend call vary.

Operational numbers

The post contains no quantitative production numbers — no QPS, latency, Experience count, policy count, or brand-count disclosed. Scale is implicit from the Fashion Store's canonical 90%-traffic- on-IF / 25-market footprint established elsewhere on this wiki. The worked example (grey-vs-white packshot backgrounds, HIDE_SALE flag) and the JSON shape of an Experience are concrete.

Caveats

  • Brand-elevation case study framing. The post motivates the mechanism with 2022 selective-distribution-brand partnership requirements. The architectural generalisation (request-scoped presentation context through a micro-frontend tree) is the wiki-durable lesson; readers should not assume this is the only use case — the policy list is extensible.
  • Policy vocabulary not enumerated. The post names THEME and PRODUCT__FLAGS__HIDE_SALE as examples. The full policy list, namespacing rules, and conflict semantics within a single Experience (vs across Experiences) are not disclosed.
  • Fallback rule details not disclosed. The post mentions Fallback experiences but doesn't specify the full conflict- resolution algorithm (deterministic priority? declared default? closest-match heuristic?). Treat the pattern page accordingly.
  • Selection-metadata type vocabulary. Only brand_code is named as a type; other possible types (entity_id, query_text, filter_value, etc.) are implied by the domain-backend-specific- rules quote but not enumerated.
  • Request-state lifetime. The post states the resolved experience is "stored in the Rendering Engine request state" but doesn't address behaviour across client-side navigations in SPA mode, or caching implications for shared CDN responses.
  • No number on how many Experiences are in production — useful context for judging the cost of the machinery vs inline per-page conditionals.

Source

Last updated · 550 distilled / 1,221 read