Datalaag opzet*

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Ssander
  • Registratie: December 2009
  • Laatst online: 12-06-2023
Opzet
De laatste tijd ben ik wat bezig met design patterns en software architecture. Momenteel zit ik wat te kijken naar het MVC model, en met name naar de data laag. Ik ben bezig een simpele applicatie te schrijven in PHP / MySQL waarin eigenlijk alleen de data laag voorkomt, gewoon om te zien hoe ik deze het beste kan implementeren. Ik kom alleen wel een probleem tegen.

De Table Data Gateway gebruik ik op het moment om bij m'n tables te komen. Hierin zitten dus functies als find, update, insert. In de database zit bijvoorbeeld een veld 'albums' met wat testdata. Als ik een album met id 1 wil vinden, kun je dus find(1) gebruiken.

Probleem
Op een website zijn meerdere pagina's. Zo is er bijvoorbeeld een pagina waar een lijst met albums te zien is. Ook is er een pagina waar alle informatie over een specifiek album te zien is. Voor deze twee pagina's heb je eigenlijk twee verschillende functies nodig: de lijst pagina wil alleen de album_id en album_name (SELECT id, name FROM...), de view_album pagina wil (bijna) alle informatie (SELECT * FROM ...). Je zult dus ergens het onderscheid moeten maken om uiteindelijk op de twee verschillende queries uit te komen. Mijn probleem is dus: waar zit dat onderscheid?.

Ik zie 4 mogelijke manieren om dit op te lossen;
  1. Geen onderscheid maken. Gewoon altijd alles ophalen en teruggeven (SELECT * FROM...) en de functie die dit aanroept laten beslissen wat hij wil en niet nodig heeft. Nadeel: performance
  2. Verschillende find() functies in de gateway maken. Bijvoorbeeld findForList() en findFullAlbum() of iets dergelijks. Er moet dan wel goed bijgehouden worden wat de functies precies returneren. Nadeel: lelijk, en je kunt uiteindelijk aardig wat verschillende functies krijgen wat erg onoverzichtelijk wordt
  3. Aan de functie meegeven wat je wilt hebben. Zo kun je find(id) veranderen naar find(id, select) zodat je als argument meegeeft welke velden je precies wilt hebben. Echter, zo wordt meteen duidelijk dat het probleem eigenlijk groter is: wat als je ook een LIMIT wilt? Of een ORDER? Moet dit ook meegegeven worden? Nadeel: nu ben je al meteen een query builder aan het maken, iets wat ik persoonlijk niet heel prettig vind omdat je volgens mij dan beter gewoon queries kan meegeven. Is een stuk duidelijker.
  4. Queries aan de gateway geven. Zo kan elke pagina gewoon precies vragen wat deze nodig heeft. Een lijst pagina wil iets als SELECT id, name FROM albums ORDER BY date_added LIMIT 5 of SELECT id, name FROM albums WHERE FIRST_LETTER(name) = 'a' ORDER BY name (geen correcte MySQL, maar you get the point). Op deze manier is het duidelijk te zien wat er precies gevraagd wordt. Nadeel: de SQL verplaatst zich nu naar de klasse die de Gateway gebruikt, iets wat juist niet de bedoeling is. Ook krijg je hierdoor code duplicatie wanneer twee verschillende pagina's juist wel hetzelfde willen hebben. Al is dit misschien op te lossen door queries die meerdere keren gebruikt worden juist wel in de Gateway te stoppen. Dan krijg je echter de SQL op twee verschillende 'lagen'.
Oplossing?
Er is vast een algemeen geaccepteerde manier om dit op de juiste manier te doen. Ik heb wat geprobeerd te googlen maar kon hier niet erg veel over vinden. Zelf al zou je niet de Gateway gebruiken vind ik dit nog een moeilijk probleem. Je wilt de SQL allemaal bij elkaar hebben, maar tegelijkertijd wil je dat je alles op verschillende manieren kunt ophalen. Heeft iemand hier ervaring mee?

Acties:
  • 0 Henk 'm!

  • Asator
  • Registratie: December 2009
  • Laatst online: 12-02-2024
Nu heb ik zelf al jaren niet meer gewerkt met PHP maar in Java gebruik ik voor database access Hibernate, dit bied een ORM (Object/Rational Mapping) oplossing waar functies als save(), update(), delete() etc. al in zit.

Op wikipedia heb je ook een lijst van ORM oplossingen voor PHP:
Wikipedia: List of object-relational mapping software

Ik denk dat je wel meer kan vinden als je verder zoekt naar ORM.

Acties:
  • 0 Henk 'm!

  • Ssander
  • Registratie: December 2009
  • Laatst online: 12-06-2023
Asator schreef op zaterdag 30 januari 2010 @ 13:22:
dit bied een ORM (Object/Rational Mapping) oplossing waar functies als save(), update(), delete() etc. al in zit.
save, update & delete zijn eigenlijk nog de 'makkelijke' functies. Het probleem is vooral select. Zoals ik in mijn post al zei kun je data uit één tabel op veel verschillende manieren op willen halen. Ergens moet je dus dit onderscheid maken, volgens mij mogelijk op één van die vier manieren. Hoe heb jij dit gedaan?

Acties:
  • 0 Henk 'm!

Verwijderd

Waarom het wiel opnieuw uitvinden? Gebruik een goed framework: Zend of Kohana. Met Kohana ORM of Doctrine kom je een heel eind.

Acties:
  • 0 Henk 'm!

  • Leftblank
  • Registratie: Juni 2004
  • Laatst online: 12-09 13:51
Ssander schreef op zaterdag 30 januari 2010 @ 13:36:
save, update & delete zijn eigenlijk nog de 'makkelijke' functies. Het probleem is vooral select. Zoals ik in mijn post al zei kun je data uit één tabel op veel verschillende manieren op willen halen. Ergens moet je dus dit onderscheid maken, volgens mij mogelijk op één van die vier manieren. Hoe heb jij dit gedaan?
Daarvoor kun je nou juist bij die ORM tools kijken natuurlijk; die doen echt wel meer dan enkel een vrolijke 'find_all' ;) Bijv deze oplossing van CakePHP.

Acties:
  • 0 Henk 'm!

  • Wmm
  • Registratie: Maart 2002
  • Laatst online: 11-09 14:30

Wmm

Verwijderd schreef op zaterdag 30 januari 2010 @ 13:40:
Waarom het wiel opnieuw uitvinden? Gebruik een goed framework: Zend of Kohana. Met Kohana ORM of Doctrine kom je een heel eind.
Misschien omdat het nuttig is om zelf na te denken over dit soort problemen ipv kant en klare frameworks te gebruiken. Niet dat er iets mis is met frameworks, maar het maakt je imo een betere devver als je zelf over dit soort problemen nagedacht hebt en naar oplossingen zoekt. Het geeft je veel meer inzicht software architectuur.

@ TS: je kunt voor 1 gaan. De performance die het scheelt lijkt me nihiel (zolang je geen ingewikkelde joins gebruikt). En als je later ook naast column x en y ook z nodig blijkt te zijn hoef je niet je queries aan te passen. Optie 4 is iig iets wat je gewoon niet wilt. Nummer 3 is ook een optie, kijk hiervoor ook eens naar de link die Leftblank post.

Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 12-09 10:54

Janoz

Moderator Devschuur®

!litemod

Hoeveel manieren zijn het werkelijk? Hoeveel impact op de performance denk je dat het daaadwerkelijk zal wezen?

Ik zou dus vooral voor optie 2 gaan, maar dan wel met een heel belangrijke kanttekening! Je moet je methodenamen niet gaan richten op waar je ze gaat gebuiken, maar wat ze daadwerkelijk doen. Wat ik eigenlijk vooral een gemis in het voorbeeld vind is dat de findAll mist. Om dan terug te komen op je voorbeeld, gebruik geen findForAlbum en findForList, maar zorg dat find een meer ingevuld object terug geeft dan findAll. Sla daar trouwens niet teveel in door. Zo heel veel overhead heeft dat helemaal niet. Waar je eigenlijk alleen naar moet kijken zijn de grote velden zoals TEXT en BLOB. Die laat je eigenlijk nooit in een lijst zien. (Sterker nog, wanneer je een BLOB voor een plaatje gebruikt en je applicatie wordt alleen voor het web gebruikt dan zal het plaatje altijd een apart request zijn en zou je de BLOB niet eens bij de find terug gaan geven, maar echt een specifieke methode voor maken)

Om LIMIT funcionaliteit te implementeren kun je meerdere implementaties van de findAll maken. Let daarbij op dat je dat natuurlijk doet door 1 of twee int parameters toe te voegen, en niet door een string die aan de query geplakt wordt.


Optie 3 en 4 zijn sowieso dikke nogos! De hele essentie van een data laag is een scheiding maken tussen de database en de rest van de applicatie. Zodra je voorbij de datalaag bent (vanaf de database) mag je geen letter SQL meer tegen komen. Voor de rest van de applicatie maakt het vervolgens geen kont meer uit of de data nu uit een databse, via een webservice of door een kleine kabouter die alles snel intikt aangeleverd wordt.


Ik raad je aan om eens naar een DAO/TO oplossing te kijken. Dat lijkt heel erg op (of is eigenlijk meer een specialisatie van) Table Data Gateway. Vooral het TO deel zal je leven een stuk makkelijker maken.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • Ssander
  • Registratie: December 2009
  • Laatst online: 12-06-2023
Wmm schreef op zaterdag 30 januari 2010 @ 13:51:
[...]
Misschien omdat het nuttig is om zelf na te denken over dit soort problemen ipv kant en klare frameworks te gebruiken. Niet dat er iets mis is met frameworks, maar het maakt je imo een betere devver als je zelf over dit soort problemen nagedacht hebt en naar oplossingen zoekt. Het geeft je veel meer inzicht software architectuur.
Dat is inderdaad precies de reden dat ik dit doe. Ik heb al ervaring met Zend, waarin ze ook oplossing 3 gebruiken, net als in de link die Leftblank post. Zoals ik al zei ben ik echter geen groot fan van query builders, dus ik hoopte dat er misschien een andere manier voor is. Hier zelf over nadenken in plaats van gewoon een framework / api / whatever pakken en dit domweg gebruiken leer je een stuk meer van. Uiteindelijk al een bestaand iets gebruiken is geen probleem, maar het is wel leuk als je ook weet wat je aan het doen bent.

Acties:
  • 0 Henk 'm!

  • Ssander
  • Registratie: December 2009
  • Laatst online: 12-06-2023
Janoz schreef op zaterdag 30 januari 2010 @ 14:00:
Optie 3 en 4 zijn sowieso dikke nogos! De hele essentie van een data laag is een scheiding maken tussen de database en de rest van de applicatie. Zodra je voorbij de datalaag bent (vanaf de database) mag je geen letter SQL meer tegen komen. Voor de rest van de applicatie maakt het vervolgens geen kont meer uit of de data nu uit een databse, via een webservice of door een kleine kabouter die alles snel intikt aangeleverd wordt.

