WPF MVVM business logic in entities?

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • BikkelZ
  • Registratie: Januari 2000
  • Laatst online: 21-02 08:50
Ik werk aan een project met de volgende acroniemen:

WPF, MVVM, LLBLGen

In dit framework worden of de entities direct tegen een editscherm geplakt, bijvoorbeeld bij stamtabelletjes, of via een View iets meer gescheiden van XAML en entity, maar eigenlijk kan bijvoorbeeld een checkbox nog steeds direct het entityveld manipuleren in vrijwel alle gevallen.

Nu is er één entity die normaliter een proces doorloopt, waarbij steeds meer gegevens verzameld en opgeslagen worden. Stel je bijvoorbeeld voor dat je een registratieproces doorloopt waarbij je in meerdere schermen gegevens moet invullen en dat je tegen het einde een geboortedatum verplicht wil hebben voor die entity terwijl je dat in scherm 1 nog niet wil afdwingen.

Prima.

Nu wil ik tussendoor ergens anders een bepaalde status aanpassen, en vervolgens de entity opslaan. Echter, nu wil mijn entity al gaan checken op velden die ik nog niet gezet heb, want dat doe ik daar nog niet. Om dat te omzeilen heb ik de volgende peop-code geschreven:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
        /// <summary>
        ///  We want to be able to change the registration type at any moment.
        /// </summary>
        public bool ChangeRegistrationTypeOverridingStatus(RegistrationTypeEntity newRegistrationType)
        {
            // Set the special flag for status overriding
            IsStatusOverridden = true; // Dit is een bool veld wat ik zelf heb toegevoegd

            // Then determine status again to get a non-checking status 
            DetermineStatus(); // Dit doorloopt hij altijd voordat hij Save()d dus ik kan hier ook niet omheen

            // Otherwise we don't always validate the business rules when saving....
            try
            {
                RegistrationType = newRegistrationType;
                return Save();
            }
            catch (Exception ex)
            {
                Log.Error(ex);
                return false;
            }
            finally
            {
                // Then we put the status back
                IsStatusOverridden = false;

                // Then determine status again to get the previous status back
                DetermineStatus();
            }
        }


Dit doet determine status. Hij kijkt verderop nog naar de waarde van bepaalde velden van de entity om te bepalen welke status hij heeft.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
        private void DetermineStatus()
        {
            Status = RegistrationStatus.Created;

            if (IsStatusOverridden)
            {
                Status = RegistrationStatus.Override;
                return;
            }

// some more checks.....
}


Voordat hij Saved wordt altijd deze functie uitgevoerd:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
        public override void ValidateFields()
        {
            base.ValidateFields();

            // When the status is overridden, we only want to validate the base
            if (Status == TreatmentTrajectStatus.Override)
                return; // Hier zorg ik er dus voor dat hij eigenlijk helemaal niks meer valideert

            if (IsRegistration && IsFirstStepDone)
            {
                if (BirthDay == null)
                {
                    SetFieldError(RegistrationFields.BirthDay, "Er moet een geboortedatum ingevoerd zijn");
                }
            }
// Some more validation
}


Ik vind het op zich een goed schema dat je bepaalt wat je moet valideren en dat dan valideert, zo dat je per stap een op maat gesneden groep validaties hebt. Echter, omdat deze code zich in de Entity bevindt, kan ik nu niet meer direct de Entity manipuleren zonder dit soort vieze hacky code te gebruiken, terwijl ik gewend ben meer vanuit een traditioneel three-tier model te denken waarbij een Business laag alle validaties doet en de Entities zeg maar aan je UI knoopt. En dan kan ik dus heel eenvoudig mijn entities blijven manipuleren terwijl aan de andere kant ik ook die geleidelijk aan striktere validatieregels kan toepassen per scherm, gewoon doordat dat andere functies zijn in mijn Business laag.

Wat mijn vraag is, bovenop of dit wel of niet de manier is om het praktisch gezien te doen, wat nou de achterliggende filosofie cq theorie is om in dit soort MVVM applicaties dit soort vraagstukken te tackelen.

