Strukturierung des Projekts
Lino wurde entwickelt, um die Erstellung skalierbarer und modularer Projekte effizient zu vereinfachen. Es bietet eine gut strukturierte Lösung mit einer klaren Trennung der Verantwortlichkeiten zwischen den Schichten und ist bereit, mit den Anforderungen Ihres Projekts zu wachsen.
Beim Erstellen eines Projekts mit Lino erstellen Sie eine .NET-Lösung, die nach den besten Praktiken der Architektur und Modularisierung organisiert ist, mit Fokus auf Leistung, Skalierbarkeit und Wartungsfreundlichkeit.
Erstellen eines neuen Projekts
Der Befehl lino project erleichtert das Erstellen neuer .NET-Projekte auf einfache und effiziente Weise. Damit können Sie die Struktur Ihres Projekts einrichten, die erforderlichen Abhängigkeiten auswählen und Spracheinstellungen sowie Infrastruktur konfigurieren.
Um ein neues Projekt zu erstellen, verwenden Sie den folgenden Befehl:
lino project new
Während der Ausführung fordert die CLI die folgenden Informationen an:
- Projekt-Namespace: Definiert den Haupt-Namespace der Lösung.
- Anzeige-Name: Benutzerfreundlicher Name, der in den Schnittstellen angezeigt wird.
- Programmiersprache: Derzeit wird nur C# (.NET) unterstĂĽtzt.
- Entwicklungsstack: Derzeit .NET 9 mit Aspire.
- Code-Analyzer verwenden? Wählen Sie Ja oder Nein.
- Verwenden von verteiltem Cache? Wählen Sie Ja oder Nein.
- Verwenden von asynchroner Kommunikation? Wählen Sie Ja oder Nein.
- Datensprache: Definiert die Sprache, die für die Namen der Entitäten und andere im System angegebenen Daten verwendet wird.
- Unterstützte Sprachen der Anwendung: Ermöglicht das Hinzufügen von Unterstützung für bis zu 10 Sprachen zur Internationalisierung (i18n).
- Standardsprache: Definiert die Hauptsprache, die in APIs, Rückmeldungen, Validierungen und in der Benutzeroberfläche verwendet wird.
Nachdem Sie die Informationen bestätigt haben, erstellt Lino automatisch die Projektstruktur, wie unten gezeigt:
MyApp/ ├── MyApp.sln ├── src/ │ ├── Aspire/ │ │ ├── AppHost/ │ │ │ └── MyApp.AppHost.csproj │ │ └── ServiceDefaults/ │ │ └── MyApp.ServiceDefaults.csproj │ └── Services/ │ └── Shared/ │ ├── API/ │ │ └── MyApp.Shared.API.csproj │ ├── Application/ │ │ └── MyApp.Shared.Application.csproj │ ├── Domain/ │ │ └── MyApp.Shared.Domain.csproj │ ├── Infrastructure/ │ │ └── MyApp.Shared.Infrastructure.csproj │ └── Infrastructure.Persistence/ │ └── MyApp.Shared.Infrastructure.Persistence.csproj └── tests/
Code-Analyzer
Statische Code-Analyzer sind leistungsstarke Tools, die dazu beitragen, die Qualität und Konsistenz des Codes während der Entwicklung zu gewährleisten. Sie tun dies, indem sie den Quellcode inspizieren, ohne dass eine Ausführung erforderlich ist, und Fehler, Stilprobleme und andere Inkonsistenzen erkennen.
Wenn Sie sich entscheiden, während der Erstellung des Projekts die Code-Analyzer zu aktivieren, wird die CLI automatisch die folgenden Pakete für Sie konfigurieren:
- StyleCop.Analyzers: Führt Code-Stilprüfungen durch, wie z.B. Einrückung, Abstände und Namenskonventionen.
- SonarAnalyzer.CSharp: Ein Regelwerk zur Codequalität und Sicherheit, das hilft, häufige Fehler und potenzielle Sicherheitslücken im Code zu erkennen.
- Roslynator.Analyzers: Bietet eine Vielzahl von Qualitätsregeln und Verbesserungen für C#-Code, um Refaktorisierungs- und Optimierungsmöglichkeiten zu identifizieren.
Vorteile der Code-Analyzer:
- Verbesserung der Qualität: Sie helfen, den Code sauber, lesbar und frei von häufigen Problemen zu halten.
- Fehlerprävention: Sie erkennen Fehler, bevor sie ausgeführt werden, sodass der Entwickler Probleme frühzeitig im Prozess beheben kann.
- Standardisierung: Sie stellen sicher, dass alle Entwickler die gleichen Konventionen und Stilregeln einhalten, was die Konsistenz des Codes verbessert.
- Unterstützte Refaktorisierung: Sie erleichtern die Refaktorisierung des Codes und bieten Verbesserungsvorschläge.
Mit der Aktivierung der Code-Analyzer haben Sie eine proaktive Möglichkeit, die Qualität Ihres Codes zu verbessern und das Risiko von Fehlern in der Produktionsumgebung zu verringern.
Verteiltes Cache
Verteiltes Caching ist eine Technik, die verwendet wird, um die Leistung und Skalierbarkeit von Anwendungen zu verbessern, indem häufig abgerufene Daten in einer externen Cache-Schicht zum Datenbank gespeichert werden. Es ermöglicht den Instanzen der Anwendung, Daten effizient zu teilen, was hohe Verfügbarkeit und kürzere Antwortzeiten gewährleistet.
Wenn Sie sich entscheiden, das verteilte Caching während der Erstellung Ihres Projekts zu aktivieren, wird Redis in Ihren Aspire-Container integriert, um ein hochverfügbares Caching-System bereitzustellen.
Vorteile des verteilten Cache:
- Leistungsverbesserung: Reduziert die Antwortzeit von Anfragen, indem der Zugriff auf die Datenbank minimiert wird.
- Skalierbarkeit: Ermöglicht es der Anwendung, horizontal zu skalieren, da der Cache von verschiedenen Instanzen transparent abgerufen werden kann.
- Hohe Verfügbarkeit: Mit Redis bleibt der Cache selbst bei Ausfällen verfügbar und bietet eine robuste Lösung für verteilte Systeme.
- Kostensenkung: Verringert die Last auf der Datenbank und im System, wodurch die Notwendigkeit fĂĽr aufwendige Verarbeitung bei wiederholten Anfragen reduziert wird.
Mit der Aktivierung des verteilten Caches verbessern Sie die Leistung Ihrer Anwendung erheblich und garantieren eine schnellere Antwortzeit für Benutzer, während Sie die Last auf Backend-Systemen verringern.
Asynchrone Kommunikation
Asynchrone Kommunikation ist ein Ansatz, der es Systemen und Komponenten ermöglicht, auf nicht blockierende Weise zu kommunizieren, d.h., ohne dass das Senden oder Empfangen von Daten die Verarbeitung anderer Aufgaben unterbricht. Dies ist besonders nützlich in verteilten Systemen und in Situationen mit hoher Last, in denen Effizienz und Resilienz entscheidend sind.
Wenn Sie sich entscheiden, asynchrone Kommunikation zu aktivieren, wird RabbitMQ in Ihr Projekt integriert und MassTransit wird konfiguriert, um die Verwendung dieser asynchronen Kommunikation in Ihrer Anwendung zu erleichtern.
Vorteile der asynchronen Kommunikation:
- Verbessertes Leistung: Ermöglicht parallele Operationen, ohne den Ablauf des Systems zu blockieren.
- Skalierbarkeit: Erleichtert die Skalierung des Systems, sodass es groĂźe Datenmengen und gleichzeitige Benutzer ohne Leistungsverlust verarbeiten kann.
- Resilienz: Bei temporären Ausfällen können Nachrichten neu verarbeitet oder für eine spätere Verarbeitung gespeichert werden.
- Entkopplung: Systeme können so entworfen werden, dass sie ohne direkte Abhängigkeit von sofortigen Antworten kommunizieren, was mehr Flexibilität und Organisation fördert.
Die Integration mit RabbitMQ und die Verwendung von MassTransit machen die Kommunikation zwischen den Komponenten effizienter und robuster und tragen so zur Skalierbarkeit und Flexibilität Ihrer Anwendung bei.
Nächste Schritte
Jetzt, da Sie Ihr .NET-Projekt mit Lino erstellt haben, öffnen Sie es in Ihrem bevorzugten Code-Editor. Sie können Visual Studio, Visual Studio Code oder jede andere IDE Ihrer Wahl verwenden.
Sobald das Projekt geöffnet ist, können Sie beginnen, die Dienste hinzuzufügen und zu konfigurieren, die Ihre Anwendung ausmachen. Dies ist der nächste Schritt im Entwicklungsprozess.
In der nächsten Sektion zeigen wir Ihnen, wie Sie diese Dienste in Ihrem neuen Projekt erstellen und konfigurieren, um es für eine organisierte und skalierbare Weiterentwicklung gemäß den Anforderungen Ihrer Anwendung vorzubereiten.
Erstellen und Verwalten von Diensten
Nachdem das Projekt erstellt wurde, ist der nächste Schritt, Dienste hinzuzufügen. Lino bietet eine einfache und intuitive Möglichkeit, Dienste zu erstellen, die in monolithischen Systemen oder Mikroservice-Architekturen verwendet werden können.
Um einen neuen Dienst zu erstellen, verwenden Sie den folgenden Befehl:
lino service new
Während der Ausführung fordert die CLI die folgenden Informationen an:
- Namespace des Dienstes: Definiert den Namen und den Namespace des Dienstes.
- Anzeige Name: Der benutzerfreundliche Name, der in den Schnittstellen angezeigt wird.
- Diensttyp: Wählen Sie zwischen Einfach oder Modular.
- Datenbank: Wählen Sie zwischen PostgreSQL oder SQL Server.
Wenn der gewählte Diensttyp Einfach ist, werden auch die folgenden Informationen abgefragt:
- Architekturstil: Derzeit nur Clean Architecture.
- Strongly Typed ID verwenden? Wählen Sie zwischen Ja oder Nein.
Arten von Diensten
Einfacher Dienst: Ein einfacher Dienst hat eine schlankere Struktur und ist für monolithische Systeme oder Mikroservice-Projekte geeignet, bei denen jeder Dienst eine unabhängige Verantwortung hat.
Modularer Dienst: Für größere Systeme, die eine bessere Organisation und Skalierbarkeit erfordern, können Sie sich für einen modularen Dienst entscheiden. Dieser Typ ermöglicht es, den Dienst in kleinere, spezifischere Module zu unterteilen, was die Wartung und das Wachstum des Systems erleichtert.
Ob einfacher oder modularer Dienst, die Datenbank wird fĂĽr jeden Dienst einzigartig sein. Die Architektur und die Verwendung von Strongly Typed IDs gelten fĂĽr einfache Dienste. Im Fall von modularen Diensten wird diese Entscheidung auf Ebene jedes Moduls getroffen, das innerhalb des Dienstes erstellt wird.
Die Struktur von Lino ist flexibel und ermöglicht die Erstellung von einfachen und modularen Diensten innerhalb desselben Projekts.
Architekturstil
Lino verwendet bereits Clean Architecture für alle Dienste. Dies stellt sicher, dass Ihre Anwendung einer gut strukturierten Architektur folgt, die die Trennung von Verantwortlichkeiten fördert und Wartbarkeit sowie Skalierbarkeit erleichtert.
Vorteile von Clean Architecture:
- Entkopplung: Die Geschäftslogik ist von technischen Details unabhängig, was mehr Flexibilität und Testbarkeit ermöglicht.
- Wartbarkeit: Da die Schichten gut voneinander getrennt sind, können Änderungen vorgenommen werden, ohne andere Teile des Systems zu beeinträchtigen.
- Testbarkeit: Die Trennung von Anliegen erleichtert das Erstellen von Unit- und Integrationstests für jede Schicht unabhängig.
- Skalierbarkeit: Das Projekt ist leichter skalierbar, da Komponenten geändert oder ausgetauscht werden können, ohne die Geschäftslogik zu beeinträchtigen.
Wenn Sie sich entscheiden, einen einfachen Dienst mit der Implementierung von Clean Architecture zu erstellen, wird das Projekt mit der folgenden Struktur generiert:
MyApp/ ├── MyApp.sln ├── src/ │ ├── Aspire/ │ │ ├── AppHost/ │ │ │ └── MyApp.AppHost.csproj │ │ └── ServiceDefaults/ │ │ └── MyApp.ServiceDefaults.csproj │ ├── Integrations/ │ │ └── Internal/ │ │ └── MySimpleService/ │ │ └── Http/ │ │ ├── Clients/ │ │ │ └── MyApp.Integrations.MySimpleService.Http.Clients.csproj │ │ └── Contracts/ │ │ └── MyApp.Integrations.MySimpleService.Http.Contracts.csproj │ └── Services/ │ ├── Shared/ │ │ ├── API/ │ │ │ └── MyApp.Shared.API.csproj │ │ ├── Application/ │ │ │ └── MyApp.Shared.Application.csproj │ │ ├── Domain/ │ │ │ └── MyApp.Shared.Domain.csproj │ │ ├── Infrastructure/ │ │ │ └── MyApp.Shared.Infrastructure.csproj │ │ └── Infrastructure.Persistence/ │ │ └── MyApp.Shared.Infrastructure.Persistence.csproj │ └── MySimpleService/ │ ├── API/ │ │ └── MyApp.MySimpleService.API.csproj │ ├── Application/ │ │ └── MyApp.MySimpleService.Application.csproj │ ├── Domain/ │ │ └── MyApp.MySimpleService.Domain.csproj │ ├── Infrastructure/ │ │ └── MyApp.MySimpleService.Infrastructure.csproj │ ├── Infrastructure.Persistence/ │ │ └── MyApp.MySimpleService.Infrastructure.Persistence.csproj │ └── IntegrationEvents/ │ └── MyApp.MySimpleService.IntegrationEvents.csproj └── tests/ └── Services/ └── MySimpleService/ ├── IntegrationTests/ │ └── MyApp.MySimpleService.IntegrationTests.csproj └── UnitTests/ └── MyApp.MySimpleService.UnitTests.csproj
Wenn Sie einen modularen Dienst wählen, wird das Projekt die folgende Struktur übernehmen, die das Hinzufügen neuer Module im Verlauf der Dienstentwicklung flexibel und organisiert ermöglicht:
MyApp/ ├── MyApp.sln ├── src/ │ ├── Aspire/ │ │ ├── AppHost/ │ │ │ └── MyApp.AppHost.csproj │ │ └── ServiceDefaults/ │ │ └── MyApp.ServiceDefaults.csproj │ └── Services/ │ ├── Shared/ │ │ ├── API/ │ │ │ └── MyApp.Shared.API.csproj │ │ ├── Application/ │ │ │ └── MyApp.Shared.Application.csproj │ │ ├── Domain/ │ │ │ └── MyApp.Shared.Domain.csproj │ │ ├── Infrastructure/ │ │ │ └── MyApp.Shared.Infrastructure.csproj │ │ └── Infrastructure.Persistence/ │ │ └── MyApp.Shared.Infrastructure.Persistence.csproj │ └── MyModularService/ │ ├── Host/ │ │ └── MyApp.MyModularService.Host.csproj │ ├── Infrastructure/ │ │ └── MyApp.MyModularService.Infrastructure.csproj │ └── Modules/ └── tests/
Strongly Typed ID
Strongly Typed ID ist ein Ansatz, der darauf abzielt, die Sicherheit und Klarheit des Codes zu verbessern, indem er sicherstellt, dass Identifizierer (IDs) spezifischer und stark typisiert sind, um die Verwendung von generischen Typen wie int
oder guid
zur Darstellung eindeutiger Entitäten zu vermeiden.
Mit Strongly Typed ID würden Sie anstelle von generischen Typen, wie einer Ganzzahl zur Darstellung einer Benutzer-ID, einen spezifischen Typ für die Benutzer-ID erstellen. Dies hilft, häufige Fehler zu vermeiden, wie die falsche Verwendung von IDs unterschiedlicher Typen in falschen Kontexten.
Vorteile der Verwendung von Strongly Typed IDs:
- Typensicherheit: Stellt sicher, dass IDs korrekt verwendet werden, und vermeidet die Mischung verschiedener Identifizierer, wie Benutzer-IDs und Produkt-IDs.
- Klarheit: Der Code wird klarer, da jeder ID-Typ explizit durch eine spezifische Klasse dargestellt wird.
- Leichtere Refaktorisierung: Wenn der ID-Typ geändert werden muss, ändern Sie nur den spezifischen ID-Typ, und der restliche Code bleibt sicher.
- Vermeidung von Fehlern: Reduziert die Wahrscheinlichkeit von Fehlern durch unsachgemäße Verwendung generischer Identifizierer in falschen Kontexten.
Erstellen und Verwalten von Modulen
Nachdem ein modularer Dienst erstellt wurde, besteht der nächste Schritt darin, Module hinzuzufügen. Module ermöglichen es, die Geschäftslogik unabhängig zu organisieren und bringen noch mehr Skalierbarkeit und Struktur in das System.
Um ein neues Modul zu erstellen, verwenden Sie den folgenden Befehl:
lino module new
Während der Ausführung fordert die CLI die folgenden Informationen an:
- Dienst: Definiert, zu welchem Dienst das neue Modul gehört.
- Modul-Namespace: Definiert den Namen und Namespace des Moduls.
- Anzeigename: Benutzerfreundlicher Name, der in den Benutzeroberflächen angezeigt wird.
- Architekturstil: Derzeit nur Clean Architecture verfĂĽgbar.
- Stark typisierte ID verwenden? Wählen Sie zwischen Ja oder Nein.
Am Ende generiert Lino das neue Modul und behält die Struktur Ihres modularen Dienstes bei:
MyApp/ ├── MyApp.sln ├── src/ │ ├── Aspire/ │ │ ├── AppHost/ │ │ │ └── MyApp.AppHost.csproj │ │ └── ServiceDefaults/ │ │ └── MyApp.ServiceDefaults.csproj │ ├── Integrations/ │ │ └── Internal/ │ │ └── MyModularService/ │ │ └── MyModule/ │ │ └── Http/ │ │ ├── Clients/ │ │ │ └── MyApp.Integrations.MyModularService.MyModule.Http.Clients.csproj │ │ └── Contracts/ │ │ └── MyApp.Integrations.MyModularService.MyModule.Http.Contracts.csproj │ └── Services/ │ ├── Shared/ │ │ ├── API/ │ │ │ └── MyApp.Shared.API.csproj │ │ ├── Application/ │ │ │ └── MyApp.Shared.Application.csproj │ │ ├── Domain/ │ │ │ └── MyApp.Shared.Domain.csproj │ │ ├── Infrastructure/ │ │ │ └── MyApp.Shared.Infrastructure.csproj │ │ └── Infrastructure.Persistence/ │ │ └── MyApp.Shared.Infrastructure.Persistence.csproj │ └── MyModularService/ │ ├── Host/ │ │ └── MyApp.MyModularService.Host.csproj │ ├── Infrastructure/ │ │ └── MyApp.MyModularService.Infrastructure.csproj │ └── Modules/ │ └── MyModule/ │ ├── API/ │ │ └── MyApp.MyModularService.MyModule.API.csproj │ ├── Application/ │ │ └── MyApp.MyModularService.MyModule.Application.csproj │ ├── Domain/ │ │ └── MyApp.MyModularService.MyModule.Domain.csproj │ ├── Infrastructure/ │ │ └── MyApp.MyModularService.MyModule.Infrastructure.csproj │ ├── Infrastructure.Persistence/ │ │ └── MyApp.MyModularService.MyModule.Infrastructure.Persistence.csproj │ └── IntegrationEvents/ │ └── MyApp.MyModularService.MyModule.IntegrationEvents.csproj └── tests/ └── Services/ └── MyModularService/ └── Modules/ └── MyModule/ ├── IntegrationTests/ │ └── MyApp.MyModularService.MyModule.IntegrationTests.csproj └── UnitTests/ └── MyApp.MyModularService.MyModule.UnitTests.csproj
Datenbankstruktur
Es ist wichtig zu beachten, dass die Datenbank mit dem Dienst verbunden ist. Innerhalb eines modularen Dienstes wird jedes Modul durch sein eigenes Schema in der zugehörigen Datenbank repräsentiert. Dieser Ansatz bietet Isolation und Organisation, ohne dass mehrere verschiedene Datenbanken erforderlich sind.
Über die Unabhängigkeit zwischen Modulen
Ebenso wie Dienste keine direkten Abhängigkeiten untereinander haben, werden auch Module als unabhängige Projekte innerhalb des Dienstes erstellt.
Vorteile dieser Entkopplung:
- Isolation: Jedes Modul kann unabhängig weiterentwickelt werden, was Wartung und kontinuierliche Verbesserung erleichtert.
- Organisation: Die Anwendung wird wirklich modular, respektiert die Grenzen des Kontexts (Bounded Contexts) und fördert gute Architekturpraktiken.
- Flexibilität: Ermöglicht das Hinzufügen, Entfernen oder Refaktorieren von Modulen, ohne andere Module direkt zu beeinflussen.
- Testbarkeit: Jedes Modul kann isoliert getestet werden, was die Zuverlässigkeit und Qualität des Systems erhöht.
Damit schließen wir den Prozess der Modulerstellung ab. In den nächsten Themen werden wir sehen, wie interne Elemente eines Moduls wie Entitäten, Wertobjekte, Aufzählungen, Befehle, Abfragen, APIs, Integrationen und vieles mehr strukturiert werden.