Ik raad je aan om eens naar een DAO/TO oplossing te kijken. Dat lijkt heel erg op (of is eigenlijk meer een specialisatie van) Table Data Gateway. Vooral het TO deel zal je leven een stuk makkelijker maken.
Optie 3 is, helaas, de manier waarop het veel gedaan wordt (zie m'n vorige post). De scheiding verdwijnt hierdoor inderdaad. In principe geef je eigenlijk gewoon net als bij optie 4 een query mee, maar dan één die nog moet worden opgebouwd.

Ik zal eens gaan opzoeken wat TO is (Table Object?). Bedankt!

Acties:
  • 0 Henk 'm!

  • Asator
  • Registratie: December 2009
  • Laatst online: 12-02-2024
Ssander schreef op zaterdag 30 januari 2010 @ 14:09:
[...]
Ik zal eens gaan opzoeken wat TO is (Table Object?). Bedankt!
Volgens mij bedoeld Janoz een Transfer Object.

Hier kan je vinden wat DAO's en TO's zijn, het word dan wel uitgelegd door middel van Java maar als je er naar zoekt op google zijn 90% van de resultaten ook op Java gericht.

http://java.sun.com/bluep...rns/DataAccessObject.html
http://java.sun.com/bluep...terns/TransferObject.html

Edit:
Transfer object uitgelegd voor PHP.
http://www.patternsforphp...itle=Data_Transfer_Object

[ Voor 9% gewijzigd door Asator op 30-01-2010 14:28 ]


Acties:
  • 0 Henk 'm!

  • Wmm
  • Registratie: Maart 2002
  • Laatst online: 11-09 14:30

Wmm

@ Janoz: ik ben het volkomen eens met je post. Maar waar ik, net als TS, tegen aan loop: je krijgt bij optie 2 wel heel veel functies. Stel dat je een table users hebt. In bepaalde gevallen wil je findByID, in een ander geval wil je findByName. En stel dat je een searchform hebt waarin naar users gezocht kan worden en waar je b.v. naam en email in kunt vullen, je krijgt dan nog iets als findByNameAndEmail (oid :P). Vooral die searchform dus, hoe ga je daarmee om? Net als TS ben ik hier dus ook mee bezig (geweest) en liep bij punt 2 dus tegen hetzelfde probleem aan.

Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 12-09 10:54

Janoz

Moderator Devschuur®

!litemod

Transfer Object. En het is helemaal niet moeilijk. Eigenlijk is het wat in C een struct genoemd wordt. Een class met enkel data van 1 ding en geen functionaliteit*.

http://www.patternsforphp...itle=Data_Transfer_Object


* geen functionaliteit is natuurlijk niet zo heel strikt. Een (D)TO gebruiker kan natuurlijk best een methode getFullName() hebben die als resultaat de concatenatie van voornaam, tussenvoegsel en achternaam terug geeft.

[ Voor 32% gewijzigd door Janoz op 30-01-2010 14:53 ]

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 12-09 10:54

Janoz

Moderator Devschuur®

!litemod

Wmm schreef op zaterdag 30 januari 2010 @ 14:26:
@ Janoz: ik ben het volkomen eens met je post. Maar waar ik, net als TS, tegen aan loop: je krijgt bij optie 2 wel heel veel functies. Stel dat je een table users hebt. In bepaalde gevallen wil je findByID, in een ander geval wil je findByName. En stel dat je een searchform hebt waarin naar users gezocht kan worden en waar je b.v. naam en email in kunt vullen, je krijgt dan nog iets als findByNameAndEmail (oid :P). Vooral die searchform dus, hoe ga je daarmee om? Net als TS ben ik hier dus ook mee bezig (geweest) en liep bij punt 2 dus tegen hetzelfde probleem aan.
Op zich valt het allemaal wel mee. De meeste objecten haal je op op id en de overige gevallen zijn redelijk te overzien.Mbt de search gebruikten we bij het laatste project waar ik op zat een SearchCriteria object. Hierin stond alle informatie over de search. Je hoeft dan niet een complete permutatie van de mogelijke search combinaties te maken.

Wat je trouwens niet moet vergeten is dat het misschien wel druk lijkt wanneer je veel methoden aan moet maken, maar je zult ergens die functionaliteit moeten implementeren. Al die code in het search deel die zou bepalen welke methode je nu aan zou moeten gaan roepen kun je natuurlijk ook gewoon naar de DAO verplaatsen (en dan bedoel ik niet de code voor het uitlezen van de GET, maar die bepaald of een lege string betekent dat er geen filter voor dat veld is).

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

Verwijderd

Wmm schreef op zaterdag 30 januari 2010 @ 13:51:
[...]


Misschien omdat het nuttig is om zelf na te denken over dit soort problemen ipv kant en klare frameworks te gebruiken. Niet dat er iets mis is met frameworks, maar het maakt je imo een betere devver als je zelf over dit soort problemen nagedacht hebt en naar oplossingen zoekt. Het geeft je veel meer inzicht software architectuur.

@ TS: je kunt voor 1 gaan. De performance die het scheelt lijkt me nihiel (zolang je geen ingewikkelde joins gebruikt). En als je later ook naast column x en y ook z nodig blijkt te zijn hoef je niet je queries aan te passen. Optie 4 is iig iets wat je gewoon niet wilt. Nummer 3 is ook een optie, kijk hiervoor ook eens naar de link die Leftblank post.
Misschien is het nuttig om geen aannames te maken? Je hebt mijn post compleet verkeerd begrepen. De reden waarom je voor een framework kiest is omdat mensen al hebben nagedacht over een probleem dat jij probeerd op te lossen. Je hoeft niet zelf een framework te schrijven om 'een betere developer te worden', frameworks zijn immers opensource, niet waar? Ik heb te veel developers gezien die het wiel opnieuw uit vinden.

Acties:
  • 0 Henk 'm!

Verwijderd

Qua performance is het DTO principe erg winstgevend. Wij gebruiken het nu in de nieuwe architectuur van onze applicatie.

Onze applicatie is van een Access tooltje (bedacht door een manager die zei: 'jongens, ik zou wel een simpel tooltje willen hebben die even dit en dat met een druk op de knop kan uitrekenen') uitgegroeid tot een full-blown service-oriented client-server 'tooltje' met caculatie engine, workflow support en document generatie.

In deze nieuwe architectuur maken we ook gebruik van het DTO object om de data te loodsen door de verschillende tiers (die inderdaad fysiek gescheiden zijn) en je merkt duidelijk dat zonder alle business logica en lazy-loading van child objecten/ collectie er een performance winst behaald is.

De business logica zit dus verborgen op de server en speelt alleen een rol bij het aanroepen van de specifieke services. Wij gebruiken echter geen functionaliteit in de DTOs zoals Janoz zegt, maar wij gebruiken daarvoor de Model van het MVC pattern die een instantie van een DTO beheert en client-side functionaliteit toevoegd.

Wanneer we nu een lijst met DTOs ophalen dan is dit razend snel, ook al zitten we dan vooralsnog lokaal, in vergelijking met de vorige versie waarbij complete domein objecten werden opgehaald en getoond.

Overigens maken we ook gebruiken van een hoop verschillende DAO functies evt. met joins om de benodigde DTOs te vullen.

Acties:
  • 0 Henk 'm!

Verwijderd

Verwijderd schreef op zaterdag 30 januari 2010 @ 21:26:
[...]

Je hoeft niet zelf een framework te schrijven om 'een betere developer te worden', frameworks zijn immers opensource, niet waar? Ik heb te veel developers gezien die het wiel opnieuw uit vinden.
Beetje off-topic en niet bedoeld om een discussie uit te lokken, maar ik vind dat de TS er goed aan doet om zelf hierover na te denken.

Ik heb aan mijn collega ook voorgesteld om een framework te gebruiken zoals hibernate, maar dan kijken we wel tegen een dikke leercurve aan waarvoor simpelweg geen tijd is en dat terwijl onze eigen DAL gewoon in de nieuwe architectuur hergebruikt kan worden (zei het met wat aanpassingen).

Wat ik er maar mee wil zeggen dat een 'framework' niet altijd het gouden ei is.

Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 23:07
Ik zou voor optie 2 gaan (en dat doe ik zelf ook; zelfs al maak ik gebruik van een OR/M zoals NHibernate bv).
Je hebt toch altijd maar een beperkt aantal specifieke queries hoor; je moet maar eens nagaan. :)

