2009-08-18 3 views
17

Das CQS-Architekturmuster basiert darauf, dass Sie Ihre Abfragen und Befehle in verschiedene Pfade unterteilen. Im Idealfall kann Ihr Persistenzspeicher partitioniert/gelesen werden, aber in meinem Fall gibt es eine einzige, normalisierte Datenbank.Wie wird CQS (Command Query Separation) implementiert, wenn ein ORM verwendet wird?

Wenn Sie ein ORM (NHibernate in meinem Fall) verwenden, ist es klar, dass das ORM verwendet wird, wenn Befehle ausgegeben werden. Aber was ist mit all den verschiedenen Abfragen, die Sie ausführen müssen, um Daten (DTOs) für Benutzerbildschirme zu erstellen, ist es gängige Praxis, das ORM zu verwerfen, wenn Sie die Query-Seite von CQS ausführen?

Wo soll ich meine Abfragen und DTO-Projektionen implementieren? Straight ADO.NET (Datenreader, Dtos, Datatables, gespeicherte Procs)? Einige Abfragen sind ziemlich einzigartig und beinhalten eine Menge Joins, um alles zusammen zu ziehen. Ich möchte die Datenbank für die Abfragen nicht denormalisieren, aber ich könnte Ansichten erstellen (Dieormalisierung des armen Mannes).

Antwort

8

Ich nehme an, mit CQS meinst du das DDD-Architekturmuster aka CQRS, nicht streng das traditionelle CQS Prinzip.

Ich würde immer noch NHibernate für Ihr schreibgeschütztes Modell verwenden. Es gibt viele Vorteile, wie zum Beispiel zukünftige und multiple Abfragen, faules/eifriges Laden usw., was die DB-Chätlichkeit optimiert. Darüber hinaus ist es einfacher, Abfragen mit einem ORM zu erstellen, wenn die Benutzerschnittstelle die UI-Klausel wesentlich ändern kann.

In Bezug auf die technische Handhabung eines schreibgeschützten Modells können Sie eine Entität immutable mit NHibernate markieren. Sie können einfach alle gelesenen Modellobjekte als unveränderbar markieren. Außerdem glaube ich nicht, dass Sie Projektionen in NHibernate aktualisieren können, so dass dies eine andere Option ist, die Sie als schreibgeschütztes Modell verwenden können (bitte korrigieren Sie mich, wenn ich falsch liege, da ich mich nicht 100% ig sicher bin).

In Bezug auf hässliche oder unmögliche NH-Zuordnungen: NH kann Ansichten und gespeicherte Prozeduren zuordnen, also denke ich, es wäre in Ordnung, diese zu verwenden, wenn Sie müssen. Ansichten sind wahrscheinlich ein wenig flexibler als gespeicherte Prozeduren für ein schreibgeschütztes Szenario, da Ihr SQL immer noch dynamisch ist. Wenn Sie jedoch für diese abgeflachten Strukturen Lese-/Schreibzugriff benötigen, würde ich sie einer Speicherprozedur zuordnen.

+0

Ja, ich meine CQRS. Ich denke, dass der architektonische Ansatz sehr sinnvoll ist, und ich möchte den ORM für die Gründe verwenden, die Sie geben. Aber wie würden Sie die Probleme behandeln, die ich bei komplexen DTO-Mappings angesprochen habe? Zum Beispiel muss ich hierarchische und Viele-zu-Viele-Beziehungen reduzieren, die von Typen in einem Vererbungsbaum abhängig sind. Ich weiß, dass das vage ist, aber angenommen, dass NHibernate (oder jedes ORM) Mapping in diesen Fällen unangenehm ist. Würden Sie zu diesem Zeitpunkt einem gespeicherten Proc oder einer Ansicht zuordnen oder was? – pfries

+0

Verzeihen Sie mir, wenn Sie sich dessen bereits bewusst sind: NH kann aus Ansichten und Procs mappen. Daher können Sie eine Ansicht oder eine Prozedur in der DB verwenden, aber die Abfrage/Abfrage muss weiterhin NH durchlaufen. –

+0

Ja, das ist, wo ich lehne - Zuordnung zu Ansichten und/oder Procs. Ich denke, man kann sich Ansichten/Procs als das schreibgeschützte db-Modell vorstellen. Das ist, was ich als Denormalisierung des armen Mannes betrachte. In CQRS können Sie diese Ansichten in Tabellen in Leseslaves verschieben, sie werden jedoch vorerst nur als Ansichten implementiert. Vielleicht ist das der beste Weg, darüber nachzudenken. – pfries

1

Es ist nicht notwendig, einen anderen Ansatz zum Lesen Ihrer Datenbank als zum Aktualisieren Ihrer Datenbank zu verwenden. CQS gibt einfach an, dass Befehle, die den Datenspeicher aktualisieren, von den Abfragen getrennt sein sollten, die den Status aus dem Datenspeicher lesen.

Sie können weiterhin NHibernate verwenden, um Daten aus Ihrem Datenspeicher zu lesen. Sie können dies jedoch durch Erstellen von zwei verschiedenen Klassen zur Kapselung Ihres Datenzugriffs verdeutlichen. Eine Klasse würde Methoden haben, den Datenspeicher zu lesen (abzufragen), die andere Klasse würde Methoden haben, Befehle (hinzufügen, aktualisieren, löschen) an den Datenspeicher auszugeben.

