Duplizieren Services DAO-Methoden

Status
Nicht offen für weitere Antworten.

deamon

Bekanntes Mitglied
Wenn man ein DAO mit den üblichen CRUD-Methoden hat, sollte man diese Methoden dann in dem Service, der das DAO nutzt, wiederholen? Angenommen ich habe ein DAO "Kunde" und darin die Methode createKunde(String name, String strasse) und einen Service "kundenverwaltung", dann müsste die Kundenverwaltung ja eigentlich die gleiche Methoden bieten. Ist das sinnvoll? Kommt mir irgendwie überflüssig vor, aber überall wird von Services, die auf DAOs zugreifen geredet ...
 

kama

Top Contributor
Hallo,

schau Dir mal den Thread an....

Prinzipiell ist die Idee der Services den Rest der Applikation unabhängig von den im DAO genutzten Klassen bzw. Objekten zu machen (Transfer Objekte kurz TO's)...

MfG
Karl Heinz Marbaise
 

deamon

Bekanntes Mitglied
Hallo Karl Heinz,

danke für deine Antwort. Ich denke TOs sind ein etwas anderes Thema, deshalb teile ich meine Antwort mal in zwei Teile.

Der Sinn von Transfer-Objekten

Ich habe mir die Diskussion und die Seite zum Entwurfsmuster Transfer Object (TO) durchgelesen, und der Sinn von TOs scheint zu sein, viele Aufrufe von get-Methoden durch den einmaligen Abruf eines TOs, das alle Werte enthält, zu ersetzen. Das ist deshalb ein Vorteil, weil bei J2EE potenziell jeder Aufruf über das Netz geht und man durch ein TO Netzwerk-Overhead sparen kann.

Offen bleibt aber die Frage, warum der Service nicht gleich das Entity-Objekt, das vom DAO erzeugt wird, ausliefern sollte. Aus meiner Sicht blasen TOs die Archtitektur nur unnötig auf. Mir scheint das Konstrukt aus der schwergewichtigen J2EE-Zeit zu kommen, wo man keine fetten EJBs verschicken wollte, die ja voll von technischem Code waren.

Bei leichtgewichtigen Ansätzen wie Spring+Hibernate oder Java EE 5, sehe ich aber keinen Vorteil, eine weitere Art von Objekten einzuführen. Da man den Overhead von J2EE nicht mehr in den Entity-Objekten hat und der Client, statt von den eigentlichen Entity-Objekten eben von TOs abhängig ist, tragen TOs auch nicht zur losen Kopplung bei. Wenn man die erreichen will, kann man nur primitive Werte oder Objekte aus der Standard-API verschicken, so dass der Client nicht von speziellen Klassen abhängig wird. TOs helfen da nicht.

Sinn von DAO-Zugriffen über Services

Das alles ist aber ein anderes Problem als die Frage, warum man über DAOs prinzipiell noch Services haben sollte. Bei Grails ist es z. B. so, dass man direkt DAO-Methoden wie createX(), deleteX usw. nutzt und der Zugriff auf DAO-Methoden nicht über Service-Objekte erfolgt. Ich finde, dass man dadurch eine sehr klare Architektur erreicht. Gibt es einen Vorteil, wenn die Methoden des DAOs über den Service angesprochen werden und nicht direkt?

Der einzige Vorteil, der mir da einfallen würde, ist, dass der Client dann nur vom Service abhängig wäre, aber da der sich eh ändern würde, wenn das DAO andere Methoden bereit stellen würde, sehe ich da auch keinen Vorteil, außer dass man sich vielleicht einen Import spart, aber dafür muss man Code an zwei Stellen pflegen.

Wieso sollte der Client also statt kundeDAO.create(String name) die gleiche Operation über den Service ausführen, also kundenverwaltungService.create(String name)?

Ist ein Service nicht eher für übergeordnete Aufgaben da, wo vielleicht mehrere DAOs oder andere Objekte angesprochen werden? Wenn man z. B. prüfen würde, ob ein Kunde noch etwas bestellen darf, würde man erstmal das Kunden-Objekt holen, dann dessen Rechnungen, Lieferungen usw.

Gruß
Christian
 
M

maki

Gast
Das Thema TO sehe ich genauso wie du deamon, eier der Vorteilevon POJOs als Entitäten sit doch, dass man sich TOs sparen kann.

DAOs sind Teil der Persistenzschicht, also Infrastruktur, "low level", eben CRUD, eine DAO hat zB. eine createXXX Methode.

Services sind teil der Businessschicht (gibt auch DomainServices) und stellen Schnittstellen zur Businessschicht bereit, zB. von Frontends.
Jedenfalls sind sie high-level funktionen, da kann eine Methode addXXX oder insertXXX oder sonst wie heissen, hauptsache es repräsentiert die fachlichen Konzepte, nicht infrastruktur Code ;)

DAOs (bzw. der EntityManager der JPA) sollten imho in Repositories gekapselt sein, diese gehören zur Domainschicht und bieten ihrerseits wiederrum high-level funktionen, sonst ist deine Businessschicht durchzogen von Persistenzcode, dein Persistenzcode bekommt ein bisschen Busniesslogik ab und deine Entitäten sind nix weiter als "dumme" Datencontainer mit gettern und settern ;)
Lohnt sich natürlich erst wenn genug Busniesslogik da ist um davon auch als Logik zu sprechen.

Das ganze läuft dann so ab:
Client (Frondend, zB JSF) -> UserService.addUser(newUser) -> UserRepository.addUser(newUser) -> EntityManager.create(...)
 

kama

Top Contributor
deamon hat gesagt.:
Ich denke TOs sind ein etwas anderes Thema,
Das hängt davon ab, wie man ein TO definiert....
Aber mal zu Deiner Struktur. Du hast DAO's, die BO's liefern. z.B. KundenDAO (get()) liefert eine Liste mit KundenBO's ....

