Azure App Service (Linux)
Hostet die .NET-10-Anwendung mit WebSockets für Blazor Server, Always On und Health-Check-Pfad. HTTPS mit kostenlosem, verwaltetem Zertifikat für die eigene Domain.
Ein modularer Monolith mit .NET 10 und Blazor, betrieben auf Azure App Service mit Azure SQL – beschrieben als Infrastruktur-Code und automatisch ausgeliefert. Klein genug für eine Bewerbungsseite, aufgebaut wie ein echtes Produkt.
Alle Ressourcen liegen in einer Ressourcengruppe und werden per Bicep angelegt. Die App verbindet sich passwortlos per Managed Identity mit der Datenbank.
Hostet die .NET-10-Anwendung mit WebSockets für Blazor Server, Always On und Health-Check-Pfad. HTTPS mit kostenlosem, verwaltetem Zertifikat für die eigene Domain.
Serverless im Rahmen des kostenlosen Angebots: pausiert bei Inaktivität und wacht beim ersten Zugriff auf. Die Anwendung ist darauf ausgelegt und bleibt währenddessen bedienbar.
Kein Passwort in Konfiguration oder Pipeline: App Service authentifiziert sich mit seiner Identität bei Microsoft Entra ID und damit bei Azure SQL.
Requests, Abhängigkeiten, Fehler und Logs per OpenTelemetry – mit Log Analytics als Speicher.
kenokoch.de zeigt auf Azure App Service. Die Verbindung ist per HTTPS gesichert, www wird auf die Hauptdomain umgeleitet.
Kestrel rendert die Seite als HTML – schnell und für Suchmaschinen lesbar. Security-Header inklusive Content-Security-Policy mit Nonce kommen mit.
Nur die Teile, die interaktiv sein müssen, bauen eine SignalR-Verbindung auf (Live-Zähler, SPS, Gästebuch). Der Rest bleibt reines HTML.
Auf der Demo-Seite lädt der Browser die .NET-Runtime nach und führt C# lokal aus – ohne Server-Roundtrips.
EF Core spricht über Managed Identity mit Azure SQL. Statistik wird gepuffert und gebündelt geschrieben, damit kein Request auf die Datenbank wartet.
Jeder Request landet als Telemetrie in Application Insights; der Server-Timing-Header zeigt die Renderzeit im Browser.
Jedes Modul ist ein eigenes Projekt mit Domäne, Persistenz, API und UI. Der Host setzt sie zusammen – so wie die Unternehmensplattform, an der ich arbeite.
using KenoKoch.BuildingBlocks.Modules;
using KenoKoch.BuildingBlocks.Persistence;
using KenoKoch.Modules.Guestbook.Application;
using KenoKoch.Modules.Guestbook.Contracts;
using KenoKoch.Modules.Guestbook.Persistence;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace KenoKoch.Modules.Guestbook;
/// <summary>Modul "Gästebuch": Besucher hinterlassen Grüße, gespeichert mit EF Core im Schema [guestbook].</summary>
public sealed class GuestbookModule : IModule
{
public string Name => "Guestbook";
public string DisplayName => "Gästebuch";
public string Description => "Einträge von Besuchern: Validierung, Spam-Schutz und Persistenz mit EF Core im eigenen Schema.";
public string? DatabaseSchema => GuestbookDbContext.SchemaName;
public IReadOnlyCollection<Type> PublishedEvents => [typeof(GuestbookEntrySubmitted)];
public void AddServices(IServiceCollection services, IConfiguration configuration)
{
services.AddOptions<GuestbookOptions>().Bind(configuration.GetSection(GuestbookOptions.SectionName));
services.AddModuleDbContext<GuestbookDbContext>(configuration, GuestbookDbContext.SchemaName);
services.AddSingleton<SubmissionThrottle>();
services.AddScoped<GuestbookService>();
}
public void MapEndpoints(IEndpointRouteBuilder endpoints)
{
endpoints.MapGet("/guestbook", GetEntriesAsync)
.WithName("GetGuestbookEntries")
.WithSummary("Freigegebene Gästebucheinträge")
.WithDescription("Liefert die neuesten freigegebenen Einträge aus Azure SQL (Parameter take: 1–50).");
}
private static async Task<Results<Ok<GuestbookApiResponse>, ProblemHttpResult>> GetEntriesAsync(
GuestbookService service,
DatabaseState database,
int? take,
CancellationToken cancellationToken)
{
if (!database.IsReady)
{
return TypedResults.Problem(
title: "Datenbank derzeit nicht verfügbar",
detail: database.Message,
statusCode: StatusCodes.Status503ServiceUnavailable);
}
var page = await service.GetLatestAsync(take ?? 10, cancellationToken);
var entries = page.Entries
.Select(e => new GuestbookApiEntry(e.Name, e.Company, e.RoleLabel, e.Message, e.CreatedAt))
.ToArray();
return TypedResults.Ok(new GuestbookApiResponse(page.TotalCount, entries, Math.Round(page.Query.Duration.TotalMilliseconds, 2)));
}
}
Architektur ist eine Abfolge von Abwägungen. Hier sind die wichtigsten, kurz begründet.
Eine Sprache, ein Typsystem und geteilter Code von der Datenbank bis zur Oberfläche. Für Business-Anwendungen im .NET-Umfeld spart das Kontextwechsel und doppelte Modelle.
Abwägung: Das Ökosystem an UI-Komponenten ist kleiner als bei React – für Formulare, Tabellen und Dashboards aber mehr als ausreichend.
Seiten werden als HTML ausgeliefert und laden schnell. Interaktivität gibt es nur dort, wo sie gebraucht wird – und dann gezielt als Server- oder WebAssembly-Komponente.
Abwägung: Mehr Nachdenken über Render-Modi pro Komponente, dafür deutlich weniger Last und bessere Ladezeiten.
Ein Deployment, eine Datenbank, einfache Transaktionen – aber harte Grenzen zwischen Fachlichkeiten. Einzelne Module ließen sich später herauslösen, wenn es wirklich nötig wird.
Abwägung: Grenzen müssen aktiv geschützt werden – hier durch Architekturtests im CI.
Jedes Modul besitzt seine Tabellen und Migrationen. Kein Modul liest fremde Tabellen; Daten fließen über Events.
Abwägung: Abfragen über Modulgrenzen hinweg brauchen eine API oder ein Read-Model statt eines einfachen JOINs.
Für eine Seite mit wenig, aber unregelmäßigem Traffic ideal: Abrechnung nach Nutzung bzw. im kostenlosen Kontingent, automatische Pause bei Inaktivität.
Abwägung: Kaltstart nach der Pause – die Anwendung zeigt das transparent an und blockiert nie den Seitenaufbau.
Keine Geheimnisse in appsettings, Pipeline oder Key Vault für die Datenbank. Rechte werden über Microsoft Entra ID vergeben und sind nachvollziehbar.
Abwägung: Einmaliges Setup des Datenbankbenutzers per T-SQL nach dem ersten Deployment.