Skip to content

CONCEPT Cited by 1 source

Recursive entity tree resolution

Definition

Recursive entity tree resolution is the tree-walk algorithm implemented by entity-based page composition runtimes: take a root Entity, resolve it to a Renderer via the rendering rules, run the Renderer's lifecycle, extract any child Entities the Renderer emits, and recur into each child — producing a tree of Renderer invocations from a tree of Entities.

Canonical instance is Zalando's Rendering Engine as disclosed in the Part 2 post (Source: sources/2021-09-08-zalando-micro-frontends-from-fragments-to-renderers-part-2).

The algorithm

resolve(entityNode, renderingRules):
    renderer = pickRenderer(entityNode.type, entityNode.context, renderingRules)
    data    = await runLifecycle(renderer, entityNode)      # withQueries + withProcessDependencies
    markup  = renderer.withRender(data)                     # React element tree

    for each childEntity in data.tiles.entities:            # entities the renderer "suggests"
        resolve(childEntity, renderingRules)                # recur; does NOT block parent's stream

    emit(markup)                                            # stream to client as soon as ready

Two properties make this a tree resolution rather than a flat render:

  1. The Renderer chooses child Entities, not child Renderers. Per the post: "a given renderer does not know which renderers will be used for its child entities". The Renderer emits Entities; the rules map them to Renderers.
  2. Rendering rules are themselves hierarchical. The rule shape has children: [...] sub-rules that scope Entity→Renderer mapping to the placement context (e.g. "an Outfit appearing under a Collection under an OutfitView is rendered as a small outfit-card, not a main view").

Non-blocking behaviour

"We do not block the resolution process. As soon as the entity is matched to a Renderer and the data resolved, the Rendering Engine kicks off the rendering process and starts streaming the HTML content to the client." (Source: sources/2021-09-08-zalando-micro-frontends-from-fragments-to-renderers-part-2.)

The consequence — each Renderer's HTML can be streamed as its subtree resolves, without waiting for the deepest leaves. This is the streaming SSR property the Rendering Engine exposes to the Web; the same tree walk is also how the Native-apps JSON is produced by the custom reconciler path (see concepts/react-custom-reconciler).

Worked example — Zalando outfit page

The Part 2 post gives a four-level rule tree for the outfit page:

OutfitView (root Entity: outfit:4NXOAez0Qti)
└─ outfit_view renderer
    ├─ child: outfit → outfit_highlight-b
    │   └─ child: product → product_horizontal-highlight-product-card
    └─ child: collection → collection_simple-carousel
        └─ child: outfit → outfit_outfit-card

The same Entity type outfit appears twice — once as root, once as a leaf inside a collection — and is mapped to two different Renderers (outfit_view vs outfit_outfit-card) by the placement-scoped rules. This is the concrete proof that placement is the discriminator for the 1-to-many Entity→Renderer relationship; the tree walk carries the placement context through the recursion.

Why this shape

  • Personalisation is a tree. The personalisation system naturally produces trees (a product page with related outfits and a similar-products collection), not lists. A recursive resolver matches the data shape.
  • Each Renderer owns one Entity. The invariant from patterns/entity-to-renderer-mapping requires that child Entities be handed back to the engine for re-resolution, not rendered inline. Recursion is the only shape that preserves this.
  • GraphQL data fetching composes over the tree. Because each Renderer declares its data dependencies (see concepts/declarative-lifecycle-api), the engine can fan out all queries across the tree and let DataLoader batch/dedup within a single request — which only works if the tree is walked breadth/depth-first before rendering.

Failure-isolation implications (open question)

Zalando's post does not explicitly describe what happens if one Renderer in the tree throws. Streaming + per-Renderer execution implies that neighbouring subtrees should continue, but the error-propagation contract is not disclosed. The withProcessDependencies slot explicitly allows a Renderer to return action: "error" — suggesting the framework substitutes an error-card Renderer without collapsing the parent — but this is implicit, not spelled out.

Contrast with flat composition

A Mosaic-style Fragment-based page template has a fixed set of slots filled with independent Fragments. There is no recursion: Fragment boundaries do not suggest sub-Fragments. That shape is simpler but cannot express personalisation-driven tree structure — the shape IF's Entity trees encode naturally.

Seen in

Last updated · 550 distilled / 1,221 read