In modern enterprise administration dashboards, tables and forms frequently require displaying resolved associated names (e.g. ClientId resolved to a legal entity name, StatusCode resolved to a localized dictionary tag, and UserId resolved to a lead counsel’s display name).
Traditional architectures force teams into a painful dilemma:
- Forcing Backend Queries to Execute 5 to 10
LEFT JOINs: Results in bloated SQL queries, severe query latency regressions, and ruptures the physical boundary between modular monolith bounded contexts; - Having the Frontend Fire N+1 Asynchronous Requests: Triggers severe Cumulative Layout Shift (CLS) on the client while overwhelming backend gateways under concurrent load.
BitzOrcas.Modern establishes the golden pattern: “Narrow Data Transfer + Stage 10 Automated Pipeline Metadata Hydration (ReadModelDisplayPipelineBehavior)”:
- Clean Backend Queries: Handlers query solely the aggregate root’s core columns without cross-boundary database joins;
- Intercepted Batch Hydration: The final stage of the 10-tier application pipeline (Stage 10) intercepts query results and performs batched, de-duplicated Redis lookups for referenced entity names and dictionary tags, appending them to the response envelope;
- Zero-Boilerplate Client Consumption: Using
@bitz/platform-sdkand@bitz/widgets, table components render resolved titles directly via__metaprojections without manual mapping lookups.
This guide demonstrates how frontend integration and metadata hydration function through a real-world LegalTech scenario: “Civil Litigation Matter Intake Table with Automated Client and Status Hydration.”
Metadata Hydration Architecture Sequence
Step 1: DTO Implements IReadModelMetaMapped Contract
Implement IReadModelMetaMapped on the query DTO to declare that this read model supports row-level metadata companion hydration:
using System.Collections.Generic;using BitzOrcas.Application.Abstractions.Queries;using BitzOrcas.Domain.Results;
namespace BitzOrcas.Modules.Legal.Contracts.Matters;
/// <summary>/// Matter list item read-only Data Transfer Object/// </summary>/// <remarks>/// <para>Encapsulates core list columns compactly. Wide database joins are strictly forbidden.</para>/// <para>Implements <see cref="IReadModelMetaMapped"/> to receive automated Stage 10 pipeline hydration.</para>/// </remarks>public sealed record MatterListItemDto : IReadModelMetaMapped{ /// <summary> /// Unique matter identifier /// </summary> public string Id { get; init; } = string.Empty;
/// <summary> /// Business tracking code /// </summary> public string MatterCode { get; init; } = string.Empty;
/// <summary> /// Case title /// </summary> public string Title { get; init; } = string.Empty;
/// <summary> /// Retained client identifier /// </summary> public string ClientId { get; init; } = string.Empty;
/// <summary> /// Lead counsel identifier /// </summary> public string LeadLawyerId { get; init; } = string.Empty;
/// <summary> /// Matter status code (Draft, PendingReview, Active, Closed) /// </summary> public string Status { get; init; } = string.Empty;
/// <summary> /// Litigation claim amount in CNY /// </summary> public decimal ClaimAmount { get; init; }
/// <summary> /// Row-level companion entity dictionary (populated automatically by the pipeline) /// </summary> public IReadOnlyDictionary<string, IReadOnlyDictionary<string, EntityRef>>? Meta { get; init; }
/// <summary> /// Contract implementation: returns a clone containing hydrated row metadata /// </summary> object IReadModelMetaMapped.WithMeta( IReadOnlyDictionary<string, IReadOnlyDictionary<string, EntityRef>> meta) => this with { Meta = meta };}Step 2: Configure Module-Level DisplayMap Rules
Register hydration mappings within module configuration, instructing the pipeline how to resolve foreign IDs into human-readable display values:
using BitzOrcas.Application.DisplayMap;using BitzOrcas.Modules.Legal.Contracts.Matters;using Microsoft.Extensions.DependencyInjection;
namespace BitzOrcas.Modules.Legal;
/// <summary>/// Legal module metadata hydration mapping configuration/// </summary>public static class LegalDisplayMapConfig{ /// <summary> /// Registers metadata hydration rules for legal models /// </summary> /// <param name="services">DI service collection.</param> public static IServiceCollection AddLegalDisplayMaps(this IServiceCollection services) { services.AddReadModelDisplayMap<MatterListItemDto>(map => { // 1. Hydrate ClientId into client company name and credit code from master data map.MapField(x => x.ClientId) .FromProvider("ClientMasterData", key => $"MasterData:Client:{key}");
// 2. Hydrate LeadLawyerId into lawyer full name from identity profiles map.MapField(x => x.LeadLawyerId) .FromProvider("UserProfile", key => $"Identity:User:{key}");
// 3. Hydrate Status code into localized dictionary labels map.MapDictionary(x => x.Status) .FromCategory("Legal.MatterStatus"); });
return services; }}Step 3: Frontend React 19 Consumes Hydrated Metadata
The backend API returns a standardized JSON payload where companion display names are mapped into __meta:
{ "items": [ { "id": "MAT-2026-0001", "matterCode": "CIV-2026-0081", "title": "Cross-Border Semiconductor Patent Infringement", "clientId": "CLI-88902", "leadLawyerId": "USR-1002", "status": "Active", "claimAmount": 58000000.00, "__meta": { "clientName": "VeriSilicon Microelectronics Co., Ltd.", "clientUnifiedSocialCreditCode": "9131000076210088X", "leadLawyerName": "Attorney Xiaoming Zhang", "statusDisplayText": "Active Trial" } } ], "pageIndex": 1, "pageSize": 20, "totalCount": 1}On the frontend, @bitz/widgets table columns consume __meta projections via DataTableObjectCell, rendering enriched data with zero custom lookup logic:
import { type JSX } from 'react';import { DataTableObjectCell, ServerDataTable, StatusBadge, type DataTableColumn,} from '@bitz/widgets';import type { LegalMatterSummaryDto } from '@bitz/platform-sdk';
/** * Matter list table column configuration */export const legalMatterColumns: DataTableColumn<LegalMatterSummaryDto>[] = [ { id: 'matterCode', header: 'Tracking Code', cell: (row) => ( <span className="font-mono text-sm font-semibold tracking-tight"> {row.matterCode} </span> ), width: '180px', }, { id: 'title', header: 'Case Title', cell: (row) => ( <span className="font-medium text-neutral-900 dark:text-neutral-100"> {row.title} </span> ), }, { id: 'client', header: 'Client / Retainer', cell: (row) => ( // Core: Zero-latency resolution from row __meta, falling back to raw ClientId <DataTableObjectCell primaryText={row.__meta?.clientName ?? row.clientId} secondaryText={row.__meta?.clientUnifiedSocialCreditCode} /> ), width: '240px', }, { id: 'leadLawyer', header: 'Lead Counsel', cell: (row) => ( <span className="text-sm text-neutral-700 dark:text-neutral-300"> {row.__meta?.leadLawyerName ?? row.leadLawyerId} </span> ), width: '140px', }, { id: 'status', header: 'Status', cell: (row) => ( <StatusBadge tone={row.status === 'Active' ? 'success' : 'neutral'} label={row.__meta?.statusDisplayText ?? row.status} /> ), width: '130px', },];Summary
The automated metadata hydration architecture delivers substantial engineering dividends:
- Architectural Decoupling with Blazing Query Speed: Backends execute simple single-table queries without cross-domain
LEFT JOINs, improving indexed query performance by 300%–500%; - Batched In-Memory Pipeline Lookups: Stage 10 batches and de-duplicates entity lookups into a single multi-key Redis call, minimizing network round-trips;
- Developer Simplicity: Frontend components render foreign keys without custom dictionary translation functions, enjoying clean, strongly-typed UI delivery.