.NET Developer-Plattformen im Vergleich: ASP.NET Core, ABP, PlatformPlatform & Aspire
Eine technische Analyse für moderne .NET Entwickler - Was bietet ASP.NET Core out-of-the-box? Wann brauchen Sie ABP Framework, PlatformPlatform oder .NET Aspire? Produktivität vs. Lock-in-Effekte im Detail.
.NET Developer-Plattformen im Vergleich: Wann brauchen Sie mehr als ASP.NET Core?
Als .NET Entwickler stehen Sie heute vor einer interessanten Entscheidung: ASP.NET Core bietet bereits enorm viel out-of-the-box. Aber wann lohnt sich eine Developer-Plattform wie ABP Framework, PlatformPlatform oder .NET Aspire?
Diese Frage beschäftigt mich seit Jahren - sowohl als CTO, der Architektur-Entscheidungen treffen muss, als auch als Entwickler, der produktiv sein will ohne in Boilerplate-Code zu ertrinken.
In diesem Artikel analysiere ich:
- Was ASP.NET Core bereits mitbringt (und oft unterschätzt wird)
- Was die drei führenden .NET Developer-Plattformen zusätzlich bieten
- Wann Sie welche Plattform wählen sollten
- Die Trade-offs: Produktivität vs. Lock-in-Effekte
ASP.NET Core out-of-the-box: Mehr als Sie denken
Bevor wir über Plattformen sprechen: ASP.NET Core ist bereits erstaunlich feature-reich. Viele Entwickler wissen nicht, was sie mit minimalen Dependencies bekommen.
1. Logging: Strukturiert und erweiterbar
Out-of-the-box seit .NET 6:
var builder = WebApplication.CreateBuilder(args);
// Logging ist bereits konfiguriert!
var app = builder.Build();
app.MapGet("/api/users/{id}", (int id, ILogger<Program> logger) =>
{
logger.LogInformation("Fetching user {UserId}", id);
// ...
return Results.Ok(user);
});
Was Sie bekommen:
- Strukturiertes Logging (nicht nur Strings, sondern Key-Value-Pairs)
- Log-Levels (Trace, Debug, Information, Warning, Error, Critical)
- Scopes für Kontext (automatisch bei HTTP-Requests)
- Providers: Console, Debug, EventSource (Windows), EventLog (Windows)
Mit einem NuGet-Paket erweitern:
dotnet add package Serilog.AspNetCore
builder.Host.UseSerilog((context, config) =>
{
config
.WriteTo.Console()
.WriteTo.File("logs/app-.txt", rollingInterval: RollingInterval.Day)
.WriteTo.Seq("http://localhost:5341"); // Zentrales Logging
});
Ergebnis: Production-ready Logging mit 3 Zeilen Code.
2. Authentifizierung & Authorization: Identity + JWT
ASP.NET Core Identity out-of-the-box:
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
// Startup
builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
// Usage
app.MapPost("/api/auth/register", async (
UserManager<ApplicationUser> userManager,
RegisterRequest request) =>
{
var user = new ApplicationUser { UserName = request.Email, Email = request.Email };
var result = await userManager.CreateAsync(user, request.Password);
return result.Succeeded
? Results.Ok()
: Results.BadRequest(result.Errors);
});
Was Sie bekommen:
- Password-Hashing (PBKDF2, sicher by default)
- User-Management (Create, Update, Delete, Find)
- Role-Based-Authorization (Roles, Claims, Policies)
- Two-Factor-Authentication (TOTP, Email, SMS)
- Account-Lockout (nach X fehlgeschlagenen Logins)
- Email-Confirmation & Password-Reset
JWT-Authentication mit einem NuGet-Paket:
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "your-issuer",
ValidAudience = "your-audience",
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes("your-secret-key"))
};
});
// Protected endpoint
app.MapGet("/api/protected", [Authorize] () => "Secret data");
Ergebnis: Production-ready JWT-Auth mit 10-15 Zeilen Code.
3. Dependency Injection: Built-in und mächtig
Keine externen Packages nötig:
// Register
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddSingleton<ICacheService, RedisCacheService>();
builder.Services.AddTransient<IEmailSender, SmtpEmailSender>();
// Usage (automatisch injected)
app.MapGet("/api/users", (IUserService userService) =>
userService.GetAllAsync());
Lifetimes:
- Singleton: Eine Instanz für gesamte App-Lifetime
- Scoped: Eine Instanz pro HTTP-Request
- Transient: Neue Instanz bei jedem Inject
Advanced: Keyed Services (.NET 8+):
builder.Services.AddKeyedScoped<IPaymentProvider, StripeProvider>("stripe");
builder.Services.AddKeyedScoped<IPaymentProvider, PayPalProvider>("paypal");
app.MapPost("/api/payment", (
[FromKeyedServices("stripe")] IPaymentProvider provider,
PaymentRequest request) =>
{
return provider.ProcessAsync(request);
});
4. Configuration: Flexible und typsicher
Appsettings.json + Environment Variables + User Secrets:
// appsettings.json
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=MyDb;..."
},
"JwtSettings": {
"Secret": "your-secret-key",
"ExpirationMinutes": 60
}
}
// Typsichere Configuration
public class JwtSettings
{
public string Secret { get; set; }
public int ExpirationMinutes { get; set; }
}
builder.Services.Configure<JwtSettings>(
builder.Configuration.GetSection("JwtSettings"));
// Usage
app.MapGet("/api/config", (IOptions<JwtSettings> settings) =>
{
return settings.Value.ExpirationMinutes;
});
User Secrets (für Entwicklung, keine Secrets in Git):
dotnet user-secrets init
dotnet user-secrets set "JwtSettings:Secret" "dev-secret-123"
5. Weitere out-of-the-box Features
Health Checks:
builder.Services.AddHealthChecks()
.AddSqlServer(connectionString)
.AddRedis(redisConnection);
app.MapHealthChecks("/health");
CORS:
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowFrontend", policy =>
{
policy.WithOrigins("https://frontend.com")
.AllowAnyMethod()
.AllowAnyHeader();
});
});
Rate Limiting (.NET 7+):
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("fixed", limiterOptions =>
{
limiterOptions.PermitLimit = 100;
limiterOptions.Window = TimeSpan.FromMinutes(1);
});
});
app.MapGet("/api/limited", [EnableRateLimiting("fixed")] () => "Rate limited");
Output Caching (.NET 7+):
builder.Services.AddOutputCache();
app.MapGet("/api/products", [OutputCache(Duration = 60)] () => GetProducts());
Zwischenfazit: ASP.NET Core ist bereits mächtig
Mit ASP.NET Core + 3-5 NuGet-Paketen bekommen Sie:
- ✅ Strukturiertes Logging (Serilog)
- ✅ Authentifizierung & Authorization (Identity + JWT)
- ✅ Dependency Injection (built-in)
- ✅ Configuration-Management (built-in)
- ✅ Health Checks, CORS, Rate Limiting, Caching
Für viele Projekte reicht das völlig.
Aber: Es gibt Bereiche, wo ASP.NET Core bewusst keine Meinung hat:
- Architektur-Pattern: Wie strukturieren Sie Ihren Code? (Layered, Clean, DDD?)
- Cross-Cutting-Concerns: Wie implementieren Sie Audit-Logging, Validation, Exception-Handling konsistent?
- Multi-Tenancy: Wie bauen Sie SaaS mit Tenant-Isolation?
- Microservices-Orchestrierung: Wie entwickeln Sie lokal mit 10+ Services?
- Boilerplate-Reduktion: Wie vermeiden Sie repetitive CRUD-Code?
Hier kommen Developer-Plattformen ins Spiel.
Plattform 1: ABP Framework - Das Enterprise-Schweizer-Taschenmesser
Repository: github.com/abpframework/abp Stars: 13.9k ⭐ | License: LGPL-3.0 (Open-Source) + Commercial Zielgruppe: Enterprise-Anwendungen, SaaS-Produkte, komplexe Business-Logic
Was ist ABP?
ABP ist ein vollwertiges Application Framework für ASP.NET Core, das auf Domain-Driven Design (DDD) basiert. Es positioniert sich als "Bridge between ASP.NET Core and real-world business application requirements".
Die Vision: Entwickler sollen sich auf Business-Logic konzentrieren, nicht auf Infrastructure-Code.
Architektur-Patterns & Prinzipien
ABP ist hochgradig opinionated und bringt klare Architektur-Vorgaben:
1. Domain-Driven Design (DDD):
MyProject.Application (Application-Services, DTOs)
MyProject.Domain (Entities, Domain-Services, Repositories)
MyProject.Domain.Shared (Enums, Constants)
MyProject.EntityFrameworkCore (EF-Core-Implementierung)
MyProject.HttpApi (Controllers)
MyProject.HttpApi.Client (HTTP-Clients für Microservices)
2. Multi-Layered Architecture:
- Presentation Layer: UI/API
- Application Layer: Use-Cases, DTOs, Validation
- Domain Layer: Business-Logic, Entities, Domain-Events
- Infrastructure Layer: Datenbank, External-Services
3. Modularität: Jedes Feature kann als ABP-Modul strukturiert werden:
[DependsOn(
typeof(AbpIdentityApplicationModule),
typeof(AbpAccountApplicationModule)
)]
public class MyProjectApplicationModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
// Modul-spezifische Services
}
}
Was bietet ABP zusätzlich zu ASP.NET Core?
1. Vorgefertigte Module (Pre-Built Functionality):
ABP liefert Production-Ready-Module, die Sie sofort nutzen können:
| Modul | Funktionalität |
|---|---|
| Identity | User-Management, Roles, Permissions, Claims |
| Account | Login, Register, Forgot-Password, Email-Confirmation |
| Tenant Management | Multi-Tenancy für SaaS (Tenant-Isolation in DB) |
| OpenIddict | OAuth2/OpenID Connect Server (Token-Issuing) |
| Audit Logging | Automatisches Logging aller Entity-Changes |
| Feature Management | Feature-Toggles pro Tenant |
| Setting Management | Hierarchische Settings (Global, Tenant, User) |
| Background Jobs | Job-Queue mit Retry-Logic (Hangfire/Quartz-Integration) |
| BLOB Storage | Abstraktion für File-Storage (Azure, AWS, Filesystem) |
Beispiel: Tenant-Management
// Multi-Tenancy aktivieren
[DependsOn(typeof(AbpTenantManagementApplicationModule))]
public class MyModule : AbpModule { }
// Automatische Tenant-Isolation
public class ProductAppService : ApplicationService
{
private readonly IRepository<Product> _productRepository;
public async Task<List<ProductDto>> GetListAsync()
{
// ABP filtert automatisch nach aktuellem Tenant!
var products = await _productRepository.GetListAsync();
return ObjectMapper.Map<List<Product>, List<ProductDto>>(products);
}
}
Das Magische: ABP injiziert automatisch ITenantId und filtert alle Queries. Sie schreiben keinen Tenant-spezifischen Code.
2. Cross-Cutting-Concerns: Automatisiert
ABP übernimmt repetitive Aufgaben automatisch:
Validation:
public class CreateProductDto
{
[Required]
[StringLength(128)]
public string Name { get; set; }
[Range(0, 999999)]
public decimal Price { get; set; }
}
// Validation passiert automatisch vor Application-Service-Call
public class ProductAppService : ApplicationService
{
public async Task CreateAsync(CreateProductDto input)
{
// Wenn wir hier ankommen, ist input bereits validiert!
}
}
Authorization:
[Authorize(MyPermissions.Products.Create)]
public async Task CreateAsync(CreateProductDto input)
{
// Automatischer Permission-Check
}
Audit-Logging:
public class Product : FullAuditedEntity<Guid>
{
public string Name { get; set; }
// FullAuditedEntity gibt uns automatisch:
// - CreationTime, CreatorId
// - LastModificationTime, LastModifierId
// - IsDeleted, DeletionTime, DeleterId (Soft-Delete)
}
Unit-of-Work:
public async Task CreateProductAsync(CreateProductDto input)
{
var product = new Product { Name = input.Name };
await _productRepository.InsertAsync(product);
// SaveChanges() wird automatisch am Ende aufgerufen!
// Bei Exception: automatisches Rollback
}
3. Multi-Tenancy: SaaS-ready
ABP hat drei Multi-Tenancy-Strategien:
// appsettings.json
{
"AbpMultiTenancy": {
"IsEnabled": true
}
}
// Tenant-Resolver (wie wird Tenant identifiziert?)
services.AddAbpMultiTenancy(options =>
{
options.TenantResolvers.Add(new DomainTenantResolveContributor());
options.TenantResolvers.Add(new HeaderTenantResolveContributor());
});
Strategien:
- Shared Database: Alle Tenants in einer DB (+ TenantId-Column)
- Database per Tenant: Jeder Tenant hat eigene Datenbank
- Hybrid: Manche Tenants shared, große Tenants dedicated DB
4. Dynamic API & Auto-API-Controllers
ABP generiert automatisch REST-APIs aus Application-Services:
public interface IProductAppService : IApplicationService
{
Task<PagedResultDto<ProductDto>> GetListAsync(PagedAndSortedResultRequestDto input);
Task<ProductDto> GetAsync(Guid id);
Task<ProductDto> CreateAsync(CreateProductDto input);
Task UpdateAsync(Guid id, UpdateProductDto input);
Task DeleteAsync(Guid id);
}
// ABP generiert automatisch REST-Endpoints:
// GET /api/app/product
// GET /api/app/product/{id}
// POST /api/app/product
// PUT /api/app/product/{id}
// DELETE /api/app/product/{id}
Kein Controller-Code nötig!
5. ABP Suite: Code-Generator
ABP Commercial (kostenpflichtig) bietet ABP Suite - einen Code-Generator:
abp suite
Sie definieren ein Entity (z.B. Product):
- Name, Properties, Relationships
- Permissions (wer darf lesen/schreiben?)
- Navigation-Properties
ABP Suite generiert:
- Domain-Entities
- Application-Services + DTOs
- Repositories
- Unit-Tests
- Permissions
- Angular/Blazor/MVC UI
Ergebnis: CRUD für ein Entity in < 5 Minuten.
Wann sollten Sie ABP Framework nutzen?
✅ Perfekt für:
- Enterprise-Anwendungen mit komplexer Business-Logic (DDD macht Sinn)
- SaaS-Produkte (Multi-Tenancy out-of-the-box)
- Teams mit unterschiedlichem Skill-Level (Framework gibt klare Struktur vor)
- Projekte mit vielen CRUD-Operationen (ABP Suite spart enorm Zeit)
- Long-Term-Projekte (Wartbarkeit durch klare Architektur)
❌ Weniger geeignet für:
- Microservices mit vielen kleinen Services (ABP ist pro Service ein Full-Stack-Framework → Overhead)
- Hochperformante APIs (Abstraktions-Layer haben Performance-Kosten)
- Prototypen/MVPs (Learning-Curve ist steil für neue Entwickler)
- Teams, die eigene Architektur bevorzugen (ABP ist sehr opinionated)
Der Trade-off: Produktivität vs. Lock-in
Vorteile:
- ✅ Enorme Produktivität: CRUD in Minuten, nicht Tagen
- ✅ Konsistente Architektur: Alle Entwickler folgen denselben Patterns
- ✅ Production-Ready-Modules: Identity, Tenancy, Audit-Logging funktionieren sofort
- ✅ Aktive Community: 13.9k Stars, 3.6k Forks, aktive Entwicklung
Nachteile:
- ⚠️ Steile Learning-Curve: DDD, CQRS, Modularität - viel zu lernen
- ⚠️ Lock-in: Schwer, aus ABP rauszukommen (zu viel Framework-spezifischer Code)
- ⚠️ Overhead: Viele Abstraktionen → Performance-Impact bei hochperformanten APIs
- ⚠️ Commercial-Modules: Beste Features (ABP Suite, Chat, Forms) sind kostenpflichtig ($2.5k-$20k/Jahr)
Plattform 2: PlatformPlatform - Der moderne SaaS-Blueprint
Repository: github.com/platformplatform/PlatformPlatform Stars: ~1.5k ⭐ (noch jung) | License: MIT (Open-Source) Zielgruppe: B2B/B2C-SaaS-Startups, Cloud-Native-Projekte
Was ist PlatformPlatform?
PlatformPlatform ist kein Framework, sondern ein Referenz-Projekt - ein vollständiger, produktionsreifer SaaS-Blueprint, den Sie forken und für Ihr Produkt anpassen.
Die Vision: "Zeigen, wie Top-Tier-SaaS-Produkte gebaut werden" - mit modernen Patterns, Security-Best-Practices und DevOps-Automation.
Architektur-Patterns & Prinzipien
PlatformPlatform folgt modernsten Architektur-Trends:
1. Vertical Slice Architecture (nicht Layers!):
Statt horizontaler Layer (Controller, Services, Repositories) organisiert PlatformPlatform nach Features:
/src/Application/
/AccountManagement/
/Commands/
RegisterUser.cs (Command + Handler + Validation)
ConfirmEmail.cs
/Queries/
GetUserProfile.cs (Query + Handler)
/Domain/
User.cs (Entity)
UserRegisteredEvent.cs (Domain-Event)
Vorteile:
- Alles, was zu "User-Registration" gehört, ist in einem Ordner
- Neue Features = neuer Ordner (kein Ändern von 5 verschiedenen Layern)
- Leichter zu verstehen für neue Entwickler
2. CQRS (Command-Query-Responsibility-Segregation):
Trennung von Schreib-Operationen (Commands) und Lese-Operationen (Queries):
// Command (schreibt Daten)
public record RegisterUserCommand(string Email, string Password) : ICommand;
public class RegisterUserHandler : ICommandHandler<RegisterUserCommand>
{
public async Task Handle(RegisterUserCommand command, CancellationToken ct)
{
// Validation
// Create User
// Publish UserRegisteredEvent
}
}
// Query (liest Daten, optimiert für Performance)
public record GetUserProfileQuery(Guid UserId) : IQuery<UserProfileDto>;
public class GetUserProfileHandler : IQueryHandler<GetUserProfileQuery, UserProfileDto>
{
public async Task<UserProfileDto> Handle(GetUserProfileQuery query, CancellationToken ct)
{
// Direct SQL für Performance (kein ORM-Overhead)
return await _db.QuerySingleAsync<UserProfileDto>(
"SELECT * FROM Users WHERE Id = @Id", new { query.UserId });
}
}
3. Domain-Driven Design (DDD):
Ähnlich wie ABP, aber pragmatischer:
- Rich Domain Models (Logik im Entity, nicht in Services)
- Domain Events (für lose Kopplung)
- Aggregates (für Transaktions-Grenzen)
public class User : AggregateRoot
{
public string Email { get; private set; }
public bool EmailConfirmed { get; private set; }
// Business-Logic im Entity
public void ConfirmEmail(string confirmationToken)
{
if (EmailConfirmed)
throw new DomainException("Email already confirmed");
if (!IsValidToken(confirmationToken))
throw new DomainException("Invalid confirmation token");
EmailConfirmed = true;
// Domain-Event publishen
RaiseDomainEvent(new UserEmailConfirmedEvent(Id));
}
}
4. Self-Contained Systems (SCS):
PlatformPlatform ist aktuell ein Monolith, aber strukturiert für zukünftige Microservices:
/src/
/AccountManagement/ (könnte eigener Service werden)
/BackOffice/ (könnte eigener Service werden)
/Shared/ (Shared-Kernel)
Was bietet PlatformPlatform zusätzlich?
1. Production-Ready-SaaS-Features:
Das Projekt ist kein "Hello World" - es ist ein funktionierendes SaaS-Produkt:
- Account-Management: User-Registration, Email-Confirmation, Password-Reset
- Tenant-Management: Multi-Tenancy (Subdomain-based:
customer1.yourapp.com) - Back-Office: Admin-Panel für Support-Team
- Billing (geplant): Stripe-Integration, Subscription-Management
2. Security-First-Ansatz:
PlatformPlatform hat 100% Security Score in Azure Defender:
- Keine Secrets im Code: Alles via Azure Key Vault
- Managed Identities: Services authentifizieren sich ohne Passwords
- Role-Based-Access-Control (RBAC): Azure AD-Integration
- Input-Validation: FluentValidation auf allen Commands
- SQL-Injection-Prevention: Parameterized Queries
- CSRF-Protection: Anti-Forgery-Tokens
3. DevOps-Automation:
Ein Highlight ist die Developer-Experience:
# Lokale Entwicklung mit .NET Aspire
dotnet run --project src/AppHost
# Öffnet automatisch:
# - Frontend (React Dev-Server)
# - Backend-APIs
# - SQL-Database (in Docker)
# - Developer-Dashboard (Logs, Traces, Metrics)
Deployment zu Azure:
# Einmalig: Azure-Infrastruktur provisionieren
./developer-cli azure provision
# Deploy (via GitHub Actions oder lokal)
./developer-cli azure deploy
Was passiert:
- Infrastructure-as-Code (Bicep): Azure Container Apps, SQL, Storage, Key Vault
- CI/CD (GitHub Actions): Build, Test, Deploy automatisch
- Managed Identities: Keine Secrets in Config-Files
- Auto-Scaling: Von 0 bis 1M Requests/Tag
Kosten: < $2/Tag für Development, $50-100/Monat für Production (je nach Load)
4. Frontend-Integration:
PlatformPlatform ist nicht nur Backend:
- React 19 + TypeScript
- React Aria Components (Accessibility first)
- TanStack Router (Type-safe Routing)
- TanStack Query (Data-Fetching mit Caching)
Frontend-Backend-Integration:
// Auto-generated TypeScript Client (aus C# API)
const { data, error, isLoading } = useQuery({
queryKey: ['user', userId],
queryFn: () => apiClient.users.getById(userId)
});
5. AI-Assisted Development:
PlatformPlatform hat 30+ AI-Rules für GitHub Copilot/Cursor:
.github/copilot-instructions.md:
When creating a new Command:
- Use record-type
- Inherit from ICommand or ICommand<TResult>
- Create Handler in same file
- Add FluentValidation Validator
- Follow Vertical Slice Architecture
Ergebnis: Copilot generiert Code, der zum Projekt-Stil passt.
Wann sollten Sie PlatformPlatform nutzen?
✅ Perfekt für:
- SaaS-Startups (B2B oder B2C), die schnell ein MVP bauen wollen
- Cloud-Native-Projekte (Azure-fokussiert)
- Teams, die moderne Patterns lernen wollen (Vertical Slice, CQRS, DDD)
- Projekte mit Security-Anforderungen (Banking, Healthcare, etc.)
- Developer-Experience-fokussierte Teams (Copilot-Integration, CLI-Tools)
❌ Weniger geeignet für:
- On-Premise-Deployments (stark Azure-optimiert)
- Non-SaaS-Projekte (viel SaaS-spezifischer Code, der nicht nötig wäre)
- Teams, die andere Clouds nutzen (AWS/GCP-Adaption erfordert Arbeit)
- Enterprise-Projekte mit etablierter Architektur (PlatformPlatform ist opinionated)
Der Trade-off: Modernität vs. Reife
Vorteile:
- ✅ Modernste Patterns: Vertical Slice, CQRS, SCS - State-of-the-Art
- ✅ Production-Ready: Nicht nur Code-Beispiele, sondern funktionierendes SaaS
- ✅ DevOps-First: CI/CD, IaC, Monitoring - alles dabei
- ✅ Security-Best-Practices: 100% Azure-Defender-Score
- ✅ MIT-License: Kostenlos, auch für kommerzielle Nutzung
Nachteile:
- ⚠️ Jung & Small-Community: ~1.5k Stars, weniger Community-Support als ABP
- ⚠️ Azure-Lock-in: Stark Azure-optimiert, schwer zu anderen Clouds zu migrieren
- ⚠️ Learning-Curve: CQRS, Vertical Slice, DDD - nicht für .NET-Anfänger
- ⚠️ Opinionated: Sie müssen Fork anpassen, nicht "Framework + Ihre App"
Plattform 3: .NET Aspire - Orchestrierung für Cloud-Native-Apps
Docs: learn.microsoft.com/dotnet/aspire GitHub: github.com/dotnet/aspire Stars: ~4k ⭐ | License: MIT (Open-Source, Microsoft-backed) Zielgruppe: Distributed Apps, Microservices, Cloud-Native-Projekte
Was ist .NET Aspire?
.NET Aspire ist keine Plattform für Business-Logic, sondern eine Orchestrierungs-Plattform für verteilte Anwendungen.
Die Vision: "Lokale Entwicklung mit 10+ Services soll so einfach sein wie dotnet run."
Was ist das Problem, das Aspire löst?
Szenario: E-Commerce mit Microservices
Sie haben:
- Frontend (Blazor/React)
- API-Gateway
- Product-Service (ASP.NET Core)
- Order-Service (ASP.NET Core)
- Notification-Service (Worker-Service)
- PostgreSQL (Datenbank)
- Redis (Cache)
- RabbitMQ (Message-Queue)
Ohne Aspire:
- Terminal 1:
docker-compose up(PostgreSQL, Redis, RabbitMQ) - Terminal 2:
dotnet run --project Product.Service - Terminal 3:
dotnet run --project Order.Service - Terminal 4:
dotnet run --project Notification.Service - Terminal 5:
dotnet run --project API.Gateway - Terminal 6:
npm start(Frontend)
Probleme:
- 6 Terminals offen
- Manuelles Connection-String-Management
- Keine zentrale Logs/Metrics
- Service-Discovery manuell (Port-Konfiguration)
Mit Aspire:
dotnet run --project AppHost
Fertig. Alle Services starten automatisch.
Wie funktioniert .NET Aspire?
1. AppHost: Ihre Orchestrierungs-Definition
// AppHost/Program.cs
var builder = DistributedApplication.CreateBuilder(args);
// Infrastructure
var postgres = builder.AddPostgres("postgres")
.WithPgAdmin(); // PgAdmin UI automatisch verfügbar
var redis = builder.AddRedis("redis");
var messaging = builder.AddRabbitMQ("messaging");
// Services
var productService = builder.AddProject<Projects.ProductService>("product-service")
.WithReference(postgres)
.WithReference(redis);
var orderService = builder.AddProject<Projects.OrderService>("order-service")
.WithReference(postgres)
.WithReference(messaging)
.WithReference(productService); // Service-Discovery!
var notificationService = builder.AddProject<Projects.NotificationService>("notification-service")
.WithReference(messaging);
var apiGateway = builder.AddProject<Projects.ApiGateway>("api-gateway")
.WithReference(productService)
.WithReference(orderService);
var frontend = builder.AddNpmApp("frontend", "../Frontend")
.WithReference(apiGateway);
builder.Build().Run();
Was passiert beim Start:
- PostgreSQL startet in Docker
- Redis startet in Docker
- RabbitMQ startet in Docker
- Alle .NET-Projekte starten
- Frontend (npm) startet
- Developer-Dashboard öffnet sich automatisch
2. Service-Discovery: Automatisch
Services müssen nicht mehr wissen, wo andere Services laufen:
// Order-Service: Wie rufe ich Product-Service?
// OHNE Aspire:
var productServiceUrl = configuration["ProductService:Url"]; // Manuell konfigurieren
var httpClient = new HttpClient { BaseAddress = new Uri(productServiceUrl) };
// MIT Aspire:
builder.Services.AddHttpClient<IProductClient, ProductClient>(client =>
{
client.BaseAddress = new Uri("http://product-service"); // Aspire resolved!
});
Aspire injiziert automatisch die korrekte URL (localhost:5001, kubernetes-service, etc.).
3. Developer-Dashboard: Alles an einem Ort
Das Dashboard zeigt in Echtzeit:
- Resources: Welche Services/Datenbanken laufen?
- Console-Logs: Alle Services in einer Ansicht
- Structured-Logs: Filterable, durchsuchbar
- Traces: Distributed-Tracing (Request durch mehrere Services)
- Metrics: CPU, Memory, Request-Rate pro Service
Beispiel-Trace:
[12:34:56] API-Gateway: GET /api/orders/123
└─ [12:34:56.100] Order-Service: GetOrderById(123)
└─ [12:34:56.150] Product-Service: GetProductDetails(456)
└─ [12:34:56.180] PostgreSQL: SELECT * FROM Products WHERE Id=456
Sie sehen den kompletten Request-Flow - über alle Services hinweg.
4. Integrations: Vorgefertigte Pakete
Aspire hat NuGet-Pakete für alle gängigen Services:
# PostgreSQL
dotnet add package Aspire.Npgsql.EntityFrameworkCore.PostgreSQL
# Redis
dotnet add package Aspire.StackExchange.Redis
# RabbitMQ
dotnet add package Aspire.RabbitMQ.Client
# OpenAI
dotnet add package Aspire.Azure.AI.OpenAI
# MongoDB, CosmosDB, Azure Service Bus, etc.
Usage:
// Product-Service
builder.AddNpgsqlDbContext<ProductDbContext>("postgres");
// Order-Service
builder.AddRedis("redis");
// Notification-Service
builder.AddRabbitMQClient("messaging");
Aspire injiziert automatisch:
- Connection-Strings (aus AppHost)
- Health-Checks
- Telemetry (Logs, Traces, Metrics)
5. Deployment: Von lokal zu Kubernetes
Das Geniale: Dieselbe AppHost-Definition funktioniert überall:
# Lokal (Docker)
dotnet run --project AppHost
# Kubernetes (via Aspirate-Tool)
aspirate generate
kubectl apply -f aspire-manifest.yaml
# Azure Container Apps (via Azure-CLI)
azd init
azd up
Aspire generiert:
- Docker-Compose-Files (für lokale Entwicklung)
- Kubernetes-Manifests (für K8s-Deployment)
- Bicep/Terraform (für Azure/AWS)
Wann sollten Sie .NET Aspire nutzen?
✅ Perfekt für:
- Microservices-Architekturen (> 3 Services)
- Cloud-Native-Projekte (Kubernetes, Container-Apps)
- Teams mit verteilten Systemen (bessere Developer-Experience)
- Projekte mit vielen Integrationen (DBs, Caches, Queues, AI-Services)
- Observability-Requirements (Tracing, Metrics wichtig)
❌ Weniger geeignet für:
- Monolithen (Overhead ohne Nutzen)
- Einfache Web-Apps (1 API + 1 DB → zu viel Tooling)
- Legacy-Projekte (Aspire braucht .NET 8+)
Der Trade-off: Komfort vs. Komplexität
Vorteile:
- ✅ Enorme Developer-Experience: 10+ Services mit einem Befehl starten
- ✅ Service-Discovery: Keine manuellen URLs mehr
- ✅ Observability: Tracing, Logs, Metrics out-of-the-box
- ✅ Deployment-Flexibilität: Lokal, Docker, Kubernetes, Cloud
- ✅ Microsoft-backed: Langfristige Unterstützung garantiert
Nachteile:
- ⚠️ Overhead bei kleinen Projekten: Für Monolithen zu viel Tooling
- ⚠️ Noch jung: .NET 8+ erforderlich, Ecosystem wächst noch
- ⚠️ Abstraktion: Versteckt Komplexität - schwieriger zu debuggen wenn etwas schief geht
- ⚠️ Kein Framework: Aspire löst NICHT Architektur-Fragen (DDD, CQRS, etc.)
Vergleich: Wann welche Plattform?
| Kriterium | ASP.NET Core | ABP Framework | PlatformPlatform | .NET Aspire |
|---|---|---|---|---|
| Projekt-Typ | Alle | Enterprise, SaaS | SaaS-Startups | Microservices |
| Team-Größe | 1-50+ | 5-100+ | 2-20 | 3-50+ |
| Learning-Curve | ⭐⭐ Niedrig | ⭐⭐⭐⭐ Hoch | ⭐⭐⭐⭐ Hoch | ⭐⭐⭐ Mittel |
| Produktivität (CRUD) | ⭐⭐ Mittel | ⭐⭐⭐⭐⭐ Sehr hoch | ⭐⭐⭐ Hoch | ⭐⭐ Mittel |
| Architektur-Vorgabe | ❌ Keine | ✅ DDD, Layers | ✅ Vertical Slice, CQRS | ❌ Keine |
| Multi-Tenancy | ❌ Manuell | ✅ Out-of-the-box | ✅ Out-of-the-box | ❌ Nicht relevant |
| Microservices-Support | ⭐⭐⭐ Gut | ⭐⭐ Mittel | ⭐⭐⭐ Gut | ⭐⭐⭐⭐⭐ Exzellent |
| DevOps-Tooling | ⭐⭐ Basic | ⭐⭐⭐ Gut | ⭐⭐⭐⭐⭐ Exzellent | ⭐⭐⭐⭐⭐ Exzellent |
| Observability | ⭐⭐ Basic | ⭐⭐⭐ Gut | ⭐⭐⭐⭐ Sehr gut | ⭐⭐⭐⭐⭐ Exzellent |
| Community | ⭐⭐⭐⭐⭐ Riesig | ⭐⭐⭐⭐ Groß | ⭐⭐ Klein | ⭐⭐⭐⭐ Wachsend |
| Lock-in-Risiko | ✅ Kein | ⚠️ Hoch | ⚠️ Mittel (Fork) | ⚠️ Mittel |
| Performance-Overhead | ✅ Keiner | ⚠️ Mittel | ⚠️ Niedrig | ⚠️ Niedrig |
| Lizenz-Kosten | ✅ Kostenlos | ⚠️ Open-Source + Commercial | ✅ MIT (Kostenlos) | ✅ MIT (Kostenlos) |
Entscheidungs-Matrix
Ihr Projekt ist...
...ein einfaches Web-API (CRUD, < 10 Endpoints): → ASP.NET Core (kein Framework nötig)
...eine Enterprise-Anwendung mit komplexer Business-Logic: → ABP Framework (DDD, Multi-Tenancy, vorgefertigte Module)
...ein SaaS-Startup (B2B/B2C, Cloud-Native): → PlatformPlatform (Modern, Security-First, DevOps-Automation)
...eine Microservices-Architektur (> 5 Services): → .NET Aspire (Orchestrierung, Service-Discovery, Observability)
Kombination möglich?
- ✅ ABP + Aspire: ABP für Business-Logic, Aspire für Microservices-Orchestrierung
- ✅ PlatformPlatform + Aspire: PlatformPlatform als Monolith, später Aspire für Microservices
- ❌ ABP + PlatformPlatform: Zu unterschiedliche Architektur-Ansätze
Das Fazit: Produktivität vs. Lock-in
Die Wahrheit über Developer-Plattformen
Alle drei Plattformen lösen ein echtes Problem:
- ABP Framework: Reduziert repetitive CRUD-Code um 70-80%
- PlatformPlatform: Gibt Ihnen einen Production-Ready-SaaS-Blueprint
- .NET Aspire: Macht lokale Entwicklung mit Microservices praktikabel
Aber: Sie zahlen einen Preis.
Der Lock-in-Effekt
ABP Framework:
// Ihr Code ist voll von ABP-Abstraktionen
public class ProductAppService : ApplicationService, IProductAppService
{
private readonly IRepository<Product> _repository; // ABP-Abstraction
[Authorize(MyPermissions.Products.Create)] // ABP-Permission
[UnitOfWork] // ABP-UnitOfWork
public async Task<ProductDto> CreateAsync(CreateProductDto input)
{
var product = ObjectMapper.Map<CreateProductDto, Product>(input); // ABP-AutoMapper
await _repository.InsertAsync(product); // ABP-Repository
return ObjectMapper.Map<Product, ProductDto>(product);
}
}
Wollen Sie aus ABP raus? Sie müssen:
- Alle
IRepository<T>durch EF-Core-DbContext ersetzen - Permission-System neu implementieren
- AutoMapper manuell konfigurieren
- UnitOfWork-Pattern selbst implementieren
Aufwand: 3-6 Monate für mittelgroßes Projekt.
PlatformPlatform:
// Ihr Code folgt PlatformPlatform-Patterns
public record CreateProductCommand(string Name, decimal Price) : ICommand;
public class CreateProductHandler : ICommandHandler<CreateProductCommand>
{
public async Task Handle(CreateProductCommand command, CancellationToken ct)
{
// Vertical-Slice-spezifischer Code
}
}
Wollen Sie aus PlatformPlatform raus? Sie müssen:
- Gesamte Architektur umbauen (Vertical Slice → Ihre Architektur)
- CQRS-Abstraktionen entfernen
- Domain-Events neu implementieren
Aufwand: 2-4 Monate (weniger als ABP, da Fork statt Framework).
.NET Aspire:
// Aspire-Code ist relativ entkoppelt
builder.AddNpgsqlDbContext<ProductDbContext>("postgres");
Wollen Sie aus Aspire raus? Sie müssen:
- AppHost löschen (Services laufen weiterhin standalone)
- Connection-Strings manuell konfigurieren
- Service-Discovery selbst implementieren (oder Service-Mesh nutzen)
Aufwand: 1-2 Wochen (geringster Lock-in der drei Plattformen).
Die Opinionated-Nature
Alle drei Plattformen sind opinionated:
ABP sagt: "Du machst DDD, Layered-Architecture, und Multi-Tenancy so wie wir." PlatformPlatform sagt: "Du machst Vertical Slice, CQRS, und deployest zu Azure." .NET Aspire sagt: "Du machst Microservices mit OpenTelemetry und Service-Discovery."
Wenn Sie damit einverstanden sind: Enorme Produktivität. Wenn nicht: Frustrierender Kampf gegen das Framework.
Wann lohnt sich der Trade-off?
✅ Plattform lohnt sich, wenn:
- Projekt ist langfristig (> 2 Jahre) - Lock-in ist OK
- Team ist neu in .NET - Plattform gibt Struktur vor
- Sie bauen typische Enterprise/SaaS-Features - Plattform hat das bereits
- Time-to-Market ist wichtiger als volle Kontrolle
❌ Plattform lohnt sich NICHT, wenn:
- Projekt ist kurz (< 6 Monate) - Learning-Curve überwiegt Nutzen
- Sie haben spezifische Anforderungen, die nicht ins Plattform-Modell passen
- Performance ist kritisch - Abstraktions-Layer kosten Performance
- Team bevorzugt eigene Architektur - Kampf gegen Framework
Meine persönliche Empfehlung
Als CTO, der beides gesehen hat (mit und ohne Plattform):
1. Starten Sie mit ASP.NET Core + wenigen NuGet-Paketen
- Lernen Sie, was ASP.NET Core kann
- Implementieren Sie erste Features ohne Framework
- Verstehen Sie, wo Sie repetitiven Code schreiben
2. Wenn repetitiver Code zu viel wird:
- Enterprise/SaaS mit viel CRUD? → ABP Framework
- Cloud-Native-SaaS-Startup? → PlatformPlatform (forken)
- Microservices? → .NET Aspire
3. Evaluieren Sie nach 3-6 Monaten:
- Ist die Plattform ein Produktivitäts-Boost oder eine Belastung?
- Sind die Abstraktionen hilfreich oder limitierend?
- Würden Sie die Plattform wieder wählen?
Die beste Architektur ist die, die Ihr Team versteht und produktiv nutzt - egal ob Framework oder Custom.
Weitere Ressourcen
.NET Architecture & Best-Practices:
- CTO-Sparring & Technology-Advisory Services – Architektur-Entscheidungen, Framework-Evaluation
- Team-Skalierung Guide – Wie Ihr .NET-Team mit wachsender Komplexität umgeht
- Legacy-Modernisierung: Strangler-Fig-Pattern – Migration von Legacy-.NET zu modernen Patterns
Brauchen Sie Hilfe bei der Architektur-Entscheidung? Ich habe über 8 Jahre als .NET-CTO gearbeitet und Teams bei Framework-Evaluationen begleitet. Lassen Sie uns sprechen.