Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[PHP] Versiebeheer van rule engine logica

Pagina: 1
Acties:

  • Gimmeabrake
  • Registratie: December 2008
  • Laatst online: 19-11 21:37
Stel ik gebruik een rule engine die aan de hand van bepaalde input iets goedkeurt of afwijst. Om de zoveel tijd wordt er gevraagd om de logica aan te passen die evalueert of de input goed- of afgekeurd wordt. De wijzigingen van de logica gebeuren niet vaak genoeg om een hele UI waarmee de klant de rules zelf kan aanpassen te kunnen verantwoorden.

Het probleem is dat op het moment van wijzigen van de evaluatielogica er nog mensen in een werkproces zitten met de oude logica, en deze werkprocessen niet de nieuwe logica mogen gaan gebruiken. Wij moeten dus bij het opstarten van zo'n werkproces het versienummer van de logica op kunnen slaan en aan de hand daarvan de juiste versie van de logica in kunnen laden.

Zelf zat ik te denken dit met namespaces en PSR-autoloading op te lossen:
code:
1
2
3
4
[...]\Logic\LogicInterface.php - interface die I/O definieert
[...]\Logic\LogicLoader.php - geeft de gevraagde implementatie terug (aan de hand van versie)
[...]\Logic\1\Logic.php - implementatie van interface, versie 1
[...]\Logic\n\Logic.php - implementatie van interface, versie n


Op de een of ander manier voelt dit heel erg smerig aan, ik word er niet heel erg vrolijk van. Wat vinden jullie? Hebben jullie hier ervaring mee?

  • creator1988
  • Registratie: Januari 2007
  • Laatst online: 21-11 16:30
Wat wij deden:

1. Je hebt een loadbalancer die sticky sessions heeft. E.g. user125 is connected naar 127.0.0.1:8133
2. Bij een upgrade start je nieuwe processen op met de nieuwe versie, en je markt alle oude processen.
3. Nieuwe users komen alleen op nieuwe processen
4. Als alle sessies zijn verlopen op oud proces -> kill proces

Zo blijft de user op dezelfde versie hangen tot zijn sessie eindigt (kan je evt. nog een tijdslimiet aan hangen, bij ons 60 minuten).

  • Gimmeabrake
  • Registratie: December 2008
  • Laatst online: 19-11 21:37
Bedankt voor de input, maar ik vermoed dat je me fout hebt begrepen. Misschien had ik het woord werkprocessen niet moeten gebruiken, dat kan nogal verwarrend zijn :+. De processen waar ik het over heb zijn bedrijfsprocessen die meerdere weken kunnen lopen, waarvan de status in een database staat opgeslagen. Als zo'n proces in een bepaald stadium terecht komt mag de output van bepaalde logica niet meer zomaar veranderen, dit omdat de output van de oude logica op dat moment contractueel al is vastgelegd. Daarom moeten we deze oude versies via persistent versiebeheer vastleggen.

[ Voor 8% gewijzigd door Gimmeabrake op 22-05-2014 16:19 ]


  • storeman
  • Registratie: April 2004
  • Laatst online: 21-11 13:00
Ik kan me helemaal indenken dat het smerig aanvoelt, maar ik zou ook op zoiets uitkomen. En of je het dan met mappen doet of met bestanden, maakt niets uit voor de 'smerigheid'.

Een andere optie kan natuurlijk zijn om in ieder project of item, een kopie te maken van de classes die veranderen en daar de versie te behouden. Bugfixes en dergelijke lopen dan niet door, dus het zal er niet beter van worden.

"Chaos kan niet uit de hand lopen"


  • ForReal
  • Registratie: Oktober 2012
  • Laatst online: 13-11 13:05
Zou idd ook in die richting gaan zoeken.

Precieze implementatie is uiteraard afhankelijk van karakteristieken van jullie werkproces.

Stel dat je werkproces uit pakweg 50 stappen bestaat en tussen v2 en v3 veranderen maar 4 van die stappen. Ga je dan al je afzonderlijke stappen extenden of niet?

Wat dingen die denk ik voor onderhoudbaarheid wel belangrijk zijn:
* Alles lekker expliciet houden. Dus processloader benoemt gewoon duidelijk welke stappen in een bepaald proces horen. Geen voodoo als voodoo niet nodig is.
* Tests voor afzonderlijke stappen zodat, als oude procesversies niet meer relevant zijn, je de bijbehordende code naar recentere versies kunt refactoren, zonder met een groeiende hoop legacy te blijven zitten.

  • hillbillie
  • Registratie: November 2010
  • Laatst online: 18-07-2022
Kan je niet meerdere applicaties deployen e.g. "v2", "v3" etc waarbij het op user niveau bepaalt welke API er gebruikt gaat worden?