In een vorig project, werkten we ook met een aantal 'TableGateway' classes: per tabel een TableGateway, en die class bevatte dan een aantal methods die specifieke queries op die tabel ging uitvoeren (zoals jij zelf in optie 2 al aangeeft). Dan hadden we nog een andere class, die deze tablegateways aansprak, en de gegevens die het van de tablegateways terugkreeg, ging gebruiken om entities te gaan opbouwen.

Nu gebruik ik NHibernate om alle DB access af te handelen. Echter, ook dit ga ik gaan 'verstoppen' in een aantal repositories. Per 'aggregate root' maak ik een repository, en die repository bevat ook een aantal specialized methods. Het zijn die methods die ik aanspreek, en voor de rest ga ik nergens rechtstreeks met NHibernate's HQL of ICriteria gaan spelen.

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 23:07
Verwijderd schreef op woensdag 03 februari 2010 @ 13:47:
[...]


Beetje off-topic en niet bedoeld om een discussie uit te lokken, maar ik vind dat de TS er goed aan doet om zelf hierover na te denken.

Ik heb aan mijn collega ook voorgesteld om een framework te gebruiken zoals hibernate, maar dan kijken we wel tegen een dikke leercurve aan waarvoor simpelweg geen tijd is en dat terwijl onze eigen DAL gewoon in de nieuwe architectuur hergebruikt kan worden (zei het met wat aanpassingen).

Wat ik er maar mee wil zeggen dat een 'framework' niet altijd het gouden ei is.
Dat eigen framework maken kost ook tijd, waarschijnlijk meer tijd dan de tijd die nodig is om een bestaande tool te gaan beheersen.
Dat eigen framework is gegarandeert niet even goed als een 'proven solution'.
Als je ergens anders gaat gaan werken, kan de kennis van een bestaand framework zoals (N)Hibernate van pas komen (als in: je hebt er nog wat aan), terwijl je dat 'custom framework' niet bij een andere werkgever/klant/whatever gaat gebruiken.

Tuurlijk, je zal heel wat bijgeleerd hebben tijdens het ontwikkelen van een eigen framework, maar je hoeft geen eigen O/R framework te schrijven om de achterliggende concepten (grote lijnen) te snappen.

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • Ssander
  • Registratie: December 2009
  • Laatst online: 12-06-2023
