[DDD] Verwijderen van objecten uit collecties en undo-ing

Pagina: 1
Acties:

  • whoami
  • Registratie: December 2000
  • Laatst online: 21:01
Stel, je hebt een aantal domein-classes:

dit is slechts een voorbeeld, dus pseudo-code
code:
1
2
3
4
5
6
7
8
9
10
class Customer
{
    private OrderCollection _orders;
}

class Order
{
    private Customer                         _cust;
    private OrderedProductCollection  _products;
}


Stel dus nu dat je een Customer object heb, die een aantal orders heeft.
Je hebt een GUI waar je de orders van een klant kunt bekijken.

Via die GUI kan je een order van een klant verwijderen, en je kan ook order-lijnen van dat order verwijderen.
Stel dus dat je het volgende scherm hebt:
een overzichtsscherm van klanten; van daar kan je een klant bewerken dmv een detail-scherm.
Op dat detail-scherm staan de gegevens van de klant, en ook de orders van die klant.
Vandaaruit kan je ook een detail gaan bekijken van ieder order: op dat OrderDetailScherm zie je de bestel-lijnen. Daar kan je een bestellijn bv verwijderen.
(Misschien niet allemaal bedrijfs-technisch correct, je zal misschien een order als 'geannuleerd' markeren, of een orderlijn als geannuleerd marken, ipv ze echt te verwijderen, maar het gaat hier om het idee).

Stel nu dat je een order-lijn verwijderd op dat schermt, maar, ipv die wijzigingen die je gedaan hebt te bevestigen (door op ok te klikken van het order-detail scherm), annuleer je die wijzing (door op cancel te klikken van het order-detail scherm).
De orderlijnen die je echter verwijderd hebt, blijven verwijderd. Als je dus daarna het order-detail scherm opnieuw opent, zie je de orderlijnen die je net verwijderd hebt niet meer, terwijl dit wel zou moeten zijn (je hebt nl de wijzigingen geannulleerd).
Nu kan je wel zeggen: je moet de orderlijnen pas echt verwijderen uit je domein-object als je op 'OK' geklikt hebt van dat scherm, maar, is dat wel een goed idee ?
Stel nl. dat je op dat scherm ook het bedrag toont van alle bestelde producten samen, dan klopt dit niet met de producten die je op het scherm ziet: de verwijderde staan niet meer op het scherm, maar, ze zitten wel nog in je collectie.

Daarom is het imho beter om de items niet 'echt' uit je collectie te verwijderen, maar ze als 'verwijderd' te markeren dmv een 'IsDeleted' flag op je domein object. Adh daarvan, kan je de verwijderde element niet tonen op je scherm, en ze dan later, als je 'echt' saved, ze in de repository gaan verwijderen. Dit zorgt dan wel weer dat ik iedere keer, zowel in m'n UI, als in m'n business logica, moet gaan checken op die IsDeleted flag. (Bv, als ik een method heb die gewoon het totaal bedrag output, dan moet ik iedere keer die vlag gaan bekijken, zodanig dat enkel de bedragen voor de niet-verwijderde elementen in beschouwing genomen worden).
Ook in m'n UI moet ik gaan checken op die IsDeleted vlag, zodanig dat ik de verwijderde elementen niet toon.

Hoe behandelen jullie zoiets ?

https://fgheysels.github.io/


  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 03-02 12:37

pjvandesande

GC.Collect(head);

In eerste instantie klinkt de 'IsDeleted' flag mij het best in de oren. Dat je in je UI moet checken opde 'IsDeleted' flag klinkt niet echt als een groot probleem. Alleen je vervuild je Business Logics wel enorm.

Ik verwijder eigelijk de Order gelijk, je hebt namelijk al bevestigd dat je de Order wilt verwijderen. Alleen daarna anuleer je de wijzigingen die je hebt gemaakt in de Customer.
Dit is inderdaad niet helemaal netjes en je zou dit kunnen oplossen met een 'IsDeleted' flag.

Een anderen oplossing is ze uit je collection verwijderen en bij het save'n kun je natuurlijk ook kijken of er bepaalde Orders niet meer in de Order list zitten en deze verwijderen uit je databank indien nodig. Dit houd alleen wel weer in dat als gebruiker A de Orders opvraagt en gebruiker B voegt er daarna een toe dat zodra gebruiker A de Orders opslaat de Order die toegevoegd is door gebruiker B ook weer verwijdert word.
Dit kun je weer oplossen door locking, dus het locken van het Customer object zodra gebruiker A dit opvraagt. Alleen vind ik dit ook niet echt netjes, want dit is gewoon niet in alle situaties gewenst.

Al met al vind ik dus eigelijk de manier die jij aanrijkt de beste. :)

[ Voor 5% gewijzigd door pjvandesande op 31-10-2005 11:10 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 21:01
Ik kan in m'n collectie natuurlijk ook een deleted-list bijhouden.
Iedere keer ik een object uit m'n collectie 'verwijder', voeg ik 'm toe aan die deleted list.
Dan moet ik er natuurlijk wel voor zorgen dat ik de items die zich in die deleted list bevinden, wel kan opvragen (in m'n repository), maar ik mag er niet zomaar dingen kunnen aan toevoegen enzo.

https://fgheysels.github.io/


Verwijderd

Ik gebruik zelf vaak OR mappers en daarom kan ik voor ieder object mutaties uitvoeren op objecten zonder dat dit direct wordt doorgevoerd op de persistentie laag. Pas als je daadwerkelijk bevestigt persisteer je het geheel en zijn de wijzingingen doorgevoerd.

  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 03-02 12:37

pjvandesande

GC.Collect(head);

whoami schreef op maandag 31 oktober 2005 @ 11:44:
Ik kan in m'n collectie natuurlijk ook een deleted-list bijhouden.
Iedere keer ik een object uit m'n collectie 'verwijder', voeg ik 'm toe aan die deleted list.
Dan moet ik er natuurlijk wel voor zorgen dat ik de items die zich in die deleted list bevinden, wel kan opvragen (in m'n repository), maar ik mag er niet zomaar dingen kunnen aan toevoegen enzo.
Dom dat ik daar niet aan gedacht heb, inderdaad, dat is een veel beteren manier. Alleen nu is zo'n lijst wel weer eng, niemand moet er zomaar dingen aan kunnen toevoegen en niemand mag de list zomaar even clearen, dit mag alleen de Repository.
Er mag niets direct ge-add worden, de enige manier om iets in die lijst te krijgen is door Order.Remove oid aan te roepen en deze verplaatst zichtzelf naar de deleted-list.
Verwijderd schreef op maandag 31 oktober 2005 @ 11:55:
Ik gebruik zelf vaak OR mappers en daarom kan ik voor ieder object mutaties uitvoeren op objecten zonder dat dit direct wordt doorgevoerd op de persistentie laag. Pas als je daadwerkelijk bevestigt persisteer je het geheel en zijn de wijzingingen doorgevoerd.
Dit betekend alleen dat als je dus een Customer opent voor details, dat je een Transaction moet openen en deze oplaten totdat je de Customer wijzigingen hebt bevestigd.

Je kunt natuurlijk pas een Transaction openen als dit daarwerkelijk nodig is. Alleen je moet de Transaction wel onnodig lang laten open staan. Wat imho niet goed is.

[ Voor 41% gewijzigd door pjvandesande op 31-10-2005 12:06 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 21:01
Verwijderd schreef op maandag 31 oktober 2005 @ 11:55:
Ik gebruik zelf vaak OR mappers en daarom kan ik voor ieder object mutaties uitvoeren op objecten zonder dat dit direct wordt doorgevoerd op de persistentie laag. Pas als je daadwerkelijk bevestigt persisteer je het geheel en zijn de wijzingingen doorgevoerd.
De mutaties op de persistence layer zijn geen probleem, het gaat 'm er om om de wijzingen die je in het geheugen gedaan hebt, te undo'en.
Dom dat ik daar niet aan gedacht heb, inderdaad, dat is een veel beteren manier. Alleen nu is zo'n lijst wel weer eng, niemand moet er zomaar dingen aan kunnen toevoegen en niemand mag de list zomaar even clearen, dit mag alleen de Repository.
Ik zit er aan te denken om die enkel mbhv een Enumerator beschikbaar te maken.

[ Voor 25% gewijzigd door whoami op 31-10-2005 12:09 ]

https://fgheysels.github.io/


  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 03-02 12:37

pjvandesande

GC.Collect(head);

whoami schreef op maandag 31 oktober 2005 @ 12:08:
[...]

Ik zit er aan te denken om die enkel mbhv een Enumerator beschikbaar te maken.
Alleen je Repository moet hem wel kunnen clearen. Hij moet voor je Repository enumerable zijn, alleen voor anderen zie ik daar het nut niet zo van in, of je moet een lijstje willen tonen bij de bevestiging dat je Order x en y gaat verwijderen.

Verwijderd

questa schreef op maandag 31 oktober 2005 @ 12:01:
Je kunt natuurlijk pas een Transaction openen als dit daarwerkelijk nodig is. Alleen je moet de Transaction wel onnodig lang laten open staan. Wat imho niet goed is.
Nee dat hoeft niet. Je kunt ook optimistic locken.

Anyways, anders implementeer je toch gewoon memento oid...

  • whoami
  • Registratie: December 2000
  • Laatst online: 21:01
questa schreef op maandag 31 oktober 2005 @ 12:11:
[...]


Alleen je Repository moet hem wel kunnen clearen.
Idd.
Ik kan in m'n base - class ook een method maken ala 'AcceptChanges', die je moet aanroepen eens je alles gepersisteerd hebt (cfr AcceptChanges v/d Dataset).

https://fgheysels.github.io/


  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 03-02 12:37

pjvandesande

GC.Collect(head);

whoami schreef op maandag 31 oktober 2005 @ 12:13:
[...]

Idd.
Ik kan in m'n base - class ook een method maken ala 'AcceptChanges', die je moet aanroepen eens je alles gepersisteerd hebt (cfr AcceptChanges v/d Dataset).
Dan neem ik aan dat je deze method binnen de Repository gaat gebruiken?

Deze method flikkerd dan de deleted-list weer leeg?

[ Voor 10% gewijzigd door pjvandesande op 31-10-2005 12:32 ]


  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 03-02 12:37

pjvandesande

GC.Collect(head);

* pjvandesande kickt deze even omhoog en is benieuwd hoe whoami het nou heeft aangepakt.

  • whoami
  • Registratie: December 2000
  • Laatst online: 21:01
Ik denk dat ik het toch met zo'n 'deleted' list ga oplossen.
Ik heb meteen ook ff naar CSLA gekeken, en daar eea van gespiekt. :P

Ik zal een interface IUndoable hebben, die deze methods definieert:
code:
1
2
3
BeginEdit
CommitEdit
RollbackEdit


Ik heb een abstracte BusinessObject class, en een abstracte BusinessObjectCollection class die beide deze interface implementeren.
Dit houdt dus in dat 'in - memory' wijzigingen kunnen ge-undo'd worden.
In het geval van de BusinessObject collection wil dat dus ook zeggen dat verwijderde items, terug moeten kunnen gezet worden indien de wijzigingen ge-undo-ed worden.
Daarom heb ik binnen die BusinessObjectCollection class een private 'deletedItems' collectie, waar ik de verwijderde items bijhou.
Echter, m'n repository moet natuurlijk wel weten welke objecten hij ook mag verwijderen uit die collectie, dus, moet die collectie de 'deleted-items' kunnen teruggeven. Daarom had ik gedacht aan een property/method die gewoon de inhoud van de deletedItems collectie als een array van BusinessObject teruggeeft. Dit is misschien niet zo netjes, maar ik moet dit op de een of andere manier toch kunnen doen...

Opmerkingen, kritiek, verbeteringen, etc... zijn welkom. Als ik dit geimplementeerd heb, en een testprojectje gemaakt heb, wil ik best wel eens wat code posten.

https://fgheysels.github.io/


  • mulder
  • Registratie: Augustus 2001
  • Laatst online: 20:26

mulder

ik spuug op het trottoir

whoami schreef op woensdag 02 november 2005 @ 12:38:
[...]Daarom had ik gedacht aan een property/method die gewoon de inhoud van de deletedItems collectie als een array van BusinessObject teruggeeft. Dit is misschien niet zo netjes, maar ik moet dit op de een of andere manier toch kunnen doen...
[...]
Misschien moet je de repository alleen vertellen welke items te behouden.

oogjes open, snaveltjes dicht


  • whoami
  • Registratie: December 2000
  • Laatst online: 21:01
Don Facundo schreef op woensdag 02 november 2005 @ 13:01:
[...]


Misschien moet je de repository alleen vertellen welke items te behouden.
Dan zou dat willen zeggen dat ik bv alle 'Items' in een 'Container' eerst moet verwijderen uit de DB, en dan alles wat ik nog heb terug moet toevoegen....
Ik geloof niet dat dat altijd lukt...

https://fgheysels.github.io/


  • mulder
  • Registratie: Augustus 2001
  • Laatst online: 20:26

mulder

ik spuug op het trottoir

Ja of je zegt 'wat had ik' en 'wat heb ik nog', maar dat komt volgens mij op het zelfde neer als zeggen 'wat wil ik verwijderen'..

oogjes open, snaveltjes dicht


  • whoami
  • Registratie: December 2000
  • Laatst online: 21:01
Don Facundo schreef op woensdag 02 november 2005 @ 13:22:
Ja of je zegt 'wat had ik' en 'wat heb ik nog', maar dat komt volgens mij op het zelfde neer als zeggen 'wat wil ik verwijderen'..
Als je zegt: wat had ik, dan moet je een query op je DB doen voor je verwijderd, of moet je de state van de collectie bijhouden zoals deze was toen je ze net opgehaald hebt.

https://fgheysels.github.io/


  • mulder
  • Registratie: Augustus 2001
  • Laatst online: 20:26

mulder

ik spuug op het trottoir

Dat laatste bedoelde ik ook :) Zit hardop meet denken hoor, vind het nl wel interessant om een manier te vinden om netjes door te geven wat te verwijderen, dit wil je namelijk 'hidden' voor bovenliggen lagen lijkt mij?

oogjes open, snaveltjes dicht


  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 03-02 12:37

pjvandesande

GC.Collect(head);

Geld de BeginEdit nou voor alle objecten binnen je Collection of alleen voor je collection?
Don Facundo schreef op woensdag 02 november 2005 @ 13:01:
[...]


Misschien moet je de repository alleen vertellen welke items te behouden.
Je kunt inderdaad met het IN statement aan de gang binnen je queries. Maar dan zit je altijd nog met locking etc, want stel dat user A alles ophaalt, daarna user B 1 toevoegd, daarna user A alles opslaat.
User A heeft het toegevoegde van user B nog niet, dus dit zal ook verwijderd worden.

Dit lijkt mij een groter probleem, een deleted-list is imho veel makkelijker.

  • whoami
  • Registratie: December 2000
  • Laatst online: 21:01
questa schreef op woensdag 02 november 2005 @ 13:31:
Geld de BeginEdit nou voor alle objecten binnen je Collection of alleen voor je collection?
Hoe bedoel je ?
Don Facundo schreef op woensdag 02 november 2005 @ 13:30:
vind het nl wel interessant om een manier te vinden om netjes door te geven wat te verwijderen, dit wil je namelijk 'hidden' voor bovenliggen lagen lijkt mij?
Ja, dit zou ik idd liefst hidden houden, maar ik denk niet dat het mogelijk is. :P

[ Voor 46% gewijzigd door whoami op 02-11-2005 13:36 ]

https://fgheysels.github.io/


  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 03-02 12:37

pjvandesande

GC.Collect(head);

C#:
1
2
3
4
5
6
7
_orders.BegintEdit();

Order selectedOrder = _orders[ selectionIndex ];
selectedOrder.Description = "nieuwe omschrijving";
selectedOrder.Items.RemoveAt( 5 );

_orders.UndoEdit();


Worden nu ook alle Orders die je gewijzigd hebt ook ge-undo'd (wat een woord)?
whoami schreef op woensdag 02 november 2005 @ 13:36:
Ja, dit zou ik idd liefst hidden houden, maar ik denk niet dat het mogelijk is. :P
Enige manier die ik kan bedenken is Reflection, maar daar zou persoonlijk niet snel voor kiezen.

[ Voor 28% gewijzigd door pjvandesande op 02-11-2005 13:40 ]


  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

wat ik soms eerder zou durven doen:

je maakt een nieuwe collection aan en stopt alle order/bestellijnen daarin.
daarna muteer je die.
wanneer je dan klaar bent, kun je die gewoon terugzetten/vergelijken/...

die collection zit dan dus in je Form logic

ASSUME makes an ASS out of U and ME


  • whoami
  • Registratie: December 2000
  • Laatst online: 21:01
questa schreef op woensdag 02 november 2005 @ 13:39:
[...]


C#:
1
2
3
4
5
6
7
_orders.BegintEdit();

Order selectedOrder = _orders[ selectionIndex ];
selectedOrder.Description = "nieuwe omschrijving";
selectedOrder.Items.RemoveAt( 5 );

_orders.UndoEdit();


Worden nu ook alle Orders die je gewijzigd hebt ook ge-undo'd (wat een woord)?
dat zou wel de bedoeling zijn, ja.
HIGHGuY schreef op woensdag 02 november 2005 @ 13:42:
wat ik soms eerder zou durven doen:

je maakt een nieuwe collection aan en stopt alle order/bestellijnen daarin.
daarna muteer je die.
wanneer je dan klaar bent, kun je die gewoon terugzetten/vergelijken/...

die collection zit dan dus in je Form logic
Daar had ik ook even aan gedacht, maar wordt je form dan niet te complex ?

[ Voor 121% gewijzigd door whoami op 02-11-2005 13:46 ]

https://fgheysels.github.io/


  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 03-02 12:37

pjvandesande

GC.Collect(head);

HIGHGuY schreef op woensdag 02 november 2005 @ 13:42:
wat ik soms eerder zou durven doen:

je maakt een nieuwe collection aan en stopt alle order/bestellijnen daarin.
daarna muteer je die.
wanneer je dan klaar bent, kun je die gewoon terugzetten/vergelijken/...

die collection zit dan dus in je Form logic
Ontzettend leuk punt!

Ik zou hier zelf niet snel voor kiezen omdat je waarschijnlijk op meerderen plaatsen in je applicatie dezelfde code krijgt om dit te realiseren.
Opzich als het op 3 plaatsen staat is dat nog wel te overzien, maar imho een doorn in het oog :)

  • The Eagle
  • Registratie: Januari 2002
  • Laatst online: 19:49

The Eagle

I wear my sunglasses at night

Typisch. Dit gevalletje herken ik wel uit mijn webbased PeopleSoft omgeving. Daar wordt met het zgn "deferred processing" gewerkt, wat zoveel inhoud als dat de pagina + de gegevens die daar in staan, pas op het moment dat op opslaan geklikt wordt gevalideerd en indie valide opgeslagen. Dit itt on line validatie en tijdelijke opslag in een tussentabel, wat eigenlijk min of meer standaard is bij de meeste schermen.
Ik heb geen flauw idee of dit iets is waar je mogelijk wat aan hebt (ik snap ook geen drol van je hele code, ben geen codeklopper :p), maar ik denk dat het idee een beetje gelijk is aan wat je wilt bereiken :)

Al is het nieuws nog zo slecht, het wordt leuker als je het op zijn Brabants zegt :)


  • mulder
  • Registratie: Augustus 2001
  • Laatst online: 20:26

mulder

ik spuug op het trottoir

En als je je objecten nou laat overerven van een object uit de repository..?

oogjes open, snaveltjes dicht


  • whoami
  • Registratie: December 2000
  • Laatst online: 21:01
Don Facundo schreef op woensdag 02 november 2005 @ 14:23:
En als je je objecten nou laat overerven van een object uit de repository..?
Ik had gedacht om een soort van 'base' repository te maken, waar al m'n repositories van over erven, en deze 'base' repository in dezelfde assembly te plaatsen als m'n 2 basis class (BusinessObject en BusinessCollection). Dan kan ik die GetDeletedItems() een internal access modifier geven, zodat deze wel beschikbaar is in m'n repository.
Echter, ik denk dat dit ook te beperkt is; wie zegt dat ik m'n repositories en domein objecten altijd in dezelfde assembly kwijt wil ?

[ Voor 8% gewijzigd door whoami op 02-11-2005 14:32 ]

https://fgheysels.github.io/


  • Cuball
  • Registratie: Mei 2002
  • Laatst online: 03-02 20:14
Als dit niet zo veel voorkomt en geen al te grote collecties zijn zou ik durven bij het drukken van de cancel knop de collectie opnieuw op te halen.

-als je met caching werkt zou dit snel moeten gaan
-je hebt als gebruiker dan altijd de laatste versie (dus geen probleem als meerdere gebruikers aan hetzelfde order bezig zijn)

anders zou ik gebruik maken van een deleted flag.

"Live as if you were to die tomorrow. Learn as if you were to live forever"


  • mulder
  • Registratie: Augustus 2001
  • Laatst online: 20:26

mulder

ik spuug op het trottoir

whoami schreef op woensdag 02 november 2005 @ 14:32:
[...]
Echter, ik denk dat dit ook te beperkt is; wie zegt dat ik m'n repositories en domein objecten altijd in dezelfde assembly kwijt wil ?
Kan je niet iets met sealed doen? (Dit is voor mij een beetje klok klepel verhaal hoor :P)

(als in accessor dan, maar hier onder zegt al van niet)

[ Voor 11% gewijzigd door mulder op 02-11-2005 15:21 ]

oogjes open, snaveltjes dicht


  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 03-02 12:37

pjvandesande

GC.Collect(head);

whoami schreef op woensdag 02 november 2005 @ 14:32:
[...]
Echter, ik denk dat dit ook te beperkt is; wie zegt dat ik m'n repositories en domein objecten altijd in dezelfde assembly kwijt wil ?
Nouja, alleen je abstract BusinessObject hoeft in dezelfde assembly te staan. Ik werk hier veel met internal methods.
Don Facundo schreef op woensdag 02 november 2005 @ 15:00:
[...]


Kan je niet iets met sealed doen? (Dit is voor mij een beetje klok klepel verhaal hoor :P)
Verkeerde klok of klepel :Y)

[ Voor 27% gewijzigd door pjvandesande op 02-11-2005 15:01 ]

Pagina: 1