Das Prinzip der Trennung von Verantwortlichkeiten und ORM

Ursprünglicher Autor: Vladimir Khorikov
  • Übersetzung
Ich möchte das Prinzip der Aufgabentrennung (Separation of Concerns, SoC) im Kontext von ORM diskutieren und auch sehen, warum dieses Prinzip so wichtig ist. Wir werden auch Beispiele für Verstöße gegen die Grenzen der Verantwortlichkeit zwischen Domänenlogik und Datenspeicherlogik betrachten.

Grundsatz der Aufteilung der Verantwortung


In jeder Anwendung haben wir es mit mehreren Bedenken zu tun. Mindestens drei davon sind normalerweise klar definiert: Benutzeroberfläche, Geschäftslogik und Datenbank. Das Prinzip der Aufgabentrennung ist eng mit dem Prinzip der Einzelverantwortung (Single Responsibility Principle, SRP) verknüpft . Sie können sich SoC als SRP vorstellen, das nicht auf eine einzelne Klasse, sondern auf die gesamte Anwendung angewendet wird. In den meisten Fällen können diese beiden Prinzipien austauschbar verwendet werden.

Im Fall von ORM bezieht sich das SoC-Prinzip auf die Trennung der Logik der Domain (Domain) Domain und der Logik des Speicherns von Daten in der Datenbank. Wir können argumentieren, dass der Anwendungscode einen guten Grad an Aufgabentrennung aufweist, wenn die Domänenklassen darin nicht wissen, wie sie in der Datenbank gespeichert sind. Natürlich ist es nicht immer möglich, diese beiden Anwendungsbereiche vollständig voneinander zu trennen. Manchmal sind die Leistungsanforderungen so, dass Sie diese Grenzen überschreiten müssen. In jedem Fall sollten Sie jedoch eine möglichst vollständige Abgrenzung der Verantwortlichkeiten anstreben.

Und natürlich können wir die Domänenlogik der Anwendung nicht einfach von der Logik zum Speichern von Daten in der Datenbank trennen, sondern benötigen etwas, das sie miteinander verbindet. Hier hilft uns ORM. ORM fungiert als Vermittler zwischen dem Domänenmodellcode und der Datenbank. In den meisten Fällen kann ORM dies so ausführen, dass weder der Domänencode noch die Datenbank voneinander Kenntnis haben.

Bild


Warum ist SoC wichtig?


Es gibt viele Informationen darüber, wie ein guter Grad an Aufgabentrennung aufrechterhalten werden kann. Aber warum ist das wichtig?

Bild


Während wir verschiedene Verantwortlichkeiten in einer einzelnen Klasse verwalten, müssen wir bei jeder Operation in dieser Klasse gleichzeitig deren Konsistenz sicherstellen. Dies führt sehr schnell zu einer kombinatorischen Explosion. Darüber hinaus wächst die Komplexität der Anwendung viel schneller als die meisten Entwickler glauben. Jede zusätzliche Verantwortung erhöht die Komplexität der Klasse um eine Größenordnung.

Um mit all dieser Komplexität fertig zu werden, müssen wir diese Verantwortlichkeiten teilen:

Bild


SoC ist nicht nur eine Frage von gutem oder schönem Code. Das SoC-Prinzip ist entscheidend für die Aufrechterhaltung einer akzeptablen Entwicklungsgeschwindigkeit. Darüber hinaus ist es wichtig für den Erfolg Ihres Projekts.

Eine Person kann nicht mehr als neun Objekte gleichzeitig in einem Kurzzeitgedächtnis halten . Eine Anwendung ohne klare Aufgabentrennung überläuft das Kurzzeitgedächtnis des Entwicklers aufgrund der Vielzahl von Kombinationen, in denen verschiedene nicht gemeinsam genutzte Konzepte miteinander interagieren können, sehr schnell.

Wenn Sie diese Konzepte in sehr verwandte Teile unterteilen, können Sie die von Ihnen entwickelte Anwendung „teilen und erobern“. Es ist viel einfacher, die Komplexität einer kleinen, isolierten Komponente zu verwalten, die lose mit dem Rest der Anwendung gekoppelt ist.

Wenn die Logik zum Speichern von Daten in der Datenbank die Domänenlogik durchdringt


Schauen wir uns Beispiele an, in denen die Logik der Datenspeicherung die Domäne der Domäne durchdringt.

Beispiel 1: Arbeiten mit dem dauerhaften Status eines Objekts in einer Domänenmodellklasse.

public void DoWork(Customer customer, MyContext context)
{
    if (context.Entry(customer).State == EntityState.Modified)
    {
        // Do something
    }
}

Der aktuelle dauerhafte Zustand des Objekts (d. H. Ob es bereits in der Datenbank vorhanden ist oder nicht) hat nichts mit der Logik des Domänenmodells zu tun. Im Idealfall sollten Domänenobjekte nur auf Daten angewendet werden, die in direktem Zusammenhang mit der Geschäftslogik der Anwendung stehen.

Beispiel 2: Arbeiten mit Bezeichnern

public void DoWork(Customer customer1, Customer customer2)
{
    if (customer1.Id > 0)
    {
        // Do something
    }
    if (customer1.Id == customer2.Id)
    {
        // Do something
    }
}

Die Arbeit mit Bezeichnern in Domänenklassen ist möglicherweise die häufigste Art, verschiedene Arten von Anwendungszuständigkeiten zu mischen. Bezeichner sind ein Detail der Implementierung, wie Ihre Objekte in der Datenbank gespeichert werden. In der Regel werden sie verwendet, um Objekte untereinander zu vergleichen. Wenn Sie sie auch für diesen Zweck verwenden, ist es eine viel bessere Lösung, die Gleichheitsmitglieder in der Basisklasse des Domänenobjekts zu überschreiben und "customer1 == customer2" anstelle von "customer1.Id == customer2.Id" zu schreiben.

Beispiel 3: Trennen der Eigenschaften einer Domänenklasse durch ein persistentes Attribut

public class Customer
{
    public int Number { get; set; }
    public string Name { get; set; }
    // Не сохраняется в БД, можем хранить здесь все что угодно
    public string Message { get; set; }
}

Wenn Sie dazu neigen, solchen Code zu schreiben, sollten Sie das Domänenmodell in Betracht ziehen. Ein ähnlicher Ansatz weist möglicherweise darauf hin, dass Sie Elemente in das Domänenmodell aufgenommen haben, die nicht damit zusammenhängen.

Wenn die Domänenlogik in die Datenbank eintritt


Beispiel 1: Kaskadiertes Löschen

Das Einrichten einer Datenbank für das kaskadierte Löschen ist ein Beispiel für das Eindringen der Domänenlogik in die Datenspeicherlogik. Im Idealfall sollte die Datenbank selbst keine Informationen darüber enthalten, wann das Löschen von Daten ausgelöst werden soll. Solches Wissen ist Domainbetreuung. Ihr C # / Java / etc-Code sollte der einzige Ort sein, an dem solche Logik gespeichert werden kann.

Beispiel 2: Gespeicherte Prozeduren Die

Verwendung gespeicherter Prozeduren zum Ändern von Daten in einer Datenbank ist ein weiteres Beispiel. Lassen Sie die Domänenlogik nicht in die Datenbank eindringen, und speichern Sie Code, der den Status der Daten in Ihrem Domänenmodell ändert.

Hier sind zwei Punkte hervorzuheben. Erstens gibt es in den meisten Fällen nichts auszusetzen, wenn gespeicherte Prozeduren Daten in der Datenbank nicht ändern (schreibgeschützte gespeicherte Prozeduren). Das Eingeben des Codes, der zu Nebenwirkungen im Domänenmodell führt, und des Codes ohne Nebenwirkungen in gespeicherten Prozeduren entspricht vollkommen dem CQRS- Prinzip .

Zweitens gibt es Fälle, in denen Sie die Verwendung von SQL nicht vermeiden können. Wenn Sie beispielsweise aus irgendeinem Grund eine Gruppe von Objekten löschen müssen, beschleunigt die SQL-Anweisung DELETE die Arbeit erheblich. In solchen Fällen ist die Verwendung von SQL, das Daten in der Datenbank ändert, gerechtfertigt, es ist jedoch erforderlich, alle derartigen Ausnahmen streng zu kontrollieren und nicht zuzulassen, dass sie wachsen.

Beispiel 3: Standardwerte

Die Standardwerte in den Datenbanktabellen sind ein weiteres Beispiel für eine Domänenlogik, die die Datenbank infiltriert hat. Die Werte der Eigenschaften, über die die Domänenentität standardmäßig verfügt, sollten im Code definiert und nicht an die Datenbank übergeben werden.

Überlegen Sie, wie schwierig es ist, dieses Wissen an verschiedenen Stellen der Anwendung zu sammeln. Es ist viel einfacher, sie an einem einzigen Ort aufzubewahren.

Fazit


Die meisten dieser „Undichtigkeiten“ entstehen dadurch, dass die Menschen nicht in Bezug auf den Themenbereich, sondern in Bezug auf Daten über ihre Anwendung nachdenken. Viele Entwickler betrachten das System, das sie auf diese Weise entwickeln. Für sie sind Klassen lediglich ein Repository für die Daten, die sie von der Datenbank an die Benutzeroberfläche übertragen, und ORM ist lediglich ein Hilfsprogramm, mit dem Sie Daten aus den Ergebnissen von SQL-Abfragen nicht manuell in diese Objekte kopieren können. Es ist oft schwierig, das Paradigma des Denkens zu ändern. Danach eröffnen sich jedoch in der Regel eine ganze Welt ausdrucksstarker Domänenmodelle, mit denen Sie Anwendungen, insbesondere bei großen Projekten, viel schneller entwickeln können.

Natürlich ist es nicht immer möglich, das gewünschte Maß an Aufgabentrennung im Anwendungscode zu erreichen. In den meisten Fällen hindert Sie jedoch nichts daran. Die meisten Projekte scheitern, weil sie keine der technischen Anforderungen erfüllen können. Die meisten schlagen fehl, weil sie unter einer Masse von unordentlichem Code verborgen sind, der Entwickler daran hindert, irgendetwas daran zu ändern. Jede Änderung in diesem Code führt zu einer Kaskade von Fehlern und unerwarteten Nebenwirkungen in der gesamten Anwendung.

SoC ist ein Prinzip, das ein solches Ergebnis vermeidet.

Link zum Originalartikel: Trennung von Anliegen im ORM

Jetzt auch beliebt: