Ferris gjør event-sourcing i UI [Luke 23, 2012]


søndag 23. desember 2012 Julekalender Webutvikling Event sourcing

Thomas Ferris Nicolaisen (@tfnico) har gjort noe spenende og lurt for å gjøre webapplikasjonen sin enklere å teste og å jobbe med – han har brukt event-sourcing! Gjennom illustrasjoner og videoer får du innblikk i hvordan dette fungerer.

2012-12-07 07.59.57

Hvem er du?
Gladlaks fra Mandal, deretter Java-konsulent i Oslo, deretter emigrant i Tyskland.

Hva er jobben din?
Systemutvikler hos Viaboxx GmbH

Hva kan du?
Mye forskjellig, med hovedvekt på Java, litt sånn DevOps-glad.

Hva liker du best med yrket ditt?
Å jobbe i en bransje hvor det er utrolig gøy og samtidig kjempeviktig å holde seg oppdatert.


Event-sourcing i GUI-laget

I min luke tenkte jeg det kunne være artig å vise frem litt ekte “German Engineering”. Dette er første gang vi snakker høyt om våre siste oppfinnelse(r), og jeg kan tenke meg at i enkelte kretser vil det kunne sees på som en revolusjonerende idé, pattern eller arkitektur.

På jobben min så går en god del av arbeidet ut på å lage GUI for trykkskjermer, ala billettautomater og minibanker. Helt spesifikt så lager vi software for pakkepost-automater, men disse har ikke tatt helt av i Norge ennå visstnok. Lesere i Oslo tafser jo en del på Flexus-automater har jeg hørt, så de av dere kan jo prøve å tenke dere et slikt brukseksempel.

 Sketch2832222

Vi pleide å utvikle GUI-biten i Flash/Flex en årrekke, men slet en del med det. Flex var bra på å lage et pent og snappy grensesnitt som passet fint for berøringsskjermer, men var tungvint i drift og utvikling. Mye av brukerens tilstand og forretningslogikk ble liggende i Flash-laget, hvor det var vanskelig med automatiserte tester og debugging.

I denne omgang så tenkte vi å prøve å lage noe med HTML5 og JavaScript, en såkalt single-page application.

hSAbfAyD-fX6h5gFBL7yFJ_7eIm9udpv7T9KR8EjWvGqRkfwr2udSM8mRNd0gX6mpg=s2000

Noe spesielt vi gjorde var å ha en event-basert, nær tilstandsløs browser, hvor eneste oppgave er å vise screens som kommer fra serveren. Jeg skal forklare hvordan dette foregår, men først vil jeg sette det i litt perspektiv:

Typiske web-applikasjoner er avhengige av å bygge opp en sesjon og følge en gitt navigasjons-sti for å havne i en gitt tilstand. Hvis du vil se hva “Mine bestillinger”-siden ser ut som, så må du logge inn og trykke på en lenke.

kdE-oLCeLLCG6AHuDuMmxsFX0vr_hbTtwt_fOLPHB0kzCflAddauvw0W4fyLShU7kg=s2000

Du er avhengig av riktig tilstand på både server og klient for i det hele tatt å kunne vise “Mine bestillinger” siden.

1DEsslgIvpDmavItDXlMJSDKye0NGMT46AyCg2dpR3nK2Cl1wZHPvVZajmKWVLs2VA=s2000

Dette mønsteret har gitt oss (og sikkert mange andre også) mye hodesmerte opp i gjennom årene. For mye tilstand og logikk på klientsiden gir rom for GUI-bugs som er vanskelig å gjenskape. Teste- og debuggingsverktøy på klientsiden er også gjerne under par det vi har til rådighet for koden på server-siden.

Det vi har gjort er å fjerne så godt som all brukertilstand fra klienten. All logikk ligger på serversiden.

wkakPEZqhGObnR41bm-Ye2zEeJjsLr-uoBQDed2FWW-9CUDgqcyrVxwhSHR-s3yjDw=s2000

Hvert eneste knappetrykk resulterer i en runde til serveren, hvor en tilstandsmaskin får beskjed om hva brukeren gjorde, for deretter å spytte tilbake en ny screen (logikken der inne er også veldig interessant, men det er ikke plass til dette teamet i denne bloggposten).

qczwS2hWWUTJAVrqOO99_detMl3xWPYJm1Kgwi-Bs9IybfAQpIVEph3Mw9WfR0FKOw=s2000

Dette høres kanskje laggy ut for en berøringsskjerm som skal være kjapp og reaktiv, men vi får det til på grunn av fire ting:

  1. Server kjører i samme automat (PC) som klient/browser, så det er er en meget kort tur over nettverket.
  2. Kommunikasjonen foregår over WebSockets, så events fra server får umiddelbar virkning.
  3. Noe smart caching og JavaScript for å oppdatere siden kjapt.
  4. Vi har veldig enkle screens med relativt lite innhold.

Hvordan ser egentlig en sånn screen ut? Her er et eksempel:

"decision": {
            "def": {
                "headline": "Please decide", 
                "question": "Is yellow more green than blue?", 
                "type": "decision"
                "events": [
                    {
                        "id": "yes", 
                        "text": "Yes"
                    }, 
                    {
                        "id": "no", 
                        "text": "No"
                    }
                ], 
            }
        }

Ren JSON som representerer absolutt alt av tilstand nødvendig for å vise en skjerm til brukeren. Dette sender vi til browseren, som kjører det gjennom en generator (Mustache) så det blir om til HTML (som vi “swapper” inn og erstatter den gamle DOM’en). Så styler vi det opp med CSS og vips så ser det slik ut:

VyjkcLUZVtMtntceg8EwzaYxSDYKmnuVXtPkKtLDHK-WwCJZTNcbw-qkjTUrnB6rHA=s2000

Det som er utrolig digg å ha denne løse koblingen mellom hva som skal vises, og hvordan det blir generert.

Fordeler med dette da?

Fikse på webdesign uten å måtte trykke masse rundt i applikasjonen.

Du behøver ikke jobbe deg til riktig tilstand for å se på det du vil designe. Du trenger bare et snupp JSON (som den over). Her har jeg fyrt opp vår lille screen-IDE, den kjører på en lokal liten Node-server (se i høy oppløsning):

Man kan velge fra noen forhåndsdefinerte JSON-eksempler, eller man kan copy/paste inn fra loggfiler, og man kan justere innholdet for hånd for å få den tilstanden man vil se.

Dette gir ekstrem kjapp feedback til webdesigneren. Etter å ha endret CSS (eller LESS som vi bruker), Mustache-template eller Javascript trenger man bare å trykke F5/Refresh i browseren for å se screen’en bli rendret på nytt med det siste innholdet. Man kan også simulere enkle server-side events med litt JavaScript.

Man kan teste GUI akkurat i hvilken grad man vil

Tradisjonelt er det mange ulemper med å teste applikasjonen gjennom GUI, men mange gjør det fordi de har ingen annen måte å teste GUI på.

Vi kan derimot teste at..

  • riktig screen object blir produsert av tilstandsmaskinen (ren unit-test)
  • forventet JSON blir spyttet ut av gitt screen  (ren unit-test)
  • riktig HTML blir produsert fra gitt JSON (men da må man ha en headless browser som kan kjøre Javascript, ala PhantomJS)
  • HTML fra en screen ser OK ut (man kan ta screenshots med PhantomJS)
  • HTML fra en screen ser akkurat riktig ut (men da må man bruke produksjonsbrowseren (Chrome) på riktig operativsystem og ta screenshots med Selenium/WebDriver)

Alle disse typer tester gjør vi i forskjellige grader: de øverste veldig mye, og i mindre grad dem lenger ned på listen fordi de er dyrere ressursmessig, og brekker lettere.

Det som hjelper en del er at vi kan skille mellom (a) kommer det riktige innholdet ut (screens) fra serveren, og (b) blir en gitt screen seende riktig ut i browseren. Denne delingen gjør at vi kan holde testene våre utrolig kjappe: Rene unit-tester tester tilstandsmaskinen og screensene som spyttes ut, mens forskjellige browser-tester lager screenshots for screens.

Man kan også teste hele stacken sammen, eller i faser. Vi gjør sistnevnte når vi genererer dokumentasjon og screenshots basert på story-tester (men vi mocker ut hardware og tjenester):

  • Story-tester genererer dokumentasjon med JSON der screenshots skal legges inn
  • Dokumentasjonen kjøres gjennom et script, hvor JSON blir rendret til ekte screenshots.

Det hele går utrolig kjapt, og man trenger fortsatt ikke fyre opp noen ekte applikasjonsserver - det holder med Node simulatoren.

Se nøyaktig hva brukeren så på skjermen på et gitt tidspunkt

La oss se for oss dette scenarioet: Noen leverer en pakke i en automat, sender hente-kode over SMS, mottaker kommer og henter pakke:

Siden vi logger det meste som skjer på en automat, inkludert hvilke screens som sendes, så burde det jo være en smal sak å lese gjennom en hel loggfil, og så generere et hendelsesforløp over hva som har skjedd på en automat den siste tiden, og kunne spille av gamle screens på nytt.

Så vips, etter et par dager med utvikling:

Ikke bare kan vi spole frem og tilbake i hva brukeren opplevde i villkårlig tempo, vi kan også kopiere screens ut herfra for så å jobbe med dem i simulatoren.

Det skulle ikke mye kode for å få dette til: en loggparser og en side JavaScript. Den flotte timeline-widgeten er laget med dette Timeline-biblioteket.

Ennå kan vi ikke se hvor brukeren trykker på skjermen, men vi jobber i disse dager med å få til det også.

Nå forstår du kanskje hvorfor tittelen på denne posten er Event Sourcing.

Vanligvis så bruker man Event Sourcing for events i domenelaget, for ting som pakkesporing og sånnt, men det er sannelig utrolig mektig å bruke det i  GUI-laget også.

Konklusjon

Det er ikke sikkert at denne arkitekturen passer for alle applikasjoner, men vi har lekt med tanken av å bruke det videre i ekte web-applikasjoner, spesielt hvis det er interaksjon av typen veiviser/wizard eller lignende, slik som vi har på automatene våre.

Videre er det utrolig gøy å jobbe med denne teknologi-stacken. Det går knapt en sprint uten at vi jubler over hva vi har fått til, og hvor relativt enkelt det var å få til. En viktig del av det er nok at vi har skrudd sammen mesteparten selv, og ikke bundet oss fast i noe svært rammeverk.

Håper dette gir dere noe å tygge på i ferien. God jul og godt nyttår!


comments powered by Disqus