iOS developer


Acties:
  • 0 Henk 'm!

  • SaphuA
  • Registratie: September 2005
  • Laatst online: 06-05 16:40
.

[ Voor 103% gewijzigd door SaphuA op 31-01-2022 15:39 ]


Acties:
  • 0 Henk 'm!

  • BikkelZ
  • Registratie: Januari 2000
  • Laatst online: 21-02 08:50
De Entities zijn in dit geval LLBLGen Entities en kunnen naar mijn mening nooit unmanaged POCO objecten zijn? Bedoel je dat je voor iedere FietsEntity een Fiets class hebt die alle logische en validatietaken afhandelt als TrapPedaal(Pedaal pedaal) en IsKettingHeel zoals in klassieke OO, en dan de persistency afhandelt met de Entity zodat de UI niets van de Entity ziet? Want zoals het framework nu in elkaar zit zitten Entities er heel diep in verweven en kán ik er dus niks meer "tussen plakken".

Het is ook opgezet door een behoorlijk slimme jongen met wel enige naam en faam in de .Net wereld. Wellicht dat ik dus even een aha-erlebnis niet heb zoals je in alle stappen hebt in je programmeercarrière van je eerste stapjes op het gebied van OO, wat doet een Interface, hoe werkt MVC, waarom rocken Unit Tests vaak (maar niet altijd), etcetera. Van die dingen waar je eerst tegenaan kijkt met een houding "waarom moeilijk doen" terwijl het achteraf gewoon een mooi elegant idee is.

-------

Even een concreet voorbeeld:

Ik heb een complexe set business rules die meerdere entities overspant. Deze business rules bepaal ik een een Helper class. Dus als x.val1 == null en y.val3 == 7 en z.blaDatum > gisteren neem actie 3 en 5, maar als z.blaDatum < gisteren dan doe 3 en 7 en draai 5 dan terug. En zo nog een stuk of 12 mogelijke scenario's.

Deze business rules zaten eerst over meerdere schermen (ViewModels) en entities uitgesmeerd, waardoor je nooit een overzicht had van wat wanneer gebeurde, en duplicate code had op sommige plekken waar effectief de zelfde regel uitgevoerd zou moeten worden. Het "voordeel" hier van was wel dat ze gewoon op de entities konden meeliften, terwijl alle acties vanuit de Helper class uitgevoerd worden min of meer apart gebeuren van wat er in de ViewModels en Entities gebeurt rondom de UI.

[ Voor 34% gewijzigd door BikkelZ op 03-02-2012 11:49 ]

iOS developer


Acties:
  • 0 Henk 'm!

  • Rickets
  • Registratie: Augustus 2001
  • Niet online

Rickets

Finger and a shift

Probeer je wat over de filosofie van de code waarmee je werkt te leren? Of wil je weten hoe je het wat handiger opzet met de entities van LLBLGen?
Ik werk zelf op een andere manier met LLBLGen, dus ik hoop dat ik goed begrijp wat je probeert te bereiken.

Als je validatie van je entity niet in je entity zelf wil hebben, kan je een losse Validator-class maken. Dit gaat per entitytype, en in die validator kan je dan je fields en je entity valideren. LLBLGen heeft een eigen DI-framework waarmee je de validator heel makkelijk automatisch aan de juiste entity kan koppelen.
Als je validatie van meerdere soorten entity wil doen, ontkom je niet aan een tussenlaag voor je validatieregels.

Je kan daarnaast ook gebruik maken van de UnitOfWork-class van LLBLGen. Die is bij uitstek geschikt om data/acties over meerdere schermen te verzamelen, met methods als AddForSave of AddDeleteEntitiesDirectlyCall. Aan het eind van alle schermen roep je dan de Commit van UnitOfWork aan en worden alle acties naar de database gestuurd.

De documentatie van LLBLGen legt het uitgebreider uit, dus daar zou je eens kunnen kijken of je wat aan deze twee zaken hebt.

