Suche/Domänengetriebene (DDD) .NET-Module bauen, ohne Schichten oder öffentliche APIs zu brechen

Domänengetriebene (DDD) .NET-Module bauen, ohne Schichten oder öffentliche APIs zu brechen

Für Fälle, in denen eine KI eine modulare .NET-App mit domänengetriebenem Design ändert. Sie folgt bestehenden Mustern, hält Schichtabhängigkeiten einseitig, legt Aggregate, Repositorys und DTOs in die richtige Schicht und testet mit allen Datenbanken, ohne bestehende Nutzer zu stören.

Diese KI-Regeln verwenden
## Bestehenden Mustern folgen und Schichtabhängigkeiten nur in eine Richtung zulassen Suchen Sie vor der Codeerstellung im Repository nach einer vergleichbaren Implementierung. Befolgen Sie etablierte Muster für Projektstruktur, Benennung, Abhängigkeiten, Tests und Erweiterungen, sofern die angeforderte Änderung sie nicht ausdrücklich ersetzt. Halten Sie Diffs fokussiert und vermeiden Sie beiläufige Formatierung, Umstrukturierung oder Aktualisierung von Abhängigkeiten. Verwenden Sie ausdrückliche Schichten mit einseitigen Abhängigkeiten: - **Domain.Shared:** Konstanten, Enums, Lokalisierungsressourcen, Ereignisverträge und gemeinsame Typen, die für alle Schichten und externe Clients sicher sind. - **Domain:** Entitäten, Aggregatwurzeln, Repository-Verträge, Domänendienste und Geschäftsregeln. - **Application.Contracts:** Schnittstellen von Anwendungsdiensten und Datenübertragungsobjekte. - **Application:** Implementierungen von Anwendungsdiensten und Orchestrierung von Anwendungsfällen. - **Persistence integrations:** anbieterspezifische Kontexte, Abbildungen und Repository-Implementierungen, die von der Domäne abhängen. - **HTTP API:** Transportadapter, die von Anwendungsverträgen und nicht von Implementierungen abhängen. - **Remote client:** erzeugte oder handgeschriebene Proxys, die von Anwendungsverträgen abhängen. - **Web UI:** Darstellungscode, der die HTTP API nutzt. Halten Sie Web-, Transport-, Authentifizierungskontext- und ORM-Belange aus der Domäne heraus. Halten Sie die Anwendungslogik von der Webschicht unabhängig. Behandeln Sie einen verbotenen Verweis als Architekturfehler, auch wenn er eine Implementierung verkürzen würde. Bewahren Sie öffentliche APIs und Paketgrenzen. Führen Sie keine inkompatible Änderung ein, sofern sie nicht ausdrücklich verlangt, begründet, migriert und durch Kompatibilitätstests abgedeckt ist. ## Aggregate von Anfang an gültig anlegen und entitätsübergreifende Regeln in Domänendienste legen Erstellen Sie Entitäten in der Domänenschicht und stellen Sie sicher, dass sie bei der Konstruktion gültig sind. Der Hauptkonstruktor muss Invarianten erzwingen und alle eigenen Sammlungen initialisieren. Wenn ein ORM es verlangt, stellen Sie einen geschützten parameterlosen Konstruktor bereit, ohne die normalen Erstellungsregeln abzuschwächen. Nehmen Sie eine Kennung vom Aufrufer entgegen, statt sie in der Entität zu erzeugen. Schützen Sie Konsistenz mit nichtöffentlichen Settern und Domänenmethoden, deren Absicht klar erkennbar ist. Machen Sie Mitglieder überschreibbar, wenn Laufzeit, Proxysystem oder unterstütztes Erweiterungsmodell dies verlangen. Aggregatwurzeln sollten statt eines zusammengesetzten Schlüssels eine stabile Kennung verwenden, klein bleiben und andere Aggregatwurzeln über deren Kennung statt über eine Navigationseigenschaft referenzieren. Legen Sie entitätsübergreifende Regeln des Modells in einem Domänendienst ab. Erstellen Sie eine Schnittstelle nur, wenn mehrere Implementierungen, Austauschbarkeit oder Testisolierung sie wirklich erfordern. Verwenden Sie ein klares domänenbezogenes Suffix wie `Manager`, nehmen Sie Domänenobjekte und primitive Werte statt DTOs entgegen und werfen Sie bei Regelverstößen lokalisierte Geschäftsausnahmen mit eindeutigen, namensraumgebundenen Fehlercodes. Domänendienste dürfen domänenspezifische Suche, Normalisierung, Zwischenspeicherung oder Auflösung durchführen; einfache Persistenzabfragen gehören jedoch in Repositorys. Lesen Sie den aktuell authentifizierten Benutzer nicht in der Domäne, sondern übergeben Sie benötigte Akteur- oder Mandantenwerte aus der Anwendungsschicht. ## Ein Repository pro Aggregatwurzel anlegen, unabhängig von der Datenbank Definieren Sie Repository-Verträge in der Domänenschicht und einen eigenen Vertrag je Aggregatwurzel. Erstellen Sie keine Repositorys für interne Entitäten, die einem Aggregat gehören. Halten Sie öffentliche Repository-Verträge anbieterunabhängig. Bevorzugen Sie grundlegende oder schreibgeschützte Repository-Funktionen und legen Sie keine Abstraktion eines Abfrageanbieters wie `IQueryable` gegenüber Modulverbrauchern offen. Anbieterimplementierungen dürfen intern reichhaltigere Abfrage-APIs verwenden. Führen Sie jede Repository-Operation asynchron aus und nehmen Sie ein optionales Abbruchsignal entgegen. Aktivieren Sie bei einer Methode, die ein Aggregat zurückgibt, das Laden von Details standardmäßig, wenn Aufrufer gewöhnlich ein vollständiges Aggregat benötigen. Deaktivieren Sie es bei Listenmethoden standardmäßig, um unbegrenzte Joins und Datenübertragung zu vermeiden. Verwenden Sie aussagekräftige Methoden, die Aggregate zurückgeben, statt behelfsmäßiger zusammengesetzter Projektionsklassen. Ergänzen Sie projektionsspezifische Methoden nur, wenn gemessene Leistungsanforderungen sie rechtfertigen. Bewahren Sie die Logik zum vorzeitigen Laden in einem anbieterspezifischen `IncludeDetails`-Hilfsprogramm auf, damit Anwendungsdienste nicht wissen müssen, wie ein Speicher ein Aggregat zusammensetzt. Übergeben Sie Abbruchsignale über das Normalisierungshilfsprogramm des Frameworks, sofern eines vorhanden ist. Stellen Sie sicher, dass Datenfilter, Mandantenfähigkeit und Soft-Delete-Regeln auf Anbieterebene aktiv bleiben, wenn intern ein abfragbares Objekt abgerufen wird. ## Schnittstellen für Anwendungsdienste mit eigenem DTO pro Operation entwerfen Legen Sie je Anwendungsdienst eine Schnittstelle im Vertragspaket ab. Verwenden Sie an der Grenze DTOs und primitive Werte; nehmen Sie niemals Domänenentitäten entgegen und geben Sie sie nicht zurück. Halten Sie Methoden asynchron, verwenden Sie ein einheitliches Suffix `Async` und wiederholen Sie den Entitätsnamen nicht, wenn der Dienstkontext ihn bereits deutlich macht. Verwenden Sie vorhersehbare CRUD-Formen: Kennungsargumente bleiben von Aktualisierungs-DTOs getrennt, Erstellungs- und Aktualisierungsvorgänge geben bei Bedarf ein detailliertes DTO zurück, Löschvorgänge geben keine Nutzdaten zurück und Listenabfragen enthalten optionale Felder für Filterung, Sortierung und Seiteneinteilung mit einer sicheren maximalen Seitengröße. Erstellen Sie für jeden Vorgang ein eigenes Eingabe-DTO. Verwenden Sie Eingabe-DTOs nicht nur deshalb erneut, weil ihre Felder derzeit übereinstimmen, und vermeiden Sie Vererbung zwischen Eingabe-DTOs. Halten Sie DTOs frei von Geschäftslogik, validieren Sie sie an der Grenze und verwenden Sie gemeinsame Konstanten für Grenzwerte. Bevorzugen Sie ein grundlegendes Ausgabe-DTO mit detaillierter Variante gegenüber vielen überlappenden Formen. Anwendungsimplementierungen orchestrieren Anwendungsfälle. Sie sollten eigene Repositorys verwenden, benötigte Aggregate laden, Domänenmethoden aufrufen und Aktualisierungen ausdrücklich persistieren, statt Änderungsverfolgung vorauszusetzen. Halten Sie Datenabfragen aus Anwendungsdiensten heraus. Bilden Sie erweiterbare Eigenschaften gemäß der unterstützten Mapper-Konvention ab. Übergeben Sie keine Webtypen, Upload-Abstraktionen oder Datenströme durch Anwendungsverträge. Lassen Sie Transportadapter Dateien in eine stabile Darstellung auf Anwendungsebene umwandeln. Vermeiden Sie den Aufruf eines anderen Anwendungsdienstes im selben Modul; verschieben Sie wiederverwendbare Regeln in die Domäne oder ein fokussiertes gemeinsames Hilfsprogramm. Modulübergreifende Aufrufe müssen die öffentlichen Verträge des anderen Moduls verwenden. Machen Sie öffentliche Vorgänge und vorgesehene Erweiterungshilfen überschreibbar. Bevorzugen Sie geschützte Erweiterungsschnittstellen gegenüber privaten Hilfsprogrammen, wenn Anpassung Teil des Frameworkvertrags ist. ## Datenbankcode je Anbieter trennen und jedes Modul eigenständig nutzbar halten Geben Sie jedem Modul für jeden unterstützten Persistenzanbieter eine eigene Kontextschnittstelle und Implementierung. Legen Sie Mengen oder Sammlungen nur für Aggregatwurzeln offen. Weisen Sie den Verbindungsnamen des Moduls ausdrücklich zu, halten Sie Tabellen- oder Sammlungspräfixe konfigurierbar und reservieren Sie Präfixe des Frameworks. Verlassen Sie sich nicht auf verzögertes Laden. Konfigurieren Sie Abbildungen in einer benannten Erweiterung des Modellgenerators, statt die Kontext-Lebenszyklusmethode mit Entitätsdetails zu füllen. Wenden Sie die konventionelle Abbildung des Frameworks vor modulspezifischen Regeln auf jede Entität an. Anbieterspezifische Repository-Implementierungen sollten von der Standardbasis des Anbieters erben, die Kontextschnittstelle als Grenze verwenden, Abbruchsignale korrekt weitergeben und das Laden von Aggregatdetails anbietergerecht umsetzen. Ein Dokumentspeicher kann eigene Sammlungen von selbst laden, während ein relationaler Anbieter ausdrückliche Einbindungen benötigt; keine dieser Unterscheidungen darf in Domäne oder Anwendungsvertrag gelangen. Jedes Paket sollte eine Modulzusammenstellungsklasse mit ausdrücklichen Abhängigkeitserklärungen offenlegen. Registrieren Sie Dienste und Optionen während der Konfiguration; verwenden Sie Initialisierungshooks nur für Arbeit, die eine laufende Anwendung wirklich benötigt. Ein Modul muss ohne verborgene Kopplung an ein anderes Paket eigenständig nutzbar bleiben. Unterstützt ein Modul mehrere Anbieter, führen Sie denselben Repository- und Verhaltensvertrag für jede Integration aus. Anbieterunabhängigkeit ist eine getestete Eigenschaft, keine Namenskonvention. ## Erweiterungspunkte anbieten und alte APIs schrittweise abkündigen, statt sie zu brechen Entwerfen Sie öffentliche und geschützte Mitglieder für das unterstützte Erweiterungsmodell des Frameworks. Verwenden Sie überschreibbare Methoden, austauschbare Dienstregistrierungen, Schnittstellen an echten Austauschpunkten und Optionsobjekte zur Konfiguration. Dokumentieren Sie Erweiterungspunkte und die Invarianten, die benutzerdefinierte Implementierungen bewahren müssen. Entfernen Sie ein öffentliches Mitglied nicht und benennen Sie es nicht um, wenn kein Veraltungszyklus vorgesehen ist. Kennzeichnen Sie das alte Mitglied mit klaren Migrationshinweisen als veraltet, ergänzen Sie optionale Parameter mit Standardwerten und stellen Sie für neue Basisklassenmitglieder nach Möglichkeit Standardimplementierungen bereit. Bevorzugen Sie eine neue Schnittstelle gegenüber dem Bruch einer bestehenden. Bewahren Sie benutzerseitige Zeichenfolgen in Lokalisierungsressourcen des gemeinsamen Domänenpakets auf. Verwenden Sie namensraumgebundene Schlüssel und lokalisierte Geschäftsfehler statt fest codiertem Text. Definieren Sie Namen von Einstellungen, Funktionen und Berechtigungen als Konstanten in ihrer zuständigen Schicht und ordnen Sie sie unter stabilen Modulnamensräumen. Verwenden Sie für Kommunikation innerhalb eines Prozesses einen prozessinternen Ereignisbus und über Module oder Dienste hinweg einen verteilten Ereignisbus. Legen Sie verteilte Ereignisverträge im gemeinsamen Domänenpaket ab, halten Sie sie einfach und serialisierbar und verwenden Sie primitive Werte oder verschachtelte Ereignisverträge statt Domänenentitäten. Verarbeiten Sie Integrationsereignisse in der Anwendungsschicht. Behandeln Sie Kompatibilität, Lokalisierung und Erweiterungsverhalten als öffentliche Produktoberflächen. Decken Sie Änderungen daran mit gezielten Tests und Migrationshinweisen ab. ## Jede Änderung mit allen unterstützten Datenbanken testen und den finalen Diff prüfen Stimmen Sie eine wesentliche Architektur- oder öffentliche Vertragsänderung vorab über den genehmigten Entwurfs- oder Issue-Prozess des Projekts ab. Halten Sie den Pull Request fokussiert und prüfbar und nehmen Sie die Tests auf, die das betroffene Verhalten belegen. Verwenden Sie Unit-Tests für isolierte Domänenregeln und Integrationstests für Repositorys, Abbildungen, Berechtigungen, Validierung und Modulzusammenstellung. Prüfen Sie jeden unterstützten Persistenzanbieter, wenn der Vertrag Anbieterunabhängigkeit beansprucht. Benennen Sie Tests nach Vorgang, Szenario und erwartetem Ergebnis. Prüfen Sie das endgültige Diff anhand dieser Liste: - Die Abhängigkeitsrichtung ist intakt; Web- oder Persistenzbelange dringen nicht nach innen. - Entitäten erzwingen Invarianten, eigene Sammlungen sind initialisiert und Aggregatverweise verwenden Kennungen. - Repository-Verträge bleiben asynchron, abbrechbar, aggregatzentriert und anbieterunabhängig. - Anwendungsverträge verwenden minimale validierte DTOs und stabile Methodenformen. - Anwendungsdienste delegieren Abfragen an Repositorys und Änderungen an Domänenmethoden. - Relationale und dokumentbasierte Integrationen befolgen ihre jeweiligen Konventionen für Abbildung und Detailladen. - Öffentliche APIs, Lokalisierungsschlüssel, Einstellungen, Funktionen, Berechtigungen und Ereignisse bleiben kompatibel. - Tests decken Berechtigungen, Validierung, Fehlerfälle, Anbieterunterschiede und Randbedingungen ab. - Das Diff enthält keine sachfremde Umstrukturierung, Bearbeitung erzeugter Dateien oder unnötige API-Oberfläche. Berichten Sie die ausgeführten gezielten Prüfungen sowie alle noch ungeprüften Anbieter- oder Kompatibilitätspfade. Behaupten Sie Architekturkonformität nicht allein aufgrund erfolgreicher Kompilierung.