Was Sie vermeiden möchten, ist eine Methode, die eine Nachricht von der Datenbank abruft und die Nachricht dann als gelesen in der Datenbank markiert. Dies sollte zwei verschiedene Methodenaufrufe sein. Sie sollten den Status nicht ändern und einen Wert von derselben Methode zurückgeben.

+0

Ich sehe, was Sie über zwei getrennte Klassen bedeuten, um die Trennung explizit zu machen, und ich stimme zu. Aber wenn ich nur abfragen möchte, um DTOs zu erstellen, möchte ich wirklich ein ORM dafür verwenden? Ich brauche keine Änderungsverfolgung oder Arbeitseinheit. Ich mag die Projektionen/Mapping, aber meine Abfrage-Seite DTOs haben nichts mit meiner Befehlsobjekt Grafik zu tun, und die Abfragen sind manchmal komplex - einfacher in einer Ansicht oder gespeicherten Proc als in HQL oder mit Kriterien zu schreiben. Was ist mit Create Views für die Query DTOs? Gut für einige, aber alle? und ich bin mir nicht sicher, ob ich N-Tabellen in NHib abbilden möchte, nur um ein DTO zu bekommen. DANKE. – pfries

+1

Ich sehe Ihren Punkt, ich habe sicherlich gekämpft, um HQL zu tun, was ich will, wenn Sie ein paar Tabellen beitreten. Ich denke, wenn Sie eine Schnittstelle haben, die die Methoden definiert, die Sie zum Abfragen verwenden, ist die Implementierung letztlich egal. Code an die Schnittstelle, und verwenden Sie, was sinnvoll ist. Vielleicht ist es eine Mischung aus Nhibernate, ADO.NET oder ADO.NET. Wenn Sie später eine Schwäche mit Ihrer Wahl finden, implementieren Sie eine neue konkrete Klasse mit der Technologie, die Sinn macht. Dies ist ein Szenario, in dem Sie sicher am besten wissen, was richtig ist, und klingt wie das ORM fügt nichts in Ihrem Szenario hinzu. – NerdFury

3

Wir verwenden EF für den Befehlsteil und gerade ADO.NET => DTOs für Abfragekette. Vorteile:

1) Die Fähigkeit von SQL-Abfragen zu optimieren und erweiterte DB-Speicher verwenden Funktionen, die nicht in ORM-Schicht abstrahiert

2) Weniger Kopf

Aber wir verwenden die Trennung nur für anspruchsvolle Teile (Suche) Der Rest beruht auf dem gemeinsamen Entity Framework-Modell.

6

Die Idee dahinter ist, dass Sie den Query-Kanal verwenden sollten, der am einfachsten zu erstellen und zu verwalten ist. Sie müssen sich nicht mehr um Updates kümmern, Geschäftsregeln durchsetzen, die Datenintegrität aufrechterhalten oder sogar die Last (größtenteils) handhaben. Sie können also viele Optionen auswählen, die zuvor nicht auf dem Tisch lagen.

Aber NHibernate kann immer noch eine gute Option sein ... es ist einfach nicht mehr der automatische Standard (was es manchmal für die Command-Seite ist).

Wir haben entschieden, Castle Active Record zu verwenden (das auf NHibernate unter der Haube basiert), hauptsächlich, weil es eine nette Eigenschaft hat, die eine Tabelle für Sie von einer Klasse erzeugen wird. Das passt hervorragend zu uns, denn hier geht es um unseren Workflow: Zunächst erstellen wir eine ViewModel-Klasse. Diese Klasse ist ganz auf die Bedürfnisse des Views zugeschnitten. Dann markieren wir dieses ViewModel mit Castle Active Record-Attributen. Dann bitten wir Active Record, die entsprechende Tabelle für diese Klasse in der Query-Datenbank zu generieren. Dies ist der schnellste und glatteste Weg, den wir gefunden haben, um schnell eine Query-Datenbanktabelle zu erhalten, die die ViewModel-Klasse bedient. Die automatische Generierung spiegelt die Tatsache wider, dass der einzige Grund dafür ist, dass die Tabelle der Ansicht dient.

+0

Wie behandeln Sie Einfügungen/Aktualisierungen Ihrer Lesetabellen? Verwenden Sie Messaging? – pfries

+0

Ja, wir verwenden nServiceBus, und die Domänenobjekte veröffentlichen Ereignisse, wenn Schreibvorgänge auftreten. Ein Subskribent für diese Ereignisse füllt dann die Abfragedatenbank. –

+0

Juwel des Tages. Ich mochte vorher kein aktives Aufnahmemuster, aber mit cqrs macht es wirklich Sinn. Vielen Dank. –

1

Ich mag getrennte ORM halten für liest und schreibt so würde ich (und ich verwende):

Nhibernate für Befehle - schön abbildet mein Domain-Modell

Dapper.net für Abfragen - bildet meine DTOs schön ab und ermöglicht Flexibilität, wenn die Abfrage zu komplex ist.

Sie sind ein perfektes Paar wie Han Solo und Chewbacca.

Verwandte Themen