So baust du ein skalierbares Reporting-Backend mit List & Label Cross Platform

Die eigentliche Herausforderung beginnt nicht beim Rendern von Berichten, sondern beim Aufbau eines Reporting-Backends, das sauber skaliert. Dieser Beitrag zeigt, wie du mit List & Label Cross Platform eine moderne .NET-Architektur mit zustandslosem Rendering, zentralem Template-Management, ASP.NET Core, Docker und Observability aufbaust.

Geschätzte Lesezeit: 8 Minuten
cross platform reporting with list & label

Wichtigste Erkenntnisse

  • Nutze LLCP als zustandslose Rendering-Engine, um Windows-Abhängigkeiten zu vermeiden.
  • Implementiere den Repository-Modus, um Vorlagen zentral zu verwalten und Multi-Tenant-Architekturen zu unterstützen.
  • Betreibe das Ganze als Service mit ASP.NET Core in Docker für horizontale Skalierbarkeit.
  • Integriere Observability, um Zuverlässigkeit und Performance unter Last sicherzustellen.
  • Ziehe hybride Lösungen in Betracht zur Integration webbasierter Design-Tools.

Inhaltsverzeichnis

Warum LLCP für ein Reporting-Backend?

Wenn du ein Backend willst, das unter Linux, macOS und Windows läuft – in Containern und in CI/CD – dann benötigst du List & Label Cross Platform statt des klassischen, nur unter Windows verfügbaren List & Label.

LLCP ist eine plattformübergreifende Reporting-Engine für .NET, die speziell für Server- und Cloud-Szenarien entwickelt wurde. Sie nutzt eine einheitliche API und JSON-basierte Projektdateien, ist auf Hintergrund- und Backend-Nutzung ausgelegt und unterstützt Umgebungen wie Docker, einschließlich Linux-Containern, Azure, ARM und weitere Plattformen.

Anders gesagt: LLCP ist dafür gebaut, hinter einem HTTP-Endpunkt zu laufen und den ganzen Tag PDFs und Bilder zu erzeugen.

Das klassische List & Label bleibt relevant, wenn du den Windows Designer oder das Web Report Designer-Backend benötigst – diese setzen aktuell einen Windows-Server und IIS voraus.

Für einen reinen Render-Service ist LLCP jedoch genau die Komponente, die du im Backend einsetzen willst.

Das passende mentale Modell:

LLCP ist deine Rendering-Engine. Sie erhält Daten (eine Template-ID und ein Ausgabeformat), erzeugt das Dokument und liefert es zurück. Keine UI, kein Zustand, keine Windows-Abhängigkeit.


Das Kernmuster: Zustandsloser Render-Service

Im Zentrum der Architektur steht ein zustandsloser Service, der genau eine Aufgabe erfüllt: Rendering-Anfragen entgegennehmen und Bytes zurückliefern.

Dieses Muster wird häufig als Teil einer größeren Webanwendung eingesetzt. In solchen Fällen solltest du sicherstellen, dass jede entwickelnde Person, die an der Anwendung arbeitet, über eine eigene gültige List & Label-Lizenz verfügt.

