[MVC/SQL] Loosely coupled models met afhankelijke entiteiten

Pagina: 1
Acties:

Onderwerpen


Verwijderd

Topicstarter
Hey :)

Wellicht dat de titel niet helemaal het correcte beeld vormt over het probleem, maar ik zit nu met het volgende. Het doel van mijn vraag is niet zozeer om tot een geweldige, waterdichte oplossing te komen, maar meer om even te brainstormen en tot nieuwe inzichten te komen.

Inleiding

Beschouw de entiteiten A en B. Beide entiteiten hebben een eigen tabel, waarbij er een relatie 1:n bestaat voor deze entiteiten. Met andere woorden: de rows in tabel B hebben een foreign key naar een row in tabel A. Een entiteit B kan dus niet bestaan zonder een "parent" entiteit A. Een correcte analogie hiervoor is bijvoorbeeld een systeem betreffende een paar huizen met kamers. Een kamer behoort toe aan één huis en kan bovendien niet bestaan zonder een huis. Wanneer je een huis met de grond gelijk maakt, moeten de kamers ook worden verwijderd.

Op de server gebruik ik een arbitraire database-abstractie adapter (bijvoorbeeld PDO) om CRUD-bewerkingen uit te voeren. Ik heb een abstracte klasse geschreven met generieke code en functies waarbij ik één model per entiteit die klasse laat extenden. Dus:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interface MyProject_Model_Interface
{
  //Interface containing relevant CRUD-methods
}

abstract class MyProject_Model_Abstract implements MyProject_Model_Interface
{
  //Implementation
}

class MyProject_Model_EntA extends MyProject_Model_Abstract
{
  //Implementation
}
class MyProject_Model_EntB extends MyProject_Model_Abstract
{
  //Implementation
}


Binnen deze klasse worden de PDO-objecten gecreëerd, worden de mySQL queries gegenereerd en uitgevoerd, enzovoort. Dit is de business logic van mijn applicatie.

Het probleem

Het probleem hier is dat ik enerzijds loosely coupled code wil hebben. De klassen MyProject_Model_EntA en MyProject_Model_EntB functioneren op dit niveau dus compleet los van elkaar.
Echter bestaat er op database-niveau wél een relatie tussen deze twee entiteiten. Ik kan niet zomaar een delete() method aanroepen van de MyProject_Model_EntA klasse, omdat daarbij ook een n-aantal entiteiten B moeten worden verwijderd alvorens het verwijderen van de entiteit A.

Mogelijke oplossingen

• Om bovenstaand voorbeeld door te trekken kan ik simpelweg de benodigde code voor het verwijderen van entiteiten B in de klasse MyProject_Model_EntA gooien, met een aparte functie of iets dergelijks. Dit is echter error-prone en je gaat bestaande, werkende code lopen bewerken.
• Het Observer-pattern gebruiken en zo de code aan elkaar knopen. Dit is mijn huidige oplossing voor het huidige probleem. Echter creëer je hiermee nóg een laag code bovenop de bestaande code, maar blijft je code wel netjes schoon. Een zeer groot voordeel is dat je gemakkelijk functionaliteit kunt toevoegen zonder bestaande code te wijzigen. Echter is mijn bezwaar hiertegen dat het semantisch niet he-le-maal klopt, omdat de code niet meer werkt wanneer je de desbetreffende observer (die verantwoordelijk is voor het verwijderen van children entities) loskoppelt. En laat dat nou precies hetgeen zijn wat het observer-pattern zou moeten oplossen: afhankelijkheden tussen code.
• De foreign keys helemaal verwijderen en alle logic op applicatie-niveau afhandelen.

Graag zie ik wat commentaar op bovenstaande mogelijkheden en wellicht dat er zelfs nog andere wegen naar Rome zijn.

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 10:54

Janoz

Moderator Devschuur®

!litemod

Het probleem hier is dat ik enerzijds loosely coupled code wil hebben. De klassen MyProject_Model_EntA en MyProject_Model_EntB functioneren op dit niveau dus compleet los van elkaar.
Ik snap niet helemaal waarom je dit wilt. Zoals je nu ziet levert het problemen op. De entiteiten zijn gekoppeld. Dat is een gegeven. Dat loskoppelen geeft gegarandeerd problemen.