If some cunt can fuck something up, that cunt will pick the worst possible time to fucking fuck it up, because that cunt’s a cunt.


Acties:
  • 0 Henk 'm!

  • BikkelZ
  • Registratie: Januari 2000
  • Laatst online: 21-02 08:50
Ik ben nog niet ver genoeg doorgedrongen in de applicatie om te zeggen dat Commit altijd na mijn Helper acties uitgevoerd wordt en het dus altijd voldoende is om vanuit mijn Helper de Entity gewoon in UnitOfWork te zetten, maar UnitOfWork wordt inderdaad wel veelvuldig gebruikt dus die kans is zeer groot aanwezig. Dus dat zou wellicht een deel van het concrete probleem kunnen oplossen, tenzij de Entity al een keer gecommit is van te voren.

De losse Validatorclasses zouden wellicht ook wat makkelijker werken in verband met extra validaties bovenop je basisvalidatie. Dat je zegt: een Persoon heeft minimaal een voornaam, achternaam en geboortedatum. Dat handel je af op Entity niveau. En dat je een extra Validator toevoegt op het moment dat je iemand op overleden wilt zetten vanuit je DeceasedViewModel en dus een overlijdensdatum dan ook verplicht is. Maar ik neem aan dat een Validator class je huidige validatie vervangt? Of worden die allebei nog uitgevoerd?

Maar uiteindelijk ben ik inderdaad niet op zoek naar direct een praktische oplossing, aangezien het op dit moment gewoon werkende code is, maar wil ik naar een soort standaard toe voor in ieder geval nieuwe functionaliteit en wellicht ook het refactoren van bestaande functionaliteit. Tot nu toe kom ik nog wel wat euhm... variaties van implementatiewijze tegen in de code.

iOS developer


Acties:
  • 0 Henk 'm!

  • Guldan
  • Registratie: Juli 2002
  • Laatst online: 02-07 22:34

Guldan

Thee-Nerd

Het klinkt alsof jij een nTier model wilt hebben met een losse domeinlaag. Aangezien je nu op llblgen entity niveau een save hebt. Je zou ipv. self servicing voor adapter kunnen kiezen in llblgen. Dit levert je een losse domain laag op van llblgen entiteiten die de adapters kunnen saven. Je business logica beleg je in het N-tier model in de BLL (Business Logic Layer)

De validatie kan je perfect laten regelen door wpf d.m.v INotifyDataErrorInfo. Ik hoop dat ik je nu iig een lijst van termen heb gegeven waar je in kan duiken zodat je een goede oplossing hebt.

You know, I used to think it was awful that life was so unfair. Then I thought, wouldn't it be much worse if life were fair, and all the terrible things that happen to us come because we actually deserve them?


Acties:
  • 0 Henk 'm!

  • BikkelZ
  • Registratie: Januari 2000
  • Laatst online: 21-02 08:50
Ondanks dat ik het niet wil vind ik het wel grappig dat je constateert uit mijn verhaal dat ik een nTier applicatie wil hebben. Dat is namelijk precies wat ik gewend ben om te doen en waar mijn mindset misschien nog een beetje te veel op blijft haken. Maar om op dat niveau nog deze applicatie te gaan herschrijven is absoluut niet meer haalbaar zijn, dus ik moet zo goed als mogelijk rollen met wat er al is.

En ik heb al genoeg applicatie gezien waarin iemand halverwege een nog veel beter idee had, dat niet doorgevoerd heeft in de oude code (want te veel werk) waardoor je helemaal niet meer weet wat je nog kan verwachten. Liever een suboptimale standaard die consistent is dan de beste oplossing op vijf verschillende manieren geïmplementeerd. Dat is niet onderhoudbaar.

iOS developer


Acties:
  • 0 Henk 'm!

  • jip_86
  • Registratie: Juli 2004
  • Laatst online: 00:22
Wil je niet een wizardidee hebben met een validatie op je volgende/vorige stap? Wij hebben hier ook wel zo iets waar je de status van de volgende/vorige knop kan bepalen aan de hand van de data die je verzamelt.
Pagina: 1