deamon hat gesagt.:
Offen bleibt aber die Frage, warum der Service nicht gleich das Entity-Objekt, das vom DAO erzeugt wird, ausliefern sollte. Aus meiner Sicht blasen TOs die Archtitektur nur unnötig auf. Mir scheint das Konstrukt aus der schwergewichtigen J2EE-Zeit zu kommen, wo man keine fetten EJBs verschicken wollte, die ja voll von technischem Code waren.
Das hat auch noch einen sehr wichtigen anderen Grund. Wenn ich das Objekt (Nennen wir es einmal BO) weiter reiche, dann müssen alle anderen z.B. Clients etc. dieses Objekt Kennen. Somit baue ich über diesen Umweg eine Bindung zwischen den Objekten, die in der DB liegen und dem rest auf...Sprich man baut eine Kopplung auf, die man eigentlich vermeiden möchte...

Dazu wäre eine Möglichkeit eine Service Schicht dazwischen zu schalten....wobei man hier (Pattern TO Assembler) auch häufig weitere Gründe hat eine Service Schicht einzuführen. Im einfachsten Falle wird hier ein umkopieren der Daten von den BO's in die VO's (value Objects) durchgeführt. Aber häufig kommt es auch dazu, dass man in der Service Schicht weitere Anfragen erstellt, die nicht mehr aus einem einfachen DAO Aufruf bestehen. z.B. Alle Kunden, die auch noch ein Konto haben (Zusammenstellung aus KundenDAO und KontenDAO).


Der nächste Vorteil des genannten Ansatzes ist der, dass ich die Persistenzschicht (DAO) hier austauschen kann OHNE den Rest ändern zu müssen (ausgenommen die Service Schicht)....z.B. weil mir der OR-Mapper nicht mehr gefällt etc....

deamon hat gesagt.:
Wieso sollte der Client also statt kundeDAO.create(String name) die gleiche Operation über den Service ausführen, also kundenverwaltungService.create(String name)?
Eben um vom DAO unabhängig zu sein....und hier kann man am Namen bereits die Unterscheidung sehen....KundenverwaltungsService kann noch prüfen, ob des Kunden schon gibt oder nicht ?

deamon hat gesagt.:
Ist ein Service nicht eher für übergeordnete Aufgaben da, wo vielleicht mehrere DAOs oder andere Objekte angesprochen werden? Wenn man z. B. prüfen würde, ob ein Kunde noch etwas bestellen darf, würde man erstmal das Kunden-Objekt holen, dann dessen Rechnungen, Lieferungen usw.
Ja so kann man es auch sehen....aber dafür könnte man das in der Form machen...

DAO -> BO-Assembler -> Service

Wobei man sich hier durchaus darüber streiten kann, wo man die Übersetzung der Objekte Vornimmt (Bo-Assembler oder im Service)....das ist aber schlicht geschmack sache...


MfG
Karl Heinz Marbaise
 

deamon

Bekanntes Mitglied
kama hat gesagt.:
Wenn ich das Objekt (Nennen wir es einmal BO) weiter reiche, dann müssen alle anderen z.B. Clients etc. dieses Objekt Kennen. Somit baue ich über diesen Umweg eine Bindung zwischen den Objekten, die in der DB liegen und dem rest auf...

Man hat aber nichts gewonnen, wenn der Client statt vom BO vom TO abhängig ist. Die Kopplung ist in beiden Fällen gleich stark. Lockerer wäre die Kopplung nur, wenn man zum Austausch nur allgemein verfügbare Datentypen nehmen würde, das heißt primitive Datentypen oder Klassen aus der Java API.

kama hat gesagt.:
Im einfachsten Falle wird hier ein umkopieren der Daten von den BO's in die VO's (value Objects) durchgeführt.

Genau das ist meiner Meinung nach Unsinn. Die Kopplung wird dadurch nicht loser, man schreibt stattdessen unnötigen Code und verschwendet Laufzeit und Hauptspeicher.

kama hat gesagt.:
Aber häufig kommt es auch dazu, dass man in der Service Schicht weitere Anfragen erstellt, die nicht mehr aus einem einfachen DAO Aufruf bestehen. z.B. Alle Kunden, die auch noch ein Konto haben (Zusammenstellung aus KundenDAO und KontenDAO).

So stelle ich mir auch die Aufgaben einer Service-Schicht vor.

kama hat gesagt.:
Der nächste Vorteil des genannten Ansatzes ist der, dass ich die Persistenzschicht (DAO) hier austauschen kann OHNE den Rest ändern zu müssen (ausgenommen die Service Schicht)....z.B. weil mir der OR-Mapper nicht mehr gefällt etc....

Wie viele Stufen der Indirektion soll man noch in seinen Code bauen, um alles austauschen zu können? Wenn ich z. B. Spring nehme, kann ich über die Standard-DAO-Schnittstelle ohne eine zusätzliche Stufe alle möglichen Persistenztechnologien austauschen, ohne dass die restliche Anwendung davon betroffen wäre. Dafür bräuchte man (im Falle von Spring) also keine zusätzliche Service-Schicht.

kama hat gesagt.:
deamon hat gesagt.:
Wieso sollte der Client also statt kundeDAO.create(String name) die gleiche Operation über den Service ausführen, also kundenverwaltungService.create(String name)?
Eben um vom DAO unabhängig zu sein....

Aber das braucht man ja gar nicht, weil man hinter dem DAO ja eh schon den Persistenzmechanismus versteckt und ganz auf DAOs wird man ja auch nicht verzichten.

kama hat gesagt.:
und hier kann man am Namen bereits die Unterscheidung sehen....KundenverwaltungsService kann noch prüfen, ob des Kunden schon gibt oder nicht ?

Das wäre ein Argument für einen Service. Über ein DAO ginge das zwar zur Not auch, aber ich gebe dir Recht, dass das da nicht unbedingt hingehört.