Mijn vraag is dus eigenlijk: Welke voordelen wil je behalen door die entiteiten los van elkaar te koppelen?

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


Verwijderd

Topicstarter
Janoz schreef op donderdag 18 november 2010 @ 13:30:
Mijn vraag is dus eigenlijk: Welke voordelen wil je behalen door die entiteiten los van elkaar te koppelen?
Nou, ik wil de entiteiten niet zozeer loskoppelen om het loskoppelen an sich, maar ik zoek meer een nette manier om op applicatie-niveau netjes met deze afhankelijkheid om te gaan waarbij de code zo schoon en netjes mogelijk blijft.
Bovendien: entiteit B is op zichzelf vrij aanroepbaar en kan prima veranderd en verwijderd worden zonder dat de parent entity, een zekere entiteit A, daarbij betrokken hoeft worden. Dus: ik heb al hoe dan ook een specifieke klasse nodig voor entiteit B om deze bewerkingen uit te voeren.

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 10:54

Janoz

Moderator Devschuur®

!litemod

Persoonlijk zou ik eerder het 'entiteitschap' en de CRUD functionaliteit gaan scheiden. Gewoon het persistence gebeuren achter een service zetten.

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


  • CodeCaster
  • Registratie: Juni 2003
  • Niet online

CodeCaster

Can I get uhm...

Ik roep nu maar wat hoor, maar een 'on delete cascade' doet toch eigenlijk precies wat jij wilt?

Je kunt dan gewoon een A-object verwijderen zonder je daarbij zorgen hoeven te maken dat de B-kinderen daarvan worden verwijderd, want hier zorgt je database al voor.

[ Voor 50% gewijzigd door CodeCaster op 18-11-2010 14:43 ]

https://oneerlijkewoz.nl
Op papier is hij aan het tekenen, maar in de praktijk...


Verwijderd

CodeCaster schreef op donderdag 18 november 2010 @ 14:40:
Ik roep nu maar wat hoor, maar een 'on delete cascade' doet toch eigenlijk precies wat jij wilt?

Je kunt dan gewoon een A-object verwijderen zonder je daarbij zorgen hoeven te maken dat de B-kinderen daarvan worden verwijderd, want hier zorgt je database al voor.
Eensch..

Het is zelfs zo sterk gekoppeld als huizen en kamers zeg jij. Het huis is dus "verantwoordelijk" voor de kamers in dat huis.

Verwijderd

Topicstarter
Janoz schreef op donderdag 18 november 2010 @ 14:13:
Persoonlijk zou ik eerder het 'entiteitschap' en de CRUD functionaliteit gaan scheiden. Gewoon het persistence gebeuren achter een service zetten.
Met andere woorden: je abstraheert de CRUD functionaliteit zodanig, dat deze functioneert onafhankelijk van het type entiteit wat wordt gebruikt? Dat klinkt als een interessante insteek. Heb je hier wellicht een concreet voorbeeld van? :)
CodeCaster schreef op donderdag 18 november 2010 @ 14:40:
Ik roep nu maar wat hoor, maar een 'on delete cascade' doet toch eigenlijk precies wat jij wilt?

Je kunt dan gewoon een A-object verwijderen zonder je daarbij zorgen hoeven te maken dat de B-kinderen daarvan worden verwijderd, want hier zorgt je database al voor.
Klopt, dat doet inderdaad hetzelfde. Ik denk (uiteraard) weer erg ingewikkeld, maar ik vraag me af: is het wenselijk om de database dergelijke logic te laten afhandelen? Met andere woorden: geef je als programmeur op die wijze niet (te) veel uit handen qua controle?

Ik realiseer me natuurlijk dat er voor een willekeurig probleem in dit soort projecten 1001 manieren zijn om het aan te pakken en dat is precies de reden waarom ik ermee kom. Nieuwe inzichten, andere invalshoeken, enzovoort. Puur om er van te leren en wellicht dat een ander daar ook wat aan heeft. Met zoeken op het internet kom je een eind, maar af en toe vind ik het erg verhelderend om met wat "collega's" te brainstormen over dit soort dingen. :P
Verwijderd schreef op donderdag 18 november 2010 @ 15:36:
Het is zelfs zo sterk gekoppeld als huizen en kamers zeg jij. Het huis is dus "verantwoordelijk" voor de kamers in dat huis.
Exact. :)
De kamers kunnen niet bestaan zonder een huis (logisch) en je kunt niet zomaar een huis tegen de vlakte gooien zonder dat de kamers daarbij ook worden meegenomen.

  • Afvalzak
  • Registratie: Oktober 2008
  • Laatst online: 31-08 12:02

