All resources

Diagram Cloud & Automation

Report Engine Architecture Reference

A concise reference for report request validation, cache decisions, Fabric queries, asynchronous delivery and observability.

Type
Diagram
Level
Intermediate
Updated
Format
Printable

In short

Trace UI and API requests through a secure report engine, tenant-aware cache or query path, result construction, asynchronous delivery, and monitoring.

Who it is for

  • Engineers designing analytical APIs and report services
  • Architects reviewing tenant isolation and caching

What it helps you do

  • Explain the request lifecycle and cache decision
  • Identify security, asynchronous delivery, and monitoring boundaries

Core path

UI or API clients call a gateway. The gateway establishes identity and coarse limits. The report engine authorizes the report, validates tenant and parameters, applies workload policy, checks a tenant-aware cache, and on a miss queries Fabric through controlled templates. The result builder produces an approved format and returns a bounded response or protected artifact.

Client → API Management or Gateway → Report Engine → Cache or Query → Fabric Warehouse or Lakehouse SQL endpoint → Result builder → Client

1 Consumers

Web UIDashboards, filters, downloads
External API clientsPartners and integrations

2 Edge

API ManagementAzure API ManagementToken validation · rate limits · routing · correlation ID

3 Report Engine

Report EngineApp Service or Azure Functions
  1. 1Authenticate & authorizeCaller, report and format allowed
  2. 2Validate requestRanges, filters, row and byte limits
  3. 3Tenant rulesTenant from trusted claims only
  4. 4Cache lookupKey: tenant · report · params · format · version
  5. 5Query builderApproved templates, bound parameters
  6. 6Result builderJSON · CSV · Parquet

4 Microsoft Fabric

Fabric WarehouseMicrosoft FabricCurated serving tables
Lakehouse SQL endpointMicrosoft FabricRead-only T-SQL over Delta tables

Cached and generated outputs

Report cacheAzure Storage AccountRead on cache lookup, written by the result builder
  • Azure Storage Account · private endpoint, encryption at rest
    • report-cacheBlob container for report outputs
      • {tenant}Tenant boundary: access and cache keys start here
        • {report-name}One folder per report definition
          • {yyyy}/{mm}/{dd}Date partitions; lifecycle rules expire old outputs
            • {request-id}.{format}One output per request: json · csv · parquet

Example report-cache/contoso/sales-by-region/2026/10/03/7f3c9a1e.csv

Output path

  1. Small, bounded result

    1. Result builder
    2. JSON in the response
    3. Web UI or API client
  2. Large result or file

    1. Result builder
    2. CSV / Parquet written to Storage
    3. Short-lived download link
    4. Client downloads
  3. Long-running request (asynchronous)

    1. POST /reports
    2. 202 Accepted + request ID
    3. GET /reports/{id} status
    4. Download link when succeeded

    Status: queued · running · succeeded · failed · expired

Monitoring Azure Monitor · Application Insights

  • Request and correlation IDs
  • Latency per step
  • Cache hit rate per tenant
  • Fabric query duration
  • Failures, throttling and retries
Report Engine on Azure and Microsoft Fabric. Requests pass API Management and the engine’s checks; cache hits are served from the tenant’s folder in the Storage account, misses query Fabric. Small results return as JSON, larger files are written to Storage and delivered through a short-lived link. Every step reports to monitoring.

Request lifecycle

  1. API Management

    Step 1: Receive request

    Assign a request ID, accept a correlation ID

  2. Report Engine

    Step 2: Validate request

    Report, format, date range, row and byte limits

  3. Report Engine

    Step 3: Resolve tenant & security

    Tenant from trusted claims; authorize report and filters

  4. Storage account

    Step 4: Check cache

    Tenant-aware key; fresh output → skip to step 6 or 8

  5. Microsoft Fabric

    Step 5: Query Fabric if needed

    Approved template, bound parameters, timeout

    Only on a cache miss

  6. Report Engine

    Step 6: Build result

    Shape rows into JSON, CSV or Parquet

  7. Storage account

    Step 7: Store output / cache result

    Write to the tenant’s folder with a TTL

  8. Report Engine

    Step 8: Return response

    JSON body or short-lived download link

The lifecycle of one report request. Steps 1–4 always run; step 5 runs only on a cache miss; every request ends with a stored or reused output and a response that carries its request ID.

In detail:

  1. Generate a request ID and accept a correlation ID according to a controlled policy.
  2. Authenticate the caller and derive tenant identity from trusted claims.
  3. Authorize the report, dimension, filters, and delivery format.
  4. Validate date order, maximum range, row and byte limits.
  5. Normalize the request into a canonical model.
  6. Apply tenant and client rate or concurrency limits.
  7. Check a cache key containing tenant, report type, range, dimensions, filters, format, and report version.
  8. On a miss, bind validated values to an approved Fabric query.
  9. Build JSON, CSV, Parquet, or another permitted result.
  10. Store and cache only with explicit freshness, expiration, encryption, and access policy.

Cache decision

  1. RequestValidated, tenant resolved
  2. Cache keytenantreportparametersformatversion
  3. Look upIn report-cache, under{tenant}/{report}/…
  4. Cached and fresh?Within the report’s TTL

Yes Cache hit

  1. Authorize again for this caller
  2. Read the stored output
  3. Return result

No Cache miss

  1. Query Fabric (approved template)
  2. Build output: JSON, CSV or Parquet
  3. Store under the tenant’s path, with a TTL
  4. Return result

Tenant-aware: same report, same parameters, different tenants

  • contosocontoso · sales-by-region · 2026-09 · csv · v3report-cache/contoso/sales-by-region/…
  • fabrikamfabrikam · sales-by-region · 2026-09 · csv · v3report-cache/fabrikam/sales-by-region/…

Two keys, two folders: never shared.

Cache hit or miss. The tenant is the first part of the cache key and of the storage path, so two tenants asking for the same report with the same parameters never share an entry. A hit is still authorized; a miss queries Fabric and stores the new output for the next request.

A cache hit still requires authorization. A miss is not automatically safe to execute: cost and concurrency policy apply first. TTL follows the report’s freshness objective. Invalidation may follow a completed data refresh or a bounded time policy. Never share cache entries across tenants merely because other parameters match.

Asynchronous path

Small, bounded requests may return synchronously. Large work can use:

POST /reports → 202 Accepted plus reportRequestId

GET /reports/{id} → queued, running, succeeded, failed, or expired

GET /reports/{id}/download → short-lived authorized file access

Retries use idempotency so the same logical request does not create uncontrolled duplicate jobs or artifacts.

Monitoring fields

Capture request_id, correlation_id, tenant_id, client_id, api_key_id, report_name, requested_at, started_at, completed_at, duration_ms, cache_hit, query_duration_ms, rows_returned, output_bytes, status, and error_code. Protect high-cardinality and tenant data, but preserve enough context to connect a user symptom to gateway, engine, cache, query, and Fabric evidence.

Review points

Confirm tenant identity cannot be overridden by a request parameter. Ensure query generation is allow-listed and parameterized. Test stale cache, cache corruption, Fabric timeout, caller cancellation, oversized result, duplicate submission, and expired download. Review authorization again when a result is downloaded, not only when it was generated.

Planned articles on these topics

Tags

  • Data Architecture
  • API
  • Reporting
  • Caching
  • Microsoft Fabric
  • Multi-Tenancy
  • Observability