Skip to content

ADR-003: link.v2 and ESL Image DTO Split (PLT-2484)

Status: In Progress Date: 2026-04

Context

The link.v1 DTO carries the assignment between an ESL barcode and the content to display on it. Over time, two distinct rendering systems have grown up alongside each other: the Studio rendering path (design-based, evaluated by studio-link-evaluator) and the ECC rendering path (model-based, rendered directly from link fields). Both paths shared the same link.v1 DTO, which used a design_source oneof containing either a forced_design_id (Studio path) or an eccmodel_id (ECC path). This oneof had to be inspected by every service in the pipeline to determine the routing.

The image side had an analogous problem. renderedimage was a single DTO written by whichever renderer produced the final image for a given ESL page. When the active rendering system for an ESL changed (e.g., from Studio to ECC or vice versa), both renderers could race to write the same renderedimage slot. There was no clean way to signal that the previous renderer's output was stale.

The designerlink DTO (a Studio-specific projected link) suffered from naming that implied it was a Studio-internal concern when it was in fact consumed by other services. Similarly, renderedimage was a generic name that conveyed nothing about which renderer produced it.

A migration is required because existing services depend on link.v1 and renderedimage. They cannot be switched off instantaneously; services must be migrated independently.

Decision

link.v1 is replaced by link.v2. The key structural change is that link.v2 uses a top-level oneof system with two explicit cases:

  • ecc — contains Single (single item) or MultiItem variants, each carrying a short store-scoped eccmodel_id.
  • studio — contains Single or FloatingCanvas variants, each with an optional short forced_design_id.

All IDs in link.v2 are short (tenant and store are implicit from the DTO ID path). There is no longer an ambiguous shared field: the rendering system is declared at the top level.

Two projected link DTOs replace designerlink:

  • ecclink — the ECC-side projected link, produced by the ECC Link Projector.
  • studiolink — the Studio-side projected link, produced by the Studio Link Evaluator.

Each projected link DTO carries a clean, system-specific payload with unambiguous delete signals when the originating link.v2 record is removed.

Image DTOs

renderedimage is superseded by three DTOs:

  • ecceslimage — written by the ECC renderer; contains the ECC-rendered page image.
  • studioeslimage — written by the Studio renderer; contains the Studio-rendered page image.
  • eslimage — the final merged result, written exclusively by the ESL Image Merger service.

The Merger service is the single writer of eslimage. Neither the ECC renderer nor the Studio renderer writes the final image directly. This eliminates the race condition that arose when the rendering system for an ESL changed.

Migration helper service

A Migration Helper service provides bidirectional synchronisation between link.v1 and link.v2 during the transition. Services that have not yet migrated to link.v2 continue to receive link.v1 notifications via the helper. Services that have migrated to link.v2 do not need to know whether the originating writer is still on link.v1. This allows services to be migrated independently and in any order.

Supersession summary

Old DTO Superseded by
link.v1 link.v2
designerlink studiolink
renderedimage eslimage (via ecceslimage + studioeslimage + Merger)

Consequences

Easier: - The rendering system is declared explicitly in the link DTO. Services no longer need to inspect a shared design_source oneof to determine routing. - Renderer switching (ECC ↔ Studio) cannot leave a stale final image, because Merger owns the eslimage write and will overwrite on input from either renderer. - Deleting ecclink or studiolink is an unambiguous signal to the corresponding renderer to clean up its output image. - Services can be migrated to link.v2 independently, without a coordinated cutover.

Harder: - The number of DTO types in the image pipeline increases from 1 (renderedimage) to 3 (ecceslimage, studioeslimage, eslimage), plus the Merger service must be built and operated. - The migration helper service adds a transitional operational burden: it must be kept running until all services have migrated, and its removal must be coordinated. - During the transition, the same link data exists in two places (link.v1 and link.v2). Any inconsistency in the helper's sync logic will cause divergence.

Trade-offs accepted: - The three-DTO image model adds complexity relative to a single renderedimage. This complexity is accepted because it is bounded: once all services have migrated and renderedimage is retired, the final state (eslimage as a single merged output) is simpler than the race-prone dual-writer model it replaces. - The migration helper is explicitly transitional. It will be removed once all consumers of link.v1 and renderedimage have migrated.