Afvalzak

Zet jij mij even buiten?

Verwijderd schreef op donderdag 18 november 2010 @ 21:14:


[...]


Klopt, dat doet inderdaad hetzelfde. Ik denk (uiteraard) weer erg ingewikkeld, maar ik vraag me af: is het wenselijk om de database dergelijke logic te laten afhandelen? Met andere woorden: geef je als programmeur op die wijze niet (te) veel uit handen qua controle?

[...]
Dit is natuurlijk helemaal aan jezelf maar ik denk dat het slimmer is om de database dit te laten doen zodat jij je er niet druk om hoeft te maken, en mocht het ook zo zijn dat er aan de database een andere applicatie gehangen wordt, of de applicatie door iemand anders aangepast gaat worden er niet onverhoopte fouten ontstaan.

Kamers die spontaan tóch verwijderd worden of een huis dat verwijdert wordt en de kamers die blijven bestaan.

Last.fm | Code Talks


  • cariolive23
  • Registratie: Januari 2007
  • Laatst online: 18-10-2024
Verwijderd schreef op donderdag 18 november 2010 @ 12:58:
• De foreign keys helemaal verwijderen en alle logic op applicatie-niveau afhandelen.
Kortom, de reeds uitgevonden wielen weggooien en zelf een poging wagen om iets te maken wat lijkt op een wiel. Dat gaat mislukken, al is het maar omdat jouw applicatie niet ACID is.

En vraag je af waarom FK's überhaupt zijn uitgevonden, het is vast niet omdat Codd niks te doen had op een regenachtige zondagmiddag. ;)

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
cariolive23 schreef op donderdag 18 november 2010 @ 22:29:
En vraag je af waarom FK's überhaupt zijn uitgevonden, het is vast niet omdat Codd niks te doen had op een regenachtige zondagmiddag. ;)
Hehe, daar geef ik je (uiteraard) gelijk in. Echter kan het geen kwaad om ook andere mogelijkheden te onderzoeken, toch? :)

Acties:
  • 0 Henk 'm!

  • YopY
  • Registratie: September 2003
  • Laatst online: 13-07 01:14
Janoz schreef op donderdag 18 november 2010 @ 14:13:
Persoonlijk zou ik eerder het 'entiteitschap' en de CRUD functionaliteit gaan scheiden. Gewoon het persistence gebeuren achter een service zetten.
^. Uit je post merk ik op dat het ophalen van de gegevens in je model-objecten zelf zit. Hierdoor krijgen ze (globaal gezien) twee verantwoordelijkheden: het bevatten van hun informatie, en het zichzelf vullen. Wat je zou moeten doen is een service-niveau toevoegen. Daar doe je dan in:

PHP:
1
2
3
4
5
6
7
function delete($entityAId) {
   // (evt) start transactie
   // query delete from entityBtabel where relatiemetentityAkolom = $entityAid
   // query delete from entityAtabel where id = $entityAid
   // (evt) commit transactie
   // (evt) bij fout rollback transactie
}


Je object entity A heeft dan op zich geen weet van entity B nodig (in je applicatiecode). Wel heeft je applicatie op een ander niveau kennis nodig van de 1:n relatie tussen A en B (waardoor je eigenlijk foreign keys in je applicatie stopt).

Om de analogie door te trekken: Je wilt dus dat je huis wel kamers heeft, maar dat je huis niet weet hoeveel of welke kamers hij heeft - maar als je het huis sloopt wil je zowel het huis + de kamers slopen.

Daar komt de derde partij (het service niveau) bij, de sloper komt langs, kijkt eerst in het huis en sloopt alle kamers eruit alvorens het huis zelf te slopen.

[/vrijdagmiddag uitleg met brakke pseudocode.]
Pagina: 1