Danke auf jeden Fall für deine Denkanstöße, so langsam bekomme ich ein Bild, was ein Service eigentlich ist, auch wenn ich manche Sachen vielleicht anders sehe, aber es gibt eben immer viele Wege ...

Gruß
Christian
 

byte

Top Contributor
Zum Thema TOs:
Sind in meinen Augen veraltet und widersprechen dem Prinzip des Domain Driven Designs. Man muss sich aber dennoch über die Konsequenzen im Klaren sein, worauf man achten muss, wenn man die persistenten Objekte weiterreicht (Stichwort: lazy loading von Objektgraphen).
In meinen Augen steigt die Kopplung, wenn man TOs einsetzt. Und es steigt vor allem der Implementierungsaufwand, wenn man das Datenmodell erweitert. Wenn man hingegen strikt nach Interfaces implementiert und auch nur diese Interfaces nach aussen weiterreicht, dann ist die Kopplung minimal.

Zum Thema Sinn und Zweck der Serviceschicht:
Wie die Serviceschicht genau aussieht, das hängt natürlich stark vom Anwendungsfall ab. Der Service stellt die Schnittstelle nach aussen dar. Häufig werden die Methoden wohl ähnlich aussehen, wie die der DAOs. Aber man sollte auch nicht dazu übergehen, die DAOs in einer Serviceschicht zu duplizieren. Ein Service kann durchaus mehrere DAOs verwenden, um alle nötigen Daten zu besorgen. Er kann auch durchaus Business Logik enthalten. Er muss womöglich Parameter validieren sowie Autorisierung und Authentifizierung des Clients sicherstellen. Und nicht zuletzt kann man in den Servicemethoden prima Transaktionen konfigurieren. Services öffnen Transaktionen bei Bedarf und comitten sie. Auf jeden Fall sollte der Service alle technischen Details über die Persistenzschicht nach aussen verbergen.
 
M

maki

Gast
>> Sind in meinen Augen veraltet und widersprechen dem Prinzip des Domain Driven Designs.

Sehe ich genauso, gilt imho aber auch für DAOs die direkt von Services aufgerufen werden.
 

byte

Top Contributor
Das Thema hatten wir ja schonmal. Und Du hast recht. Business Logik in übergeordneten Services entspricht eigentlich nicht dem DDD.
Hast Du Quellen zu dem von Dir angesprochenen Repository-Konzept parat?
 

deamon

Bekanntes Mitglied
Das Thema Servicee und DAOs beschäftigt mich nach wie vor. Ich werde das Gefühl einfach nicht los, dass Anwendungen durch den Einsatz von Services häufig unnötig aufgeblasen werden.

Dass ein DAO eher technischer Natur akzeptiere ich und ich akzeptiere auch, dass man technische möglichst von fachlichen Aspekten trennen sollte, damit man bei Änderungen des einen oder anderen nicht das ganze System anpassen muss. Soweit die Theorie. Im Falle der Kombination von Spring und Hibernate wird hinter den DAOs Hibernate verborgen und man könnte problemlos auf JPA, JDBC oder mit etwas mehr Aufwand Textdateien umsteigen. Da die Anwendung die Daten immer irgendwo speichern muss, braucht sie auch immer so etwas wie DAOs. Wenn sich etwas technisches ändert, bleibt die DAO-Schnittstelle erhalten. Es ist aus meiner Sicht deshalb unnötig, die DAOs hinter einem Service zu verstecken.

Berechtigt finde ich den Einwand von kama:
Eben um vom DAO unabhängig zu sein....und hier kann man am Namen bereits die Unterscheidung sehen....KundenverwaltungsService kann noch prüfen, ob des Kunden schon gibt oder nicht ?
Ein Service erledigt zusätzlich eine fachliche Anforderung, nämlich zu prüfen, ob es den anzulegenden Kunden schon gibt. Aber braucht man dazu einen Service?

Eine Service-Architektur kann sehr leicht zu blutarmenen Objekten führen. Martin Fowler schreibt:
"The logic that should be in a domain object is domain logic - validations, calculations, business rules - whatever you like to call it."
http://www.martinfowler.com/bliki/AnemicDomainModel.html

Demnach würde die Prüfung, ob ein Kunde schon existiert in die Klasse Kunde gehören. Das wäre ein typischer Fall für eine Klassenmethode, wo die Klasse gefragt wird, ob es ein Objekt mit bestimmten Eigenschaften gibt. Warum sollte man diese Funktionalität in einen Service packen?

Martin Fowler schreibt auf der gleichen Seite weiter:
"One source of confusion in all this is that many OO experts do recommend putting a layer of procedural services on top of a domain model, to form a Service Layer."

Es wird auch Eric Evans zitiert, der folgende Auffassung vertritt:
"The tasks this layer is responsible for are meaningful to the business or necessary for interaction with the application layers of other systems. This layer is kept thin. It does not contain business rules or knowledge, but only coordinates tasks and delegates work to collaborations of domain objects in the next layer down."
Service beschreiben also die Systemgrenze! Das deckt sich auch gut mit Fowlers Definition von "Service Layer": http://martinfowler.com/eaaCatalog/serviceLayer.html

Mir geht es aber nicht um die Kommunikation über Systemgrenzen hinweg. In meinem Fall ist die Schnittstelle nach außen eine Web-Oberfläche, die einen Controller (C von MVC) anspricht. Der Controller ist also der Service.

Und nun frage ich mich, warum es so verbreitet ist, innerhalb der Anwendung eine weitere Service-Schicht einzuzuiehen, statt die DAOs direkt in die Controller (die ja schon Services sind!) zu injezieren.

Stimmt ihr mir zu oder habe ich einen Denkfehler?
 

foobar

Top Contributor
Und nun frage ich mich, warum es so verbreitet ist, innerhalb der Anwendung eine weitere Service-Schicht einzuzuiehen, statt die DAOs direkt in die Controller (die ja schon Services sind!) zu injezieren.
Weil ein DAO (fast) keine Geschäftslogik enthält.

Stell dir mal einen simplen Bestellvorgang in einem Onlineshop vor. Da gibt es Bestellungen, Artikel und Preise. Die DAOs sind nur für die Persistenz zuständig aber die eigentliche Preisberechnung oder Bestellung wird in Srevices durchgeführt. Dafür bekommt dann der BestellService die ArtikelDAO und PreisDAO injiziert und kann dann überprüfen, ob es irgendwelche Sperren für diesen Artikel gibt und ob im Lager überhaupt noch Artikel vorhanden sind. Das ist aber alles Logik und gehört nicht in die DAOs.
DAOs stellen nur simple CRUD Operationen oder Finder zur Verfügung.
 

deamon

Bekanntes Mitglied
Dass DAOs keine Geschäftslogik enthalten (sollten) sehe ich ich auch so. Die Preisberechnung in deinem Beispiel wäre ein übergeordneter Aspekt, der auf verschiedene Objekte aus verschiedenen DAOs zugreift. Ob man dieses übergeordnete Objekte als Service bezeichnet oder nicht sei dahin gestellt, aber fest steht, dass man etwas oberhalb der DAOs braucht.

Aber spricht etwas dagegen in einem Fall, in dem es praktisch keine Geschäftslogik gibt, auf etwas wie einen Service zu verzichten. Nehmen wir zum Beispiel ein Wiki. Da gibt es ein PageDAO mit den üblichen CRUD-Methoden und ein paar Finder-Methoden. Aus meiner Sicht gibt es keinen Grund, nicht im PageController auf dieses DAO zu verweisen. Die Schnittstelle nach außen ist der Controller, der die Rolle eines Services einnimmt.

In Pseudo-Code:
Code:
class PageController{
  
	public PageDAO pageDAO;

	public void save(Request usw){
		Page p = new Page(usw);
		pageDao.save(p);
	}
}
Wozu sollte hier statt des DAOs ein Service gut sein, der nichts anderes machen würde als seinerseits das DAO aufzurufen?

Code:
class PageController{
  
	public PageService pageService;

	public void save(Request usw){
		Page p = new Page(usw);
		pageService.save(p);
	}
}

class PageService{

	public PageDAO pageDao;
	
	public void save(Page p){
		pageDao.save(p);
	}
	
}

Außer dass der Code aufgeblasen und die Wartung schwerer wird, hat man nichts erreicht, oder?

Wenn man dann noch übergeordnete Aspekte hätte, könnte man ja neben dem DAO noch ein weiteres Objekt im Controller referenzieren. Aber ich möchte dieses übergeordnete Objekt ungerne immer als Service bezeichnen, denn meiner Meinung nach führt die inflationäre Verwendung von Service zu dummen Domain-Objekten und widerspricht der Idee der Objektorientierung bzw. des Domain Driven Design. Um bei dem Beispiel mit der Bestellung zu bleiben könnte es eine Klasse Bestellung geben, die auf verschiedene DAOs zugreift. Nach meinem Verständnis würde ein Service erst ins Spiel kommen, wenn man Bestellungen an der Systemgrenze ermöglichen wollte (z. B. als Web Service).

Ich habe das Gefühl, dass Services zu oft nach Schema F eingesetzt werden. Service bilden die Systemgrenze, werden aber häufig innerhalb einer Anwendung eingesetzt. Da oft spezifische Objekte über Services ausgetauscht werden, tragen sie auch nicht wirklich zur losen Kopplung bei. Johannes Siedersleben schreibt in seinem Buch "Moderne Softwarearchitektur", dass man an Schnittstellen, die zu einer sehr losen Kopplung führen sollen, nur primitive Datentypen oder bestenfalls Objekte aus der Standard-API verwenden darf. Wenn man über einen Service z. B. "Preis"-Objekte austauscht, sind diese nicht allgemein verfügbar und es entsteht somit eine enge Kopplung zwischen dem Service und dem Nutzer des Services.

Irgendwelche Einsprüche gegen mein Gedankenmodell? ;-)
 

foobar

Top Contributor
Wozu sollte hier statt des DAOs ein Service gut sein, der nichts anderes machen würde als seinerseits das DAO aufzurufen?
Klar, braucht man nicht für alles einen Service. Teilweise sind die CRUD-Operationen der DAOs schon ausreichend.

Johannes Siedersleben schreibt in seinem Buch "Moderne Softwarearchitektur", dass man an Schnittstellen, die zu einer sehr losen Kopplung führen sollen, nur primitive Datentypen oder bestenfalls Objekte aus der Standard-API verwenden darf.

Das halte ich für Quatsch. Wenn es einen Service gibt, der eine komplexe Geschäftslogik bereits stellt, steht dahinter auch ein mehr oder weniger komplexes Domainmodel, welches ich auch auf der Clientseite benötige. Oder willste dann aus dem Domainmodel wieder Maps und Listen bauen, damit es generisch ist? Wohl eher nicht.
Das ist ja der Sinn von DomainDrivenDesign. Code as you think. Das DomainModel setzt die Domaine 1zu1 um.
 

deamon

Bekanntes Mitglied
Klar, braucht man nicht für alles einen Service. Teilweise sind die CRUD-Operationen der DAOs schon ausreichend.

Gut, jetzt ist meine Vorstellung von Services etwas klarer.

Johannes Siedersleben schreibt in seinem Buch "Moderne Softwarearchitektur", dass man an Schnittstellen, die zu einer sehr losen Kopplung führen sollen, nur primitive Datentypen oder bestenfalls Objekte aus der Standard-API verwenden darf.

Das halte ich für Quatsch. Wenn es einen Service gibt, der eine komplexe Geschäftslogik bereits stellt, steht dahinter auch ein mehr oder weniger komplexes Domainmodel, welches ich auch auf der Clientseite benötige. Oder willste dann aus dem Domainmodel wieder Maps und Listen bauen, damit es generisch ist? Wohl eher nicht.

Dann koppelt man aber eben auch zwei Systeme eng aneinander, es kann ja sein, dass diese Kopplung ok ist, aber damit wird der Nutzer des Dienstes gewissermaßen zum Teil des Systems, was den Dienst, der ja eigentlich die Systemgrenze darstellt, fragwürdig macht. Wenn man z. B. eine Online-Auktion hat und will, dass die Kunden Auktionen über einen (Web-)Service anlegen können, wird man wohl kaum die Kunden zwingen wollen, spezifische Objekte zu nutzen. Da werden dann wirklich nur einfache Datentypen ausgetauscht, aus denen der Service dann Objekte erstellt, von denen der Kunde aber nichts zu wissen braucht.
 

foobar

Top Contributor
Wenn man z. B. eine Online-Auktion hat und will, dass die Kunden Auktionen über einen (Web-)Service anlegen können, wird man wohl kaum die Kunden zwingen wollen, spezifische Objekte zu nutzen. Da werden dann wirklich nur einfache Datentypen ausgetauscht, aus denen der Service dann Objekte erstellt, von denen der Kunde aber nichts zu wissen braucht.
Hm, sehe ich anders. Wenn du dir mal ein paar WSDLs z.b. von Amazon anguckst wirste sehen, daß dort auch Domainobjekte verwendet werden. Ein Webservice ist zwar auf der technischen Seite eine Entkopplung, aber auf der fachlichen Seite soll Geschäftslogik bereit gestellt werden also das Domainmodel.
Es ist doch viel intuitiver, wenn man sich aus einem WSDL Code für seine Sprache generieren lässt, der dann direkt die Domäne wiederspiegelt.
Warum sollte man hier nur auf primitivs setzen?
 

ps

Bekanntes Mitglied
Domain Driven Design

Hier kam jetzt ein paarmal die Aussage Businesslogik lieber in den Entities zu implementieren... eben DDD. Eine ketzerische Frage: Warum?

Wiederverwendung spielt bei OOP eine große Rolle.. und in der Theorie klingt das auch alles ganz toll. In der Praxis funktioniert das weniger gut. Hat Jemand von euch schonmal ein Personenobjekt gebaut welches in mehreren Projekten unverändert Verwendung findet? (Und somit nur einmal gepflegt werden muss?)

COP (Composite Oriented Programming) klingt für mich nach einer sehr guten Lösung... zumindest würde es viele der Probleme Lösen unter welchen man mit OOP leidet. Leider gibt es für Java bisher nur Qi4J ( http://www.qi4j.org/ ) welches noch in einer relativ frühen Entwicklungsphase ist. Ich warte vor allem auf eine JPA Extension..

Zurück zu JavaEE.. durch die Services und Facaden wird eine sehr prozedurale Architektur begünstigt - und das ist doch oft überhaupt nicht verkehrt. OO ist kein Selbstzweck und auch nichts was immer Sinn macht. Gerade bei stark datengetriebene Anwendungen und Informationssystemen habe ich den Eindruck das man ohne auch ganz gut fährt, wohl aufgrund des eher prozeduralen Charakters einer solchen Anwendung.

Prozedural klingt allerdings ziemlich altbacken. Nennen wir es doch einfach SOA (ja, da brauchts noch bisschen mehr zu - aber im Prinzip ist eine "klassische" EJB Architektur out-of-the-box SOA-ready) ^^

DAOs

Was wollt ihr noch mit DAOs? Diese sind doch ein Relikt aus vergangener Zeit, sie dienten zur Entkoppelung der Datenbank.... bevor es JPA gab.

Persönlich bevorzuge ich mittlerweile aber tatsächlich nicht mehr den direkten Aufruf des EntityManagers sondern einen generischen CrudService. Dieser ermöglicht es mir dann zB. letzte Zugriffszeiten, History und dergleichen zentral zu pflegen...

TOs

Dieses pattern ist noch lange nicht tot - Entities durch die Gegend zu schieben ist nicht immer sehr ratsam, vor allem beim DDD. Die Business Objects, deren Methoden und auch einige der Eigenschaften gehen den Client oftmals überhaupt nichts an. Hier spielt die Sicherheit eine sehr große Rolle, gerade wenn verschiedene Rollen eine unterschiedliche Sicht meines Domain Models bekommen...
 

foobar

Top Contributor
Was wollt ihr noch mit DAOs? Diese sind doch ein Relikt aus vergangener Zeit, sie dienten zur Entkoppelung der Datenbank.... bevor es JPA gab.

Persönlich bevorzuge ich mittlerweile aber tatsächlich nicht mehr den direkten Aufruf des EntityManagers sondern einen generischen CrudService. Dieser ermöglicht es mir dann zB. letzte Zugriffszeiten, History und dergleichen zentral zu pflegen...
Und wie trennst du Datenzugriffe also den SQL-Code von der Geschäftslogik?
Benutz du Repositories?
 

ps

Bekanntes Mitglied
foobar hat gesagt.:
Und wie trennst du Datenzugriffe also den SQL-Code von der Geschäftslogik?
Benutz du Repositories?

Welchen SQL Code? JPAQL ist eine datenbankunabhängige DSL um relationale Daten abzufragen. Sprich mit JPA habe ich meine Datenbank schon gekapselt. Mit einem DAO würde ich dann die Kapselung nochmal kapseln...

Irgendwie finde ich das nicht schön. Also ist die Antwort wohl: Ich trenne ihn oftmals überhaupt nicht.
Wie bereits erwähnt habe ich einen CrudService welchen ich benutze um Datenbankzugriff zu erledigen. Dieser operiert mit den Business Objects - und er ist tatsächlich eigentlich nur ein Hilfskonstrukt der wiederum an den EntityManager deligiert... das mache ich um Zugriffszeiten und dergleichen speichern zu können. Hat man eine solche Anforderung nicht, so hätte ich aber auch keine Schmerzen den EntityManager in jedem ...Service oder ...Facade zu injecten. :)

Ausnahmen bestätigen natürlich wie immer die Regel: Hat man es nicht ausschließlich mit relationalen DBs zu tun, dann braucht man auch wieder ein DAO... zB. für LDAP.
 
G

Guest

Gast
Domain-Driven Design ist weniger eine konkrete Technologie als viel mehr ein Konzept. Das fachliche Modell entsteht im Kopf, wenn man sich mit dem durch Software zu lösenden Problem beschäftigt. Im Kopf eines Entwicklers nützt dieses Modell aber nicht viel, denn es muss ausgedrückt werden, um mit anderen darüber kommunizieren zu können. Die Förderung der Kommunikation ist nach meinem Verständnis das Hauptanliegen von DDD.

Wenn Objekte möglichst gut Eigenschaften und Verhalten der Realität abbilden, fällt es Experten des Fachgebiets leichter, das Objektmodell zu verstehen und umgekehrt werden Entwickler dem Fachgebiet näher kommen, weil sie sich durch DDD ernsthaft mit dem Fachgebiet befassen müssen. Die Software soll möglichst nah an dem zu lösenden Problem sein und Übersetzungen von Begriffen oder Konzepten entfallen. Manchmal fällt den Entwicklern gar nicht auf, dass sie etwas anderes bauen, als der Auftraggeber wünscht, weil sie andere Begriffe verwenden, aber denken, dass ihre Begriffe, das bedeuten, was der Kunde will.

Wenn man das Fachgebiet wirklich verstanden hat, ist die Wahrscheinlichkeit groß, dass man Objekte erzeugt, die zumindest in der Anwendung gut an verschiedenen Stellen (wieder)verwendet werden können. Manche Klassen werden vielleicht auch so allgemein, dass man sie in völlig anderen Projekten verwenden kann. Wiederverwendbarket ist vielleicht ein positiver Nebenaspekt von DDD, aber nicht das Ziel.

OOP macht es leicht, Objekte aus der Realität und deren Beziehungen in Quelltext abzubilden. Es ist also ein Weg, DDD technisch umzusetzen - so wie Composite Oriented Programming ein Weg ist.

durch die Services und Facaden wird eine sehr prozedurale Architektur begünstigt - und das ist doch oft überhaupt nicht verkehrt
Das Problem daran ist, dass zusammenhängende Dinge dadurch auseinander gerissen werden. Man hat auf der einen Seite Dienste, die Geschäftslogik enthalten und auf der anderen Seite hat man relativ dumme Domain-Objekte. Die Kohäsion wäre also gering.

In dem (kostenlos verfügbaren) Buch Domain-Driven Design quickly (S. 37 ff) steht:
Often this kind of behavior functions across several
objects, perhaps of different classes. For example, to transfer
money from one account to another; should that function be in
the sending account or the receiving account? It feels just as
misplaced in either.
When such a behavior is recognized in the domain, the best
practice is to declare it as a Service.
[...]
A Service should not replace the operation which normally
belongs on domain objects.
[...]
There are three characteristics of a Service:
1. The operation performed by the Service refers to a domain
concept which does not naturally belong to an Entity or Value
Object.
2. The operation performed refers to other objects in the domain.
3. The operation is stateless.

Die Funktionalität, die zu einem Objekt gehört, sollte also Teil des Objekts und nicht in einen Service ausgelagert sein. Services sollten, wie am Anfang der Diskussion schon erwähnt, eher übergeordnete Aufgaben erfüllen, an denen mehrere Objekte unterschiedlicher Klassen beteiligt sind.

Die Auffassung von "Service" aus dem Buch ist damit nicht so streng wie meine Äußerung, dass Service nur die Schnittstelle nach außen bilden würden. Aber natürlich sind solche Objekte prädestiniert dafür.
 

foobar

Top Contributor
Welchen SQL Code? JPAQL ist eine datenbankunabhängige DSL um relationale Daten abzufragen. Sprich mit JPA habe ich meine Datenbank schon gekapselt. Mit einem DAO würde ich dann die Kapselung nochmal kapseln...
Aber wie schreibst du Junittests für deine Services, wenn du den ganz DB-spezifischen Kram (HQL/JPAQL) nicht einfach mocken kannst?
Daher braucht man schon noch irgendeinen Container für die Queries, den man auch einfach durch etwas anderes ersetzen kann.

Bei JEE finde ich ein GenericDAO auch nicht wirklich praktisch, aber in Spring ist das schon einfacher realisierbar. Für JEE habe ich noch keine ideale Lösung gefunden.
 

ps

Bekanntes Mitglied
foobar hat gesagt.:
Aber wie schreibst du Junittests für deine Services, wenn du den ganz DB-spezifischen Kram (HQL/JPAQL) nicht einfach mocken kannst?

ejb3Unit oder auch einfach in-container testing. Gibt ja mittlerweile genügend stand alone container (jboss, glassfish, etc)

Daher braucht man schon noch irgendeinen Container für die Queries, den man auch einfach durch etwas anderes ersetzen kann.

Bei JEE finde ich ein GenericDAO auch nicht wirklich praktisch, aber in Spring ist das schon einfacher realisierbar. Für JEE habe ich noch keine ideale Lösung gefunden.

Wieso ist das deiner Meinung nach in Spring einfacher?
 

ps

Bekanntes Mitglied
foobar hat gesagt.:
Weil man in JEE gezwungen wird Interfaces für SessionBeans zu verwenden. Spring macht da nicht so viele Vorgaben wie JEE.

Ab EJB3.1 brauchst du auch kein Interface mehr... ich verstehe aber nicht wieso das ein generisches DAO vereinfachen soll?
 

foobar

Top Contributor
Naja, weil der Client ja nur das RemoteInterface kennt und das muß dann auch vom GenericDAO-Interface erben. Das finde ich alles nicht so prickelnd.
Vielleicht verzichte ich einfach komplett auf DAOs in JEE und teste mal ejb3Unit.
 

byte

Top Contributor
ps hat gesagt.:
Wie bereits erwähnt habe ich einen CrudService welchen ich benutze um Datenbankzugriff zu erledigen.
Wie definierst Du denn Service und wie definierst Du DAO? Du hast ein Object, dass CRUD-Methoden bereit stellt und nennst es Service. Für mich ist das per Definition ein Data Access Object (DAO). Man sollte sich schon auf eine Terminologie einigen.

Ich halte die DAO Schicht schon für sinnvoll. Auch mit JPA schreibt man immernoch Datenbank-Queries. Die wollen irgendwo implementiert und getestet werden. So ein technischer Code hat imo nichts im Datenmodell zu suchen.
Ob man zusätzlich dazu noch eine Service-Schicht braucht, darüber lässt sich sicherlich streiten. Es macht sicher keinen Sinn, in einem Service die DAO-Methoden zu duplizieren. Dann kann man sich die zusätzliche Schicht auch sparen. Für mich stellt die Service-Schicht (wie auch schon gesagt wurde) die Schnittstelle über die Systemgrenzen dar. Ausserdem kann man dort prima prozessorientierte (gemäß DDD objektübergreifende) Logik implementieren, Autorisierungsrichtlinien prüfen (idealerweise deklarativ) sowie Parameter validieren. Wenn man das alles nicht braucht, dann kann man sich die Service-Schicht wohl auch schenken.
 

ps

Bekanntes Mitglied
byto hat gesagt.:
ps hat gesagt.:
Wie bereits erwähnt habe ich einen CrudService welchen ich benutze um Datenbankzugriff zu erledigen.
Wie definierst Du denn Service und wie definierst Du DAO? Du hast ein Object, dass CRUD-Methoden bereit stellt und nennst es Service. Für mich ist das per Definition ein Data Access Object (DAO). Man sollte sich schon auf eine Terminologie einigen.

Naja - es ist eher ein Wrapper um den EntityManager herum. Der Grund hierfür ist das ich damit flexibler bin wenn ich bei jedem Zugriff bestimmte Werte setzen oder Sichtbarkeiten durchsetzen möchte.

Mein CrudService sieht in etwa so aus:
Code:
public interface GenericCrudService {
	public <T extends PersistentBean> T create(T t);
	public <T extends PersistentBean> T find(Class type, Serializable id);
	public <T extends PersistentBean> T get(Class type, Serializable id);
	public void delete(PersistentBean t);
	public <T extends PersistentBean> T update(T t);
	public <T extends PersistentBean> Collection<T> findByQuery(String query);
	public <T extends PersistentBean> Collection<T> findByQuery(String query, int resultLimit);
	public <T extends PersistentBean> Collection<T> findByNamedQuery(String queryName);
	public <T extends PersistentBean> Collection<T> findByNamedQuery(String queryName, int resultLimit);
}
(Dieses Interface hat nur eine Implementierung - wird also nicht für jedes EntityBean neu gebaut)

Alle meine EntityBeans leiten von einem BasicEntityBean ab (@MappedSuperClass). Dieses implementiert das Interface PersistentBean:

Code:
public interface PersistentBean extends java.io.Serializable {
	public Long getId();
	public Mandator getMandator();
	public void setMandator(Mandator m);
	public Date getLastChanged();
	public void setLastChanged(Date d);
	public Date getCreated();
	public void setCreated(Date d);
}
 

byte

Top Contributor
ps hat gesagt.:
Naja - es ist eher ein Wrapper um den EntityManager herum. Der Grund hierfür ist das ich damit flexibler bin wenn ich bei jedem Zugriff bestimmte Werte setzen oder Sichtbarkeiten durchsetzen möchte.

Mein CrudService sieht in etwa so aus:
Code:
public interface GenericCrudService {
	public <T extends PersistentBean> T create(T t);
	public <T extends PersistentBean> T find(Class type, Serializable id);
	public <T extends PersistentBean> T get(Class type, Serializable id);
	public void delete(PersistentBean t);
	public <T extends PersistentBean> T update(T t);
	public <T extends PersistentBean> Collection<T> findByQuery(String query);
	public <T extends PersistentBean> Collection<T> findByQuery(String query, int resultLimit);
	public <T extends PersistentBean> Collection<T> findByNamedQuery(String queryName);
	public <T extends PersistentBean> Collection<T> findByNamedQuery(String queryName, int resultLimit);
}

Sowas würde ich nicht Service nennen, aber der Begriff ist eh ziemlich schwammig, insofern kann man sich darüber streiten.
In der Spring-Welt gibts dafür übrigens JpaTemplate, HibernateTemplate, JdbcTemplate usw. (je nach Implementierung). Und trotzdem finde ich es sinnvoll, zusätzlich noch DAOs für die Entities zu implementieren, die man separat unit-testen kann.
 

ps

Bekanntes Mitglied
byto hat gesagt.:
Sowas würde ich nicht Service nennen, aber der Begriff ist eh ziemlich schwammig, insofern kann man sich darüber streiten.

Ja, darüber kann man sich streiten. Meiner Meinung nach kann man es als einen Service der Persistenz(komponente) sehen.
 

DanielG

Neues Mitglied
Der Begriff Service wird hier als Synonym für Facade benutzt. Ein Service ist für mich aber eher ein Facade an den Anwendungsgrenzen, der den externen Zugriff auf meine Anwendung erleichtert/ermöglicht.
Ein allgemeiner Facade dient für mich eher zu Kapselung der unteren Ebenen der Anwendung von den höheren (Unten = Domänenmodell/OOAD, Oben = UseCases/GUI/Benutzersicht).
DAOs sind für mich einfache Listen, oder Repositories, über die ich auf die einzelnen Objekte im System zugreifen kann.


Nehmen wir eine einfache Benutzerverwaltung an:

- Der einzelne Benutzer hält seine Daten vor, wie zum Beispiel Name, Login, Passwort, Ersteller, Erstelldatum, Änderungsdatum, etc.
Das Benutzerobjekt kann mir auch sagen, ob ein Passwort das richtige ist (z.B. mittels
Code:
isCorrectPassword(String password);
).
Wenn ich ein Passwort in Klartext mit
Code:
setPassword(String password)
setze, dann wird es automatisch mit MD5 oder SHA oder egal wie in einen Hash umgewandelt, bevor es gesetzt wird.
Das sind Dinge, für die die Klasse Benutzer zuständig ist.

- Das BenutzerDAO oder BenutzerRepository ermöglicht mir das Finden eines Benutzers oder mehrerer Benutzer, z. B. anhand einer ID oder des Logins, eines Suchmusters, etc. Die technische Durchführung solcher Suchen ist stark abhängig von der Implementierung (JDBC, JPA NamedQueries, etc).
Der DAO dient auch zum Speichern und Löschen von Benutzern in der Persistenzschicht.
Die Persistenzschicht habe ich am liebsten austauschbar, daher sollte hier kein einzelnes Fitzelchen Businesslogik vorhanden sein (die müsste ich sonst beim Austauschen durch eine andere Persistenzlösung wieder neu implementieren).
Will ich zum Beispiel einfache JUnit-Tests durchführen, kann ich meinen DAO einfach mit einem Stub/MockUp ersetzen, der mir ein paar Testbenutzer liefert.

- Die BenutzerFacade ermöglicht mir nun, die GUI/UseCases mit dem Domänenmodell zu verbinden.
Nehmen wir als Beispiel das Erstellen eines Benutzers (
Code:
createBenutzer(Benutzer benutzer);
):
* Es muss überprüft werden, ob alle Pflichtangaben getätigt wurden.
* Es muss überprüft werden, ob eine E-Mail-Adresse die richtige Form hat.
* Es muss überprüft werden, ob das Login bereits vergeben ist.
* Es muss überprüft werden, ob das Passwort ausreichend sicher ist.
* Es muss der Ersteller eingetragen werden.
* Es muss das Erstelldatum gesetzt werden.
* etc.
Einen Teil diese Überprüfungen kann oder sollte man auch in der Benutzerklasse selbst implementieren, z.B. so etwas wie
Code:
public boolean isValidEmail()
. Trotzdem muss sichergestellt werden, dass diese Überprüfung vor dem Speichern des neuen Benutzers auch tatsächlich durchgeführt wird. Würde diese Überprüfung innerhalb des Repositories/DAOs implementiert, hätte man am Ende in jeder Implementierung (auch im Mock) jedesmal den gleichen, doppelten Code.
Anmerkung:
Die Funktion
Code:
createBenutzer()
gibt als Rückgabewert idealerweise nicht nur ein true/false zurück, sondern auch eine Aussage darüber, was falsch gelaufen ist. Zum Beispiel
Code:
class BenutzerFacadeResult
mit einem
Code:
int statusCode
,
Code:
String error
und wenn man für einzelne Felder die Probleme aufzeigen lassen möchte, so etwas wie
Code:
Map errors
.

Das Beispiel ist natürlich sehr einfach. Trotzdem denke ich, dass daran klar wird, dass eine Kapselung der DAOs bzw. Repositories auch bei einfachen CRUD Anwendungen durchaus Sinn machen kann.
 
Zuletzt bearbeitet:

byte

Top Contributor
Sehr gutes Beispiel. Zusätzlich dazu ist der Service der richtige Ort, um Transaktions- und Security-Management zu machen. Das heisst, beim Aufruf einer Service-Methode geht die Transaktion auf und am Ende wieder zu. Macht man das in den DAOs, dann würde jede DAO Methode immer in einer eigenen Transaktion laufen. Das wäre nicht sinnvoll, wenn man mal viele DAO-Methoden am Stück aufruft.

Spätestens für Security-Constraints braucht man die Service-Schicht dann wirklich. Wenn ich bestimmte Rechte/Rollen definieren will, dann kann ich die ja schlecht an eine DAO-Methode binden, weil die Autorisierungsprüfung vom Usecase abhängig ist und nicht von einer Datenbankabfrage. Spätestens, wenn zwei Usecases mit verschiedenen Rollen die gleiche DAO-Methode aufrufen, würde ich mit Security-Handling in den DAOs auf die Schnauze fliegen.

Ansonsten muss man idR ja auch irgendwo Benutzereingaben validieren bzgl. SQL/HQL Injection oder Cross Site Scripting. Auch dafür ist die Service-Methode der geeignete Ort.
 
M

murdog

Gast
Noch eine Anmerkung zu DanielD Beispiel:
erstmal sehr schön erklärt ! Das Beispiel war gut.

zweitens: Bei der Fehlerbehandlung stimmt ich nicht überein. Für Fehlerbehandlung in java sind Exception zuständig und bieten hier tolle Möglichkeiten. createBenutzer() sollte besser den neuangelegten Benutzer zurückliefern. Dieser hat ja im Hintergrund mittlerweile eine ID bekommen und ist damit identifizierbar. Das neue Object braucht der Client natürlich.
also meine createBenutzer Methode sähe eher so aus:
Java:
public User createBenutzer() throws UserAlreadyExistsException
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen

Neue Themen


Oben