Op het moment ben ik bezig om het Zend Framework te leren gebruiken. De leercurve is inderdaad groot en waarschijnlijk kost dit minder tijd dan zelf een framework bouwen. Wanneer het project waar ik mee bezig ben af is heb ik wel veel ervaring met het framework en kan ik deze kennis verder ook gaan gebruiken. Het leren van een framework gebruiken is wat mij betreft dus zeker wel nuttig. Echter denk ik dat het ook geen slecht idee is om (in je eigen tijd) ook een eigen framework te maken. Zend is extreem uitgebreid en hier en daar ook erg ingewikkeld; ik heb al genoeg ideeen om dit veel simpeler te kunnen maken in een zelfgemaakt framework. Of ik dit ooit ga doen weet ik niet, maar het zou zeker wel een interessant en leerzaam project zijn.

Overigens is het niet zo dat Zend (en waarschijnlijk ook andere frameworks) een hele model laag voor je klaar heeft liggen. Hoe je dit implementeert moet je nog steeds zelf weten, al zal er wel een soort van standaard voor zijn. Bij Zend (en ik geloof ook bij Cake) lijkt het erop dat optie 3 (uit mijn eerste post) populair is. Zo is er in Zend een DbTable klasse die extended kan worden waarin al wat basis functionaliteit (CRUD) zit voor een Table Gateway Pattern. Hoe je deze gebruikt kan echter op meerdere manieren; de manieren uit mijn eerste post, waarbij optie 3 dus de 'standaard' lijkt te zijn. Ik vind dit echter totaal niet logisch want de laag die met je Gateways praat heeft daardoor ook database informatie. Wanneer je meegeeft aan de Gateway dat je wilt sortereren op veld 'datum', zul je toch echt ook die laag moeten veranderen als je het veld hernoemt of verwijdert. Volgens mij is het idee juist dat alles in één laag blijft (de Gateway) en je bij database veranderingen (velden hernoemen, tabellen toevoegen, heel je database omgooien naar een OODBMS, etc) alleen naar die laag hoeft te kijken. Bij optie 3 (en 4) is dit duidelijk niet het geval.

Wat mij betreft is optie 2 dus het beste, al is er nog een mogelijkheid welke te zien is op het volgende plaatje;

http://tostidee.nl/sander/datalayer.gif

De UML is totaal niet op de juiste manier gebruikt maar dat is omdat ik het even snel in elkaar heb gezet. Het idee is om nog een laag toe te voegen, hier 'Mapper' genoemd (wist even niets beters, en ik vond het wel aardig kloppen met Fowler zijn definitie van een Data Mapper, maar laat dat aub. niet een groot discussiepunt worden :P). De Mapper gebruikt de Gateways om data te vergaren, en geeft deze data vervolgens terug aan het model, welke het teruggeeft aan de controller op de manier waarop deze het wilt hebben. Zo kan het zijn dat de mapper een DAO/TO aan het model geeft, en dat het model een array aan de controller geeft waar deze makkelijk over heen loopt (of beter gezegd, waar de view vervolgens makkelijk overheen kan lopen).

In dit geval kun je ervoor kiezen om nog steeds optie 2 te gebruiken; de mappers hebben totaal geen database informatie, ze roepen de gateways aan om data op te halen. Zo gebruikt de mapper Blog de gateways Blog en BlogComments om genoeg data te pakken om een blog te weergeven. Echter kun je er ook voor kiezen om nu ook database logica in de mapper toe te laten. Een simpele find(id) zou de mapper nog aan de gateway vragen, maar wanneer de queries wat ingewikkelder worden (bv met joins, en dus niet meer afhankelijk van één tabel is, en dus ook niet van één gateway) zou de SQL zich in de mapper bevinden.

Het idee om de mapper laag toe te voegen is omdat meerdere modellen nu dezelfde functie uit één mapper kunnen aanroepen, maar de data toch anders aan de controller teruggeven. Ook, wanneer je ook SQL in de mapper toe laat, kun je zo de model laag ook met rust laten wanneer je iets aan de database verandert.

Een lange post, excuus, maar ik hoop toch dat er mensen zijn die het willen lezen/skimmen en hun gedachten erover willen delen.

Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 23:07
[b][message=33413455,noline]

maar wanneer de queries wat ingewikkelder worden (bv met joins, en dus niet meer afhankelijk van één tabel is, en dus ook niet van één gateway) zou de SQL zich in de mapper bevinden.
Waarom ?
Een SQL query die joins bevat, kan perfect in de gateway.
Waarom zou je extra complex gaan doen, en die sql statements ergens anders gaan huisvesten ? Je zorgt alleen maar voor verwarring, en op den duur zal je veel van die statements dan in je mapper mogen plaatsen, want, je zal meer 'complexe' (what's in a name) SQL statements hebben, dan eenvoudige.
Wil je dit gewoon doen omdat je dit statement niet 'in een hokje' kunt duwen ?

Een dergelijk SQL statement kan perfect in een tablegateway onderbrengen. Je moet dan gewoon kiezen in dewelke, en dan steek ik het in de TableGateway die de 'root' gegevens teruggeeft. (Moeilijk uit te leggen wat ik bedoel hoor ... ).

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

Verwijderd

whoami schreef op donderdag 04 februari 2010 @ 22:16:
[...]

Dat eigen framework maken kost ook tijd, waarschijnlijk meer tijd dan de tijd die nodig is om een bestaande tool te gaan beheersen.
Dat eigen framework is gegarandeert niet even goed als een 'proven solution'.
Als je ergens anders gaat gaan werken, kan de kennis van een bestaand framework zoals (N)Hibernate van pas komen (als in: je hebt er nog wat aan), terwijl je dat 'custom framework' niet bij een andere werkgever/klant/whatever gaat gebruiken.
Tuurlijk, ik zeg ook niet dat het geen goed idee is om een framework te gebruiken, maar ik krijg bij veel topics die hierover gaan vaak het idee dat het 'not done' is om zelf iets in elkaar te knutselen omdat er 'bewezen' frameworks zijn.

Vaak is het schrijven van een eigen DAL in een professionele omgeving niet eens zo slecht, je weet hoe je business entities in elkaar zitten dus je mapping naar de relationele wereld zal niet zoveel tijd in beslag nemen, het leren van een OR mapper als Hibernatie oid heeft ook een behoorlijk leercurve en daar is niet altijd tijd voor.

Dat neemt niet weg dat ik graag zoiets als Hibernate zou willen leren en natuurlijk als je een bewezen en veel gebruikt framework onder de knie hebt, dan zal dat zeker ten goede komen bij andere projecten. Anderzijds, er zijn zat frameworks geweest die 'de bomb' waren en uiteindelijk ook de prullenbak in gingen.

Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 23:07
Verwijderd schreef op vrijdag 05 februari 2010 @ 09:17:
[...]

Vaak is het schrijven van een eigen DAL in een professionele omgeving niet eens zo slecht, je weet hoe je business entities in elkaar zitten dus je mapping naar de relationele wereld zal niet zoveel tijd in beslag nemen, het leren van een OR mapper als Hibernatie oid heeft ook een behoorlijk leercurve en daar is niet altijd tijd voor.
Maar, er is wel tijd om een eigen DAL in elkaar te zetten 8)7
Nee, even serieus: de leercurve van Hibernate valt best mee. De basics heb je snel onder de knie, en dan kan je al verder. Gaandeweg leer je de andere features ook wel. :)

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • Ssander
  • Registratie: December 2009
  • Laatst online: 12-06-2023
whoami schreef op vrijdag 05 februari 2010 @ 00:27:
Wil je dit gewoon doen omdat je dit statement niet 'in een hokje' kunt duwen ?
Dat is eigenlijk wel de hoofdreden ja. Ik twijfel inderdaad of het wel nuttig is. Aan de ene kant lijkt het me wel een 'nette' manier om het zo te doen, tegelijkertijd maakt het het inderdaad ingewikkelder wanneer je de SQL over twee lagen gaat verdelen. Tegelijkertijd vind ik het ook niet netjes om aan de gateway bv. mee te geven op welke kolom je wilt sorteren (optie 3 uit de originele post) want dan heb je ook database-specifieke informatie in de mapper zitten, wat eigenlijk net zo 'erg' is als SQL in de mapper stoppen wat mij betreft, omdat je dan alsnog in de mapper laag moet gaan kijken wanneer je iets aan de database verandert. Net als Janoz neig ik toch wel heel erg richting optie 2, al ben ik wel nog steeds bang dat je een overkill aan functies krijgt (blogGateway->getIDName, blogGateway->getAllFields, blogGateway->getIDNameShortDescription, etc etc...).

Het zou me niets verbazen als er mensen zijn die vinden dat ik hier veel te veel over nadenk, dus even een snelle verklaring. De reden is zowel omdat ik de oplossing die volgens mij veel gebruikt wordt (optie 3) geen goede optie vind (zoals ik oa. in de vorige paragraaf aangeef), maar ook omdat ik het interessant vind om hier over na te denken en niet simpelweg de meest gebruikte manier over wil nemen en niet echt goed kan beargumenteren waarom ik het zo doe. Het feit dat zowel optie 1, 2 als 3 al door mensen genoemd zijn als een goede oplossing zegt ook wel dat niet iedereen er hetzelfde over denkt. Ook al weet ik dat er binnen programmeren vrijwel nooit een 'beste' manier is, hoop ik toch een 'erg goede' manier te vinden waar ik ook echt achter sta.

Ik zie net dat het plaatje in mijn vorige post achter een .htaccess bestand zit. Ik kom mijn CPanel om de één of andere reden niet in, dus ik heb 'm maar ff op Imageshack gezet.

http://img7.imageshack.us/img7/1408/datalayer.gif

Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 12-09 10:54

Janoz

Moderator Devschuur®

!litemod

Verwijderd schreef op vrijdag 05 februari 2010 @ 09:17:
..Vaak is het schrijven van een eigen DAL in een professionele omgeving niet eens zo slecht...
Nope. In 99% van de gevallen is het het slechtste idee. Dit anti-pattern heeft zelfs een naam. Het NIH anti-pattern.

Ik ben het met je eens dat het schrijven van een compleet eigen datalaag erg leerzaam is en dat het ook goed is voor je begrip, ook van bestaande oplossingen. Maar een professionele omgeving is juist niet de plek om dit op te pakken.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 12-09 10:54

Janoz

Moderator Devschuur®

!litemod

Ssander schreef op vrijdag 05 februari 2010 @ 09:37:
...Net als Janoz neig ik toch wel heel erg richting optie 2, al ben ik wel nog steeds bang dat je een overkill aan functies krijgt (blogGateway->getIDName, blogGateway->getAllFields, blogGateway->getIDNameShortDescription, etc etc...).
Je moet een extra methode wel een beetje afwegen tegen de performance impact. Als ik naar je voorgestelde methoden kijk dan lijkt het of al die methoden eigenlijk dezelfde entity opvragen, maar waarbij telkens een ander deel van de velden gevuld is. Of je nu van 1 record twee velden ophaalt of alle velden maakt natuurlijk nauwelijks uit. Dat gaat pas echt tellen wanneer je meerdere records ophaalt (en dan nog valt de impact heel erg mee). Je moet niet verschillende methoden maken voor de vorm waarin je je record terug wilt krijgen, maar meer over de manier waarop je je records op wilt vragen.

findById() (geeft 1 terug)
findAll() (geeft een lijst terug waarbij de volledige tekst niet ingevuld is, maar de rest wel (incl short description)
findByAuthor(author) (zelfde als boven maar dan gefiltert op auteur)
findBetween(startDate, endDate) (weer zelfde, maar nu met date range. Null interpreteer je gewoon als 'open begin/einde')
Het zou me niets verbazen als er mensen zijn die vinden dat ik hier veel te veel over nadenk,
Nope, gebeurde dat maar meer. Meeste IT blunders komen juist omdat er aan het begin gewoon maar wat gemaakt wordt en pas later nagedacht wordt waarom het zo mis gegaan was.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

Verwijderd

Janoz schreef op vrijdag 05 februari 2010 @ 09:59:
findById() (geeft 1 terug)
findAll() (geeft een lijst terug waarbij de volledige tekst niet ingevuld is, maar de rest wel (incl short description)
findByAuthor(author) (zelfde als boven maar dan gefiltert op auteur)
findBetween(startDate, endDate) (weer zelfde, maar nu met date range. Null interpreteer je gewoon als 'open begin/einde')
Zo werk ik ook het liefst. Helaas bedacht de functioneel ontwerper op mijn huidige project dat je op bijna ieder veld tegelijkertijd moest kunnen zoeken. Wij hebben dit opgelost met een dergelijke constructie:

code:
1
ArticleRepository.GetList(new ArticleCriteria { Author = "John Doe", MinDate="01-01-2010" });


Dit werkt in feite wel prima, maar je moet continu criteria objecten maken. Kan je gek van worden :'(

[ Voor 18% gewijzigd door Verwijderd op 05-02-2010 14:30 ]


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 12-09 10:54

Janoz

Moderator Devschuur®

!litemod

Op zich ben ik het eens met de ontwerper hierin, maar dan eigenlijk ook alleen voor de search actie. Dus naast de eerder genoemde methoden ook een

findByCriteria(searchCriteria)

die dan vergelijkbare objecten als de findAll terug geeft.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'

Pagina: 1