Laden...

Persistence und DomainModel korrekt trennen

Erstellt von Powerslave vor 13 Jahren Letzter Beitrag vor 13 Jahren 2.979 Views
P
Powerslave Themenstarter:in
554 Beiträge seit 2005
vor 13 Jahren
Persistence und DomainModel korrekt trennen

Guten Abend,

ich zerbreche mir gerade den Kopf wie man die Persistenzschicht und das Domänenmodell einer Anwendung am besten trennt.

Ich habe mir folgende Gedanken gemacht, komme aber nicht wirklich weiter:

'Domain'
Customer
- Save()
- Delete()
- GetById()
Employee

'Persistence'
Repository<T>
- Save()
- Delete()
- GetById()

Gemäß dem Repository Pattern würde die Persistenzschicht eine Klasse für den Datenzugriff bereitstellen, die wiederrum mit oder ohne eines ORM auf die Datenbank zugreift.

Was ich bisher gelesen habe sollte ja die Domain NICHT auf die Persistenzschicht zugreifen, wie würde ich aber dann z.B. ein Domänenobjekt abspeichern?

Pseudocode:


Customer c = new Customer("Foo");
c.Save(); // Wie ist hierfür die Implementierung wenn das Objekt die Persistenzschicht nicht kennt?

Ich hoffe ihr wisst ungefähr was ich möchte 😉

Vielen Dank schonmal.

Achtung! - Hinter dir ist ein dreiköpfiger Affe!

821 Beiträge seit 2009
vor 13 Jahren

Ich trenne Domäne und Persistenz, indem ich die Entitys aus den Domänenobjekten generieren lasse. Hierbei muss ich die nötigen Domänobjekteigenschaften öffentlich deklarieren. Meine Entitys haben dann die Funktionen

  • fromDomainObject(domainObject)
  • toDomainObject(domainObject, entity)

Falls man jetzt noch möchte, dass auch die Persistenzschicht die Domänenobjekte nicht kennt, muss man sich eine Adaptationsschicht basteln.

Speichern tue ich die Objekte meist, indem ich sie als "schmutzig" markiere oder BitteSpeicherMich-Events schmeiße.

Gruß
Christoph

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo Powerslave,

Persistance und DomainModel korrekt trennen

den einen korrekten Weg gibt es nicht, sondern nur viele mehr oder weniger gute Ansätze. Natürlich kannst du versuchen, selber etwas zu bauen, aber es gibt so viele fertige Persistenzframeworks und O/R-Mapper, dass es nicht so sinnvoll ist, das Rad nochmal neu zu erfinden.

herbivore

P
Powerslave Themenstarter:in
554 Beiträge seit 2005
vor 13 Jahren

Moin!

Danke für eure Antworten.

Ich verwende ja NHibernate zum Speichern der Entities am Ende.

Allerdings muss ich doch das Objekt speichern können, indem ich z.B. Save() darauf aufrufe.

Da ich jedoch nicht die NHibernate-Logik (session.SaveOrUpdate(),..) innerhalb jeder Save()-Methode verwenden kann/will (weil Domain die Persistance nicht kennt!), frage ich mich wie sich die Verbindung herstellt?

Achtung! - Hinter dir ist ein dreiköpfiger Affe!

821 Beiträge seit 2009
vor 13 Jahren

Im einfachsten Fall über Events oder Flag-Variablen:

Sobald sich dein Domänenobject ändert und es den Drang verspürt gespeichert werden zu müssen, setzt du im Domänenobject eine Bool-Eigenschaft IsDirty auf true und / oder lößt ein Event aus (DomainObjectChanged).

Beide Dinge kannst du (auch in Kombination) von außen adaptieren, ohne dass du spezifischen Persistierungscode in deine Domänenschicht einbringen musst.

Der unschöne Seiteneffekt ist, dass die Eingenschaften, die gespeichert werden sollen öffentlich sein müssen (oder zumindest internal).

Gruß
Christoph

F
10.010 Beiträge seit 2004
vor 13 Jahren

@Powerslave:

Allerdings muss ich doch das Objekt speichern können, indem ich z.B. Save() darauf aufrufe.

Müssen?
Du willst also ein ActiveRecord Pattern anwenden!?
Dies ist eine spezielle Art des ORM, die aber nicht mit jedem ORMapper gehen, und die auch nicht immer Sinnvoll sind.
Und schon gar nicht wenn man der Meinung ist, Domainobjekte von Datenobjekten zu trennen.

Entweder du benutzt Repositries oder ActiveRecord, beides mischen ist nicht sinnvoll.

821 Beiträge seit 2009
vor 13 Jahren

Zum Repository Pattern gibt es auch einen schönen Artikel bei Microsoft:
Design Patterns: The Repository Pattern

P
Powerslave Themenstarter:in
554 Beiträge seit 2005
vor 13 Jahren

Moin!

Erstmal vielen Dank für eure Antworten.

Eine Art von ActiveRecord mache ich momentan (ungewollt, wusste nicht, dass man das so nennt).

Repository gefällt mir ganz gut, habe mich etwas eingelesen und noch ein paar Fragen dazu:

  • Wenn ich es richtig verstanden habe, würde ja das Objekt sich nicht mehr selbst speichern können, sondern das Repository hat eine Save(T)-Methode.
    Würde ich dann im UI direkt Repository.Save(Customer) aufrufen? Oder sollte das UI das Repository auch nicht kennen?

  • Wo würdet ihr die Repositories ablegen? Bei der Persistenzschicht? Oder in eine extra Assembly?

  • Repository<T> würde ja die Standard-Datenzugriffs-Logik implementieren. Wenn ich für ein Domänenobjekt nun eigene Logik (bestimmtes HQL-Statement) benötige, mache ich dafür dann ein spezifisches Repository (z.B. CustomerRepository)?

Vielen Dank schonmal 😉

Achtung! - Hinter dir ist ein dreiköpfiger Affe!

F
10.010 Beiträge seit 2004
vor 13 Jahren

Tja, was soll das denn mal werden?
WPF oder WindowsForms?
Auf jedenfall solltest Du vermeiden die Businesslogik in die UI zu tun, schau dazu mal MVP oder MVVM als Pattern an.

Wenn du mit Repositories arbeitest, sollten die Objekte nicht wissen wie sie geschrieben oder beschrieben werden, also gehören die Abfragen dann auch in das Repository. Wenn es generisch Lösbar ist, kann es ins Repository<T>, sonst musst du spezifische machen.
Wir haben z.b. in den meisten DBO ein Deleted Flag ( zusätzlich machen wir noch auditlogging also Gürtel + Träger ) das kann man dann gleich generisch abfangen.

Businesslogik gehört da hin, wo sie benötigt wird.
Auch hier kann ein zusätzliches ViewModel/BusinessModel oberhalb des BusinessObjekts Sinn machen, aber das kommt auf die Architektur an.

P
Powerslave Themenstarter:in
554 Beiträge seit 2005
vor 13 Jahren

Weder noch, es handelt sich um eine ASP.NET Anwendung. (Aber sollte das nicht irrelevant sein?)

Businesslogik sollte am besten in die Domain oder nicht?

Wie würde es denn in der Praxis aussehen?


// Aufruf aus einer ASPX-Page?
gridView.DataSource = Repository<Customer>.GetAll();

// Oder aus dem Domänenobjekt?
gridView.DataSource = Customer.GetAll();

public IList GetAll()
{
  return Repository<Customer>.GetAll();
}

Wenn ich es so wie oben mache, würde das UI quasi direkt auf die Persistence zugreifen, was vermutlich nicht sinnvoll ist.

Im zweiten Fall wäre die Domain schlechter testbar, weil das Repository nicht mehr so einfach zu mocken ist.

Wie löst du sowas?

Achtung! - Hinter dir ist ein dreiköpfiger Affe!

W
955 Beiträge seit 2010
vor 13 Jahren

Wie löst du sowas? Schau Dir mal Asp.Net MVC 3 an.

F
10.010 Beiträge seit 2004
vor 13 Jahren

Weder noch, es handelt sich um eine ASP.NET Anwendung. (Aber sollte das nicht irrelevant sein?)

Nein, denn je nach Architektur sind die treibenden BL in anderen parts "verborgen".

Businesslogik sollte am besten in die Domain oder nicht?

Domain ist ein so schwammiger Begriff, das man da vehement ja oder nein sagen könnte.
Es wird ständig, genauso wie Tiers mit anderen Begriffen durcheinander geworfen, so das man da nichts zu sagen kann.

Datenobjekte z.b. können BL haben, denn es kann wichtig sein, das sie bestimmte Zustände nicht annehmen dürfen, dann ist die BL dazu im DTO/POCO notwendig.
Auch muss/sollte der DataLayer die entsprechende Logik haben, die beim Speichern notwendige Validierungen durchführt.

Aber es gibt auch BL die "nur" in ein ViewModel / Controller /Presenter gehört, weil es nur zur Anzeige/Auswahl benötigt wird.

Und auch ein View kann BL haben.

Aber jede stufe ist dabei ihre eigene Domain.
Deshalb meine Aussage, BL kommt dahin wo sie benötigt wird.