Ablauf:

  1. Nimm eine Anfrage mit einem Vorlagenbezeichner (Dateipfad oder repository://-ID), Datenauswahlparametern (zum Beispiel customerId oder Datumsbereich) und Ausgabeformat (PDF, PNG, JSON, …) entgegen.
  2. Hole anhand der Parameter Daten aus deiner Datenquelle oder API.
  3. Rufe LLCP auf, um den Bericht zu rendern.
  4. Streame das Ergebnis zurück oder schreibe es in den Objektspeicher und gib einen Verweis zurück.

Eine minimale LLCP-Integration sieht so aus:

using combit.ListLabel31.CrossPlatform;

public class ReportService
{
    private readonly string _licenseKey = "...";

    public async Task<byte[]> RenderReportAsync(
        object dataSource,              // e.g., DataSet, IEnumerable<T>, DbDataReader
        string projectFileOrRepoId,     // path or repository://<id>
        string format                   // e.g. "PDF", "PNG"
    )
    {
        using var ll = new ListLabel
        {
            DataSource      = dataSource,
            AutoProjectFile = projectFileOrRepoId,
            LicensingInfo   = _licenseKey
        };

        using var ms = new MemoryStream();

        var exportConfig = new ExportConfiguration
        {
            ExportTarget = format,
            ExportStream = ms
        };

        ll.Export(exportConfig);

        return ms.ToArray();
    }
}

Aus Skalierungssicht sind zwei Regeln wichtiger als es auf den ersten Blick scheint.

Eine ListLabel-Instanz pro Anfrage: Halte Instanzen kurzlebig und auf einen einzelnen Aufruf beschränkt. LLCP ist für Server-/Hintergrundnutzung konzipiert; das Teilen über mehrere Anfragen hinweg führt zu Zustandsbehaftung und schwer nachvollziehbarem Verhalten.

Vermeide lokalen Dateizugriff pro Anfrage: Verwende Repository-IDs oder Templates, die in einer Datenbank oder einem Objektspeicher liegen, anstatt bei jedem Rendering vom lokalen Datenträger zu lesen. Lokaler Speicher bindet dich an einzelne Maschinen und erschwert die horizontale Skalierung.

Betrachte den Rendering-Service wie eine reine Funktion: Eingabe (Daten + Template-ID + Format) hinein, Bytes heraus, keine Nebeneffekte.


Zentrale Vorlagenverwaltung mit Repository-Modus

Das Skalieren von Reporting in Multi-Tenant- oder Multi-Instanz-Setups ist größtenteils ein Problem der Vorlagenverteilung. Vorlagendateien auf jeden Server oder Container zu kopieren, skaliert nicht und wird schnell zu einem Wartungsproblem.

LLCP löst das mit dem Repository-Modus. Anstelle von Dateipfaden arbeitest du mit logischen IDs wie repository://{GUID}. Dahinter implementierst du IRepository, das für das Laden und Speichern von Elementen (Projekte, Bilder usw.) aus deinem gewählten Datenspeicher verantwortlich ist.

Konzeptionell wird das Repository zur „Single Source of Truth“ für Vorlagen. Ein einfacher Implementierungsweg:

  1. Implementiere IRepository auf Basis deines bevorzugten Speichers (z. B. SQL-Datenbank, S3, Azure Blob, Fileshare).
  2. Speichere die JSON-basierten Projektdateien von LLCP und binäre Assets als Repository-Elemente mit Metadaten wie ID (GUID oder String-Key), Typ (Projekt, Bild usw.), Inhalt, Zeitstempel zur Cache-Invalidierung und Tenant-Informationen.
  3. Verwende im Backend repository://-IDs anstelle physischer Dateipfade.

Die Verwendung des Repository-Modus sieht folgendermaßen aus:

var reportId = "repository://a3f3e996-9b2d-4b2b-a1b8-123456789abc";
 
using var ll = new ListLabel
{
    DataSource      = dataSource,
    AutoProjectFile = reportId,
    LicensingInfo   = _licenseKey,
    Repository      = myRepositoryInstance
};
 
ll.Export(exportConfig);

Dieses Muster erschließt mehrere Skalierungsvorteile. Mehrere Container können sich dieselben Vorlagen über Datenbank- oder Blob-Speicher teilen, anstatt sie im Image mitzuliefern. Das Ausrollen von Vorlagen-Updates wird zu einer Datenänderung statt zu einem Redeploy. Außerdem kannst du Mandantenfähigkeit und Berechtigungen zentral in der Repository-Implementierung durchsetzen, anstatt diese Logik über verschiedene Services zu verteilen.

Wenn du mandantenfähiges Reporting im SaaS-Stil planst, ist der Repository-Modus praktisch zwingend erforderlich.


Bereitstellung des Backends über ASP.NET Core

Um die Rendering-Engine nutzbar zu machen, kapselst du LLCP in eine kleine HTTP-API. ASP.NET Core mit Minimal APIs hält die Oberfläche schlank und fügt sich gleichzeitig nahtlos in bestehende .NET-Backends ein.

Ein vereinfachtes Beispiel:

var builder = WebApplication.CreateBuilder(args);
 
builder.Services.AddSingleton<ReportService>();
 
var app = builder.Build();
 
app.MapPost("/render", async (RenderRequest req, ReportService svc) =>
{
    var data = await LoadDataAsync(req);  // your implementation
 
    var bytes = await svc.RenderReportAsync(
        data,
        req.ProjectId,                    // file path or repository://<id>
        req.Format                        // e.g. "PDF"
    );
 
    var contentType = req.Format switch
    {
        "PDF" => "application/pdf",
        "PNG" => "image/png",
        _     => "application/octet-stream"
    };
 
    return Results.File(
        bytes,
        contentType,
        $"report.{req.Format.ToLower()}"
    );
});
 
app.Run();
 
public record RenderRequest(
    string ProjectId,
    string Format,
    Dictionary<string, string> Parameters
);

Dieser Service bleibt bewusst schlank:

Er ist zustandslos: Vorlagen, Eingabedaten und Ergebnisse sind vollständig in Repositories, Datenbanken oder Objektspeichern ausgelagert.

Er lässt sich einfach horizontal skalieren: hinter einem Load Balancer oder API-Gateway, da jede Instanz jede Anfrage verarbeiten kann.


Containerisierung und Cloud-native Skalierung

LLCP wurde mit Blick auf containerisierte Deployments entwickelt und unterstützt moderne .NET-Runtimes. Daher ist es unkompliziert, den Service für Docker zu paketieren und in Kubernetes, ECS oder Azure App Service auszuführen.

Die wichtigsten Vorteile der Containerisierung des Reporting-Backends:

Isolierung von Abhängigkeiten: Alle nativen Bibliotheken für Schriftarten und Rendering befinden sich innerhalb des Images. Das sorgt für konsistentes Verhalten zwischen Entwicklungs-, Staging- und Produktionsumgebungen.

Konsistenz über Umgebungen hinweg: Dasselbe Container-Image (typischerweise Linux-basiert) kann von der Entwicklung bis zur Produktion verwendet werden – unabhängig davon, ob Entwickler unter Windows oder macOS arbeiten und das Deployment auf Linux-Hosts erfolgt.

Horizontale Skalierbarkeit: Das Skalieren erfolgt einfach durch Hinzufügen weiterer Replikate; LLCP-Instanzen bleiben zustandslos und in sich abgeschlossen.

Eine typische Multi-Instanz-Architektur sieht so aus:

  • Ein Cluster (Kubernetes, ECS, Azure App Service), der deinen containerisierten ASP.NET Core Reporting-Service ausführt.
  • Ein externes Repository (SQL plus Blob- oder Objektspeicher) für Vorlagen und Assets, bereitgestellt über IRepository.
  • Anwendungs- oder Analyse-Datenbanken für deine Geschäftsdaten.
  • Objektspeicher (S3, Azure Blob usw.), wenn du gerenderte Ergebnisse persistieren und statt roher Bytes URLs zurückgeben möchtest.
  • Ein Ingress-Controller oder Load Balancer, der HTTP-Traffic zu deinen Reporting-Service-Pods weiterleitet.

Da jede Anfrage ihre eigene ListLabel-Instanz erstellt und wieder verwirft und alle Daten aus externen Speichern lädt, kannst du in der Regel linear skalieren, indem du weitere Replikate hinzufügst. Begrenzende Faktoren sind dabei CPU, Arbeitsspeicher sowie die Kapazität externer Abhängigkeiten (Datenbank-Durchsatz, Netzwerk-I/O, Speicherleistung).


Optional: Web Report Designer & Viewer (Windows-basiert)

Bisher lag der Fokus auf dem Backend-Rendering-Service. Viele Teams benötigen zusätzlich interaktives Template-Design im Browser oder einen leistungsfähigen Web-Viewer. Dafür stellt List & Label in der Enterprise Edition zwei Komponenten bereit:

  • Web Report Designer für browserbasiertes Bearbeiten von Vorlagen.
  • Web Report Viewer für die interaktive Anzeige von Berichten in Webanwendungen.

Diese Komponenten basieren auf dem klassischen, Windows-basierten List & Label Stack. Das bedeutet: Das Backend muss eine ASP.NET/MVC-Anwendung unter Windows sein, du bindest die NuGet-Pakete combit.ListLabel31 und combit.ListLabel31.Web ein, und der Server läuft typischerweise unter IIS oder einer kompatiblen Umgebung.

Das unterscheidet sich vom cloud-nativen LLCP-Ansatz, aber beide lassen sich gut in einer hybriden Architektur kombinieren.

Ein gängiges Muster ist, einen Windows-basierten Design-Server zu betreiben, der den Web Report Designer hostet und Vorlagen in dein zentrales Repository schreibt, während ein plattformübergreifendes LLCP-Rendering-Backend (der zuvor beschriebene zustandslose Service) in Linux-Containern für volumenstarkes Hintergrund- und Batch-Rendering läuft. Beide Ebenen teilen sich die Vorlagen über Repository-IDs, sodass Design-Server und LLCP-Render-Farm lose gekoppelt sind.

Designer arbeiten im Browser, Vorlagen werden im Repository gespeichert, und dein skalierbares Backend greift sie beim nächsten Rendering-Aufruf auf – ohne etwas über die Designumgebung wissen zu müssen.


Performance und Observability

Reporting spielt häufig eine zentrale Rolle in geschäftskritischen Prozessen wie Abrechnung, Jahresabschluss oder kundenorientierten Dokumenten. Um einen reibungslosen Betrieb sicherzustellen, lohnt es sich, von Anfang an grundlegende Observability zu integrieren.

LLCP bringt bereits eine umfassende Logging- und Diagnose-Infrastruktur mit, die detaillierte Informationen über Rendering-, Datenzugriffs- und Exportprozesse liefert. Dein Service sollte diese Signale in deinen bestehenden Observability-Stack integrieren und durch anwendungsspezifische Metriken ergänzen.

Es gibt drei zentrale Bereiche:

Metriken: Erfasse Renderdauer, Latenz pro Vorlage, Fehlerrate, Warteschlangenlänge (falls du eine Queue vor den Service schaltest), CPU- und Speicherverbrauch pro Pod sowie Verteilungen der Exportgrößen. Diese Metriken helfen dir bei Auto-Scaling-Regeln und beim Erkennen von Regressionen.

Logging: Erfasse LLCP-Logs und -Fehler und reiche sie mit Anwendungskontext an (Template-ID, Tenant-ID, Parameterstruktur – jedoch keine sensiblen Nutzdaten). Protokolliere außerdem Fehler und Timeouts beim Repository-Zugriff und leite alles an dein zentrales Logging-System weiter.

Kapazitätsplanung: Nutze die Metriken, um SLOs zu definieren, CPU- und Speicheranforderungen sowie -Limits für deine Container festzulegen und abzuschätzen, wie viele Replikate du für Lastspitzen benötigst.

In der Praxis bedeutet das, sowohl deinen Rendering-Service als auch deine IRepository-Implementierung zu instrumentieren und sie an OpenTelemetry, Prometheus oder dein bevorzugtes APM-Tool anzubinden.


Referenzarchitektur: Alles zusammengeführt

Um die Konzepte greifbar zu machen, hier ein Blueprint, den du anpassen kannst:

1. Template-Management-Schicht

  • Ein zentrales Repository auf Basis von SQL plus Blob- oder Objektspeicher, das IRepository für LLCP implementiert.
  • Optional ein Windows-basierter Web Report Designer als Backend für die Erstellung und Bearbeitung von Vorlagen im Browser, der direkt in das Repository schreibt.
  • Governance und Metadaten (Versionierung, Mandantenfähigkeit, Berechtigungen) werden auf Repository-Ebene durchgesetzt.

2. Reporting-Backend-Schicht (LLCP)

  • Ein zustandsloser ASP.NET Core Service, der LLCP für das Rendering nutzt und als Docker-Image paketiert ist.
  • Deployment in Kubernetes, ECS oder Azure App Service auf Linux oder in gemischten Umgebungen, mit aktivierter horizontaler Skalierung.
  • Verwendung von repository://-IDs und einer gemeinsamen IRepository-Implementierung zur Auflösung von Vorlagen und Assets.
  • Optionale Integration von Queues (z. B. SQS, Service Bus, RabbitMQ) für langlaufende oder umfangreiche Reporting-Jobs.

3. API-Konsumenten

  • Fachanwendungs-Backends, Web-Frontends, geplante Jobs oder serverlose Funktionen rufen die Render-API mit Template-IDs und Parametern auf.
  • Optionale Integration des Web Report Viewer (Windows-basiert) in bestehende .NET-Webanwendungen für interaktive Vorschauen, während Bulk- oder zeitgesteuerte Exporte über den LLCP-Rendering-Service laufen.

Von außen sehen andere Services nur eine einfache API:
„Rendere dieses Template mit diesen Parametern in diesem Format.“

Im Hintergrund übernehmen LLCP, der Repository-Modus und deine Container-Plattform die Skalierung, die Verteilung der Vorlagen und das plattformübergreifende Verhalten.


Fazit

Ein skalierbares Reporting-Backend muss kein Geflecht aus Skripten, gemeinsamen Netzlaufwerken und fragilen Windows-Services sein. Mit List & Label Cross Platform kannst du Reporting wie jede andere Backend-Komponente behandeln: hinter einer HTTP-API kapseln, in Containern betreiben, Konfiguration und Vorlagen zentral speichern und bei wachsender Last horizontal skalieren.

Die wichtigsten Punkte im Überblick:

  • Nutze LLCP als zustandslose Rendering-Engine, anstatt dein Backend an klassische, rein Windows-basierte Komponenten zu binden.
  • Setze früh auf den Repository-Modus, um Vorlagen zu zentralisieren, Multi-Tenant-Szenarien zu unterstützen und Deployments von Template-Rollouts zu entkoppeln.
  • Kapsle LLCP in einen schlanken ASP.NET Core Service, halte ihn zustandslos und betreibe ihn in Docker für vorhersehbares, plattformübergreifendes Verhalten.
  • Integriere von Anfang an Observability, damit du dem System auch unter Last vertrauen kannst.
  • Ergänze bei Bedarf Web Report Designer / Viewer in einem separaten, Windows-basierten Backend für browserbasiertes Design und interaktive Anzeige.

Mit dieser Architektur wird die Erstellung von PDFs – egal ob für einige wenige Rechnungen oder für Millionen monatlicher Abrechnungen – zu einer Frage der Skalierung deiner Container, nicht des Neuaufbaus deiner Reporting-Infrastruktur.

FAQ

Was ist List & Label Cross Platform (LLCP)?

List & Label Cross Platform (LLCP) ist eine plattformübergreifende Reporting-Engine für .NET, die für Server- und Cloud-Szenarien entwickelt wurde und horizontale Skalierbarkeit sowie Kompatibilität über verschiedene Betriebssysteme hinweg ermöglicht.

Warum sollte ich den Repository-Modus verwenden?

Der Repository-Modus zentralisiert die Verwaltung von Vorlagen, unterstützt Multi-Tenant-Szenarien und entkoppelt Deployments von Template-Rollouts, wodurch sich Reporting-Lösungen einfacher warten und skalieren lassen.

Kann LLCP in einer containerisierten Umgebung betrieben werden?

Ja, LLCP ist für containerisierte Deployments ausgelegt und unterstützt Docker, Kubernetes, ECS und Azure App Service, wodurch konsistentes Verhalten über verschiedene Umgebungen hinweg gewährleistet wird.

Welche Rolle spielt Observability in Reporting-Systemen?

Observability ermöglicht das Tracking von Performance-Metriken, das Erfassen von Fehlern sowie die Kapazitätsplanung und stellt sicher, dass das Reporting-System unter Last zuverlässig und skalierbar bleibt.

Was ist, wenn ich ein webbasiertes Design-Tool benötige?

Du kannst den Web Report Designer aus der Enterprise Edition verwenden. Dieser benötigt jedoch ein Windows-basiertes Backend; er kann mit einem LLCP-Rendering-Backend für hochvolumiges Rendering kombiniert werden.

Empfohlene Artikel