Ik zou nooit in je code zelf beslissingen a.d.v versie maken (zoals jouw opzet nu is met 2 verschillende logic.php classes).

  • Gimmeabrake
  • Registratie: December 2008
  • Laatst online: 19-11 21:37
storeman schreef op vrijdag 23 mei 2014 @ 11:22:
Een andere optie kan natuurlijk zijn om in ieder project of item, een kopie te maken van de classes die veranderen en daar de versie te behouden. Bugfixes en dergelijke lopen dan niet door, dus het zal er niet beter van worden.
Het zijn geen aparte projecten, dus het lijkt me inderdaad niet handig om kopieen te maken. Je zou hooguit de classes in een DB kunnen stoppen en dan kunnen eval'en. Maar volgens mij zou ik (terecht) mijn baan op het spel zetten als ik daarmee als optie aan zou komen zetten bij dit probleem. :+
hillbillie schreef op maandag 26 mei 2014 @ 09:56:
Kan je niet meerdere applicaties deployen e.g. "v2", "v3" etc waarbij het op user niveau bepaalt welke API er gebruikt gaat worden?

Ik zou nooit in je code zelf beslissingen a.d.v versie maken (zoals jouw opzet nu is met 2 verschillende logic.php classes).
De code die verandert is geen applicatie op zich, en een hele API er omheen bouwen zou de complexiteit alleen maar vergroten. Het gaat om een class die een vaste interface(vaste input/output datatypes) implement waarvan de logica(de configuratie van een rule engine) regelmatig wordt veranderd. Dat wil zeggen dat de inhoud van het output object van 2 versies dus verschillend kan zijn bij gelijke input.
ForReal schreef op zondag 25 mei 2014 @ 10:27:
Stel dat je werkproces uit pakweg 50 stappen bestaat en tussen v2 en v3 veranderen maar 4 van die stappen. Ga je dan al je afzonderlijke stappen extenden of niet?
De logica heeft maar invloed op 1 stap, maar de output hiervan mag niet meer veranderen als men terugkijkt/gaat naar deze stap. Het is dus iets simpeler dan jij denkt :)

We zijn er zelf inmiddels redelijk over uit dat de door mij voorgestelde aanpak de juiste is. Voor het aanmaken van een nieuwe versie hebben we een klein cli-script in de maak wat indien gewenst ook automatisch een vorige versie extend, zodat we alleen de methods waarvan de logica verandert hoeven te overschrijven. Zo worden bugfixes in eerdere versies ook automatisch meegenomen, ook al vinden die later in het traject pas plaats. De namespaces zijn gebaseerd op een filesystem/namespace-vriendelijke formatting van een DateTime object, wat de ingangsdatum/tijd van de nieuwe logica definieert. :)

Iemand opmerkingen over deze aanpak?

[ Voor 9% gewijzigd door Gimmeabrake op 27-05-2014 12:19 ]


  • hillbillie
  • Registratie: November 2010
  • Laatst online: 18-07-2022
gerrymeistah schreef op dinsdag 27 mei 2014 @ 12:17:

[...]

De code die verandert is geen applicatie op zich, en een hele API er omheen bouwen zou de complexiteit alleen maar vergroten. Het gaat om een class die een vaste interface(vaste input/output datatypes) implement waarvan de logica(de configuratie van een rule engine) regelmatig wordt veranderd. Dat wil zeggen dat de inhoud van het output object van 2 versies dus verschillend kan zijn bij gelijke input.

[...]
Wikipedia: Strategy pattern dan? :>

  • Gimmeabrake
  • Registratie: December 2008
  • Laatst online: 19-11 21:37
Dat gebruik ik toch al? Als alle implementaties gebruik maken van dezelfde interface(zie TS en enkele andere van mijn posts) is het toch sowieso al het strategy pattern, of ligt dat nou aan mij? :/

Het probleem ligt voornamelijk bij het managen van de verschillende strategies, de strategies hebben namelijk niet echt een naam zoals "Brake" of "BrakeWithABS" zoals in het wiki-voorbeeld. Over een jaar hebben we misschien wel 20 strategies, als we gewoon classes bij blijven pompen met namen als "V3WithMcDonaldsAPISupport" zijn we het overzicht gauw kwijt. Er moet dus versiebeheer komen, maar niet op git/svn-niveau maar op code-niveau. Daar gaat dit topic over. ;)

  • hillbillie
  • Registratie: November 2010
  • Laatst online: 18-07-2022
gerrymeistah schreef op dinsdag 27 mei 2014 @ 14:55:
[...]

Dat gebruik ik toch al? Als alle implementaties gebruik maken van dezelfde interface(zie TS en enkele andere van mijn posts) is het toch sowieso al het strategy pattern, of ligt dat nou aan mij? :/

Het probleem ligt voornamelijk bij het managen van de verschillende strategies, de strategies hebben namelijk niet echt een naam zoals "Brake" of "BrakeWithABS" zoals in het wiki-voorbeeld. Over een jaar hebben we misschien wel 20 strategies, als we gewoon classes bij blijven pompen met namen als "V3WithMcDonaldsAPISupport" zijn we het overzicht gauw kwijt. Er moet dus versiebeheer komen, maar niet op git/svn-niveau maar op code-niveau. Daar gaat dit topic over. ;)
hmmm.. ja :|

https://github.com/google...rc/Google/Api/Ads/AdWords

Google doet dit dus voor de PHP client library... echt te lelijk, maargoed

  • Y0ur1
  • Registratie: Oktober 2000
  • Niet online
Ik zou inderdaad ook een datum en tijd doen als identificatie van een bepaalde versie van de class ipv een nietszeggend versienummer, dit is ten slotte ook het dichtst bij het domein aan (voor zo ver ik dat kan overzien).

Verder kan ik je aanraden om de logic class op te splitsen in herbruikbare/configureerbare classes, dus niet alle logica in 1 class stoppen. Dit heeft als voordeel dat je logica kunt hergebruiken in nieuwere versies zonder inheritance te gebruiken (wat erg beperkend is). Ik zou in Logic.php een configuratie zetten van de logica die je wilt implementeren, de daadwerkelijke logica zou ik in aparte classes zetten zodat je die kunt hergebruiken tussen de verschillende versies, zonder dat je dus hoeft te extenden.

Code in database (eval) en kopieeren van classes = big no no

Het nadeel van extenden is dat je functies krijgt uit de parent die je misschien niet nodig hebt en dat je functies waarvan je de implementatie wilt veranderen gaat overriden, wat erg onoverzichtelijk wordt. Inheritance wordt op ten duur een puinhoop. Maar nogmaals ik weet niet hoe vaak deze wijziging zich voor gaat doen, het hangt helemaal af van het domein en hoe vaak dit gedeelte aangepat wordt. Maar mijn advies is composition ver inheritance.

Mocht je een keer wat gaan doen met een UI, zodat de regels in te stellen zijn voor de gebruikers van het systeem, kun je eens naar http://symfony.com/doc/cu...ssion_language/index.html kijken.

PS (ik zie dat het topic al bij een maand oud is maar hopelijk heeft de TS er nog wat aan. Zat wat oude bookmarks in te halen).

[ Voor 6% gewijzigd door Y0ur1 op 17-06-2014 15:03 ]


  • Styxxy
  • Registratie: Augustus 2009
  • Laatst online: 10:21
Y0ur1 schreef op dinsdag 17 juni 2014 @ 14:56:
Verder kan ik je aanraden om de logic class op te splitsen in herbruikbare/configureerbare classes, dus niet alle logica in 1 class stoppen. Dit heeft als voordeel dat je logica kunt hergebruiken in nieuwere versies zonder inheritance te gebruiken (wat erg beperkend is). Ik zou in Logic.php een configuratie zetten van de logica die je wilt implementeren, de daadwerkelijke logica zou ik in aparte classes zetten zodat je die kunt hergebruiken tussen de verschillende versies, zonder dat je dus hoeft te extenden.
In zekere zin beschrijf je hier dus het Strategy Pattern ;).

  • Y0ur1
  • Registratie: Oktober 2000
  • Niet online
Styxxy schreef op dinsdag 17 juni 2014 @ 17:03:
[...]

In zekere zin beschrijf je hier dus het Strategy Pattern ;).
Ja maar dat is de opzet van de TS in principe ook: client callt code die runtime bepaald welke implementatie wordt gebruikt.
Waar ik alleen voor pleit is dat je niet alle logica in 1 class stopt en die vervolgens gaat extenden in opvolgende versies, dat wordt een nachtmerrie voor de onderhoudbaarheid. Als de implementatie echt maar een paar regels code is kun je dit doen, maar zodra het complexer wordt zul je de echte logica moeten lostrekken in losse classes en vervolgens in je Logic.php een configuratie / code / object graph maken met de configuratie dmv parameters en dependency injection.

Verwijderd

Ik ben weleens tegen hetzelfde soort probleem aangelopen waarbij voor klant X wel een authenticatie nodig was met login/password combinatie en voor klant Y een single signon en klant Z weet geen authenticatie wilde.

Omdat dit al snel een rommel werd qua implementatie en uiteenlopende versies heb ik hiervoor een DIC/ServiceLocator gebruikt. Hierbij hoefde ik alleen in de config in te stellen welke 'auth_adapter' er gebruikt werd, waarna de DIC de juiste implementatie voor klant X, Y of Z teruggeeft.
Pagina: 1