Enterprise C#: Warum Code für eine Bank und Code für ein Pet-Projekt zwei verschiedene Welten sind
\n\nWenn Sie jemals von einem kleinen Webstudio in einen großen Konzern oder Fintech gewechselt sind, kennen Sie dieses Gefühl des leichten Kulturschocks. Gestern konnten Sie noch einen „heißen Fix“ ohne Tests und mit einem einzigen Kommentar //TODO: refactor committen, und heute erhalten Sie ein Review mit 20 Anmerkungen, bei dem jede öffentliche Methode eine XML-Dokumentation erfordert und jede Abhängigkeit im DI-Container registriert werden muss.
Entwicklung mit C# im Enterprise-Umfeld geht nicht um „schnell machen“, sondern darum, „es so zu machen, dass es in drei Jahren nicht kaputt geht, wenn der Autor des Codes bereits entlassen wurde“. Es geht um Vorhersagbarkeit, Skalierung und endlose Code-Reviews. Lassen Sie uns anschauen, woraus die Enterprise-Entwicklung mit C# tatsächlich besteht und wie Sie nicht schon in der ersten Woche durchfallen.
\n\n1. Architektur: SOLID ist kein Modewort, sondern ein Gesetz
\n\nIn einem Startup können Sie sich einen Monolithen mit „magischen“ Strings und riesigen Klassen mit 2000 Zeilen leisten. Im Enterprise nicht. Hier ist jedes SOLID-Prinzip keine Abstraktion, sondern eine Handlungsanleitung.
\n\nSingle Responsibility Principle (SRP)
\nIhre Klasse sollte nicht wissen, wie man gleichzeitig E-Mails sendet, in die Datenbank speichert und XML parst. Wenn Sie in einer Klasse drei oder mehr „Verantwortlichkeiten“ sehen – das ist ein Alarmzeichen. Im Enterprise wird solcher Code im Code-Review mit der Begründung „SRP-Verletzung, neu schreiben“ abgewiesen. Beispiel für schlechten Code:
\n\npublic class OrderService\n{\n public void ProcessOrder(Order order)\n {\n // 1. Validierung\n // 2. Speichern in DB\n // 3. E-Mail senden\n // 4. Logging\n }\n}\n\nDer richtige Ansatz ist die Aufteilung in separate Dienste und die Verwendung des Mediator- oder Pipeline-Musters:
\n\npublic class ProcessOrderCommandHandler : IRequestHandler<ProcessOrderCommand, bool>\n{\n private readonly IOrderRepository _repo;\n private readonly IEmailService _email;\n private readonly ILogger<ProcessOrderCommandHandler> _logger;\n\n public ProcessOrderCommandHandler(IOrderRepository repo, IEmailService email, ILogger<ProcessOrderCommandHandler> logger)\n {\n _repo = repo;\n _email = email;\n _logger = logger;\n }\n\n public async Task<bool> Handle(ProcessOrderCommand request, CancellationToken cancellationToken)\n {\n var order = await _repo.GetByIdAsync(request.OrderId);\n // Logik\n await _email.SendAsync(order.CustomerEmail, "Order processed");\n _logger.LogInformation("Order {Id} processed", order.Id);\n return true;\n }\n}\n\nBeachten Sie: Jeder Dienst macht genau eine Sache. Solchen Code zu testen ist um ein Vielfaches einfacher.
\n\n2. Dependency Injection – Ihr bester Freund
\n\nIn Enterprise-Projekten mit C# werden Sie fast nie new MyService() innerhalb einer Klasse sehen. Stattdessen kommt ein DI-Container zum Einsatz (normalerweise Autofac oder der integrierte Microsoft.Extensions.DependencyInjection).
Die Regel ist einfach: Wenn eine Klasse ihre Abhängigkeiten selbst erstellt, verstößt sie gegen das Dependency Inversion Principle. Im Enterprise wird dies als Architekturfehler betrachtet. Beispiel:
\n\n// ❌ Schlecht: feste Kopplung\npublic class ReportGenerator\n{\n private readonly PdfExporter _exporter = new PdfExporter();\n public byte[] Generate() => _exporter.Export();\n}\n\n// ✅ Gut: Abhängigkeitsinversion\npublic class ReportGenerator\n{\n private readonly IExporter _exporter;\n public ReportGenerator(IExporter exporter) => _exporter = exporter;\n public byte[] Generate() => _exporter.Export();\n}\n\nRegistrierung im Startup:
\nservices.AddScoped<IExporter, PdfExporter>();\nservices.AddScoped<ReportGenerator>();\n\nWozu das Ganze? Erstens für Tests: Sie können IExporter mocken und müssen nicht