wk voetbalpool webapp: dag 8

 
13 april 2010

Het gebruik van naamgeving begint af en toe grappige vormen aan te nemen. Om een pool te representeren hebben we nu een GroupActor die wordt aangemaakt door een GroupFactoryActor, en heeft een corresponderende view om data over de groep op te vragen, namelijk de GroupViewActor, die op zijn beurt weer wordt aangemaakt door een GroupViewFactoryActor. En om alle wedstrijdgegevens te kunnen bekijken is er nog een GamesOverviewViewActor. Misschien kunnen we eens nadenken over het hernoemen van sommige van deze dingen…?

De functionaliteit hebben we nu inmiddels zover op de rit (het vooraf definieren van eventtypes bleek erg te helpen) dat we eens wat nauwkeuriger moeten gaan kijken naar wat we eigenlijk allemaal al hebben en wat we nog missen. Daarnaast wordt het tijd om de REST API eens wat te documenteren, zodat de front-end ook makkelijker erop aangesloten kan worden. Hiervoor gaan we dan de interne Wiki gebruiken. Bijna alle panelen voor het front-end zijn nu gemockt (en we weten inmiddels welke panelen er allemaal moeten zijn), en er is al een begin gemaakt met het aanspreken van de bestaande back-end API. Hier gaat de komende paar dagen meer de nadruk op liggen. Over het algemeen vordert het front-end werk ook gestaag.

Rikkert is bezig geweest met wat stoeien met CouchDB. Het blijkt nog niet helemaal een sine-cure te zijn om die op Windows draaiend te krijgen. Het idee ziet er goed en makkelijk aanspreekbaar uit; er is een administratieve UI aanwezig, en de DB zelf kan met REST-requests met JSON-data aangesproken worden, en hij luistert in elk geval ook naar de normale HTTP verbs (GET, POST, PUT, DELETE, …), waarna er een JSON response terugkomt. Met Lift zouden we die makkelijk moeten kunnen parsen. De testresultaten op Windows zijn nog bedroevender dan de documentatie doet vermoeden; bijna alle meegeleverde tests falen. Gelukkig ontwikkelen we de back-end ook al op een CentOS (Linux) virtuele machine, en Jan-Willem heeft op zijn VM CouchDB ook al geinstalleerd. Niet veel later op de dag meldt Rikkert dat deze versie inderdaad een stuk beter werkt: op een enkele na slagen alle tests.

Het testwerk voor de back-end is tot nog toe voornamelijk de ‘happy flow’ geweest. We moeten nu ook gaan nadenken over wat er gebeurt met verschillende vormen van dingen die fout gaan. Wat te doen bij bijvoorbeeld het opvragen van een top-10 van een niet-bestaande groep? Wat als de binnenkomende JSON-data ongeldig is? Wat als er iets misgaat binnen het domein? Dit gaan we vandaag verder uitwerken en in de API-beschrijving opnemen. Geven we een JSON-melding terug? Geven we een lege response? Geven we een HTTP error-code 404 of andere?

Het wordt ook wel weer eens tijd om terug te koppelen met onze klant Simon, om eens een kleine demonstratie te geven. Agenda-technisch wordt het ofwel morgenmiddag ofwel pas over een week. Bij het overleg om de front- en back-end aan elkaar te koppelen blijkt dat we voor een ‘mooie’ demonstratie toch nog net wat teveel puzzelstukjes missen om morgenmiddag haalbaar te maken (nog afgezien van minder belangrijke functionaliteit zoals de slow-chat of nieuwsberichten). We willen als demonstratie het volgende scenario laten zien: (incl. login/logout-functionaliteit)

  • Admin voert een te spelen wedstrijd in
  • Een gebruiker registreert zich en logt in
  • Hij voert een wedstrijdvoorspelling in
  • Admin voert een wedstrijduitslag in voor die wedstrijd
  • De gebruiker krijgt een overzicht van zijn (geupdate) puntentotaal na die wedstrijd

De login-functionaliteit wordt nog een interessante kwestie, waar ik me op ga richten: bij een Login-request wil je een synchrone afhandeling hebben, omdat je meteen in het response een session token (session cookie) oid wil meegeven. Maar het ingelogd-zijn wordt bijgehouden in het domein, wat alleen een asynchrone verwerking kent. Het login-componentje zal dus expliciet moeten wachten totdat er een loginSuccess-bericht of een loginFail-bericht uit het domein terugkomt. Het is dus een vreemd hybride componentje dat niet echt een view is omdat hij berichten naar het domein stuurt, maar ook niet echt een Actor omdat het componentje niet constant live als actor in het domein hangt. Voor elke login-request moet er een nieuw object aangemaakt worden, omdat je niet alle login-resultaten door en enkel object wil laten afhandelen: de synchrone aard ervan zou betekenen dat er maar één iemand tegelijkertijd met inloggen bezig kan zijn. Toch is het noodzakelijk om hier de Actor trait te extenden omdat we toch op de EventBus aangesloten moeten kunnen worden zodat we naar LoginSuccess- en LoginFail-events moeten kunnen luisteren die uit het domein komen.

Ik dacht hierbij handig te zijn door een ‘synchrone’ login()-methode te maken waarin ik een ‘tijdelijke’ loop { react { ... } }-lus opstart die na het LoginStart-event wacht op een LoginSuccess of een LoginFail, en daarna nog wat post-processing doet om de data naar de gebruiker te kunnen doorsturen. Maar helaas: dat vindt Scala niet leuk. Ook de operator ?! kan ik niet gebruiken om ‘synchroon’ op antwoord op mijn LoginStart-event te wachten: doordat we die naar de EventBus sturen, en alle andere actoren ook via de EventBus communiceren, is het waarschijnlijk dat er eerst een niet-relevant ander event langskomt. Uiteindelijk komt het erop neer dat ik in de login()-methode deze actor start en hem bij de EventBus registreer, en ik o.a. een var loginSucces: Option[Boolean] = None toevoeg aan de klasse. Als de event handling loop de juiste events detecteert zal die deze variabele een andere waarde geven. In mijn login()-methode staat nu dus een busy wait loop (while(loginSucces==None) { }) om het resultaat af te wachten. Niet erg netjes, maar het werkt… (Al zou je toch verwachten dat er een betere manier is…)

En daarmee wordt het half zes, en alles is wel.


Werken met ?
Kijk dan bij onze mogelijkheden voor starters en/of ervaren IT'ers.


Categorieën: Development


Reactie

  • Hoi! Weer leuk om te lezen, deze laatste posts.

    Wat betreft de noodzaak van een synchrone login: volgens mij kan dat ook anders. Je zou de login-client een (niet te raden) sessie-id mee kunnen laten sturen, waarmee hij later requests kan sturen. Dat is dan eigenlijk je token. Eventueel kan de client nog eerst een aanvraag doen of de login gelukt is, maar daar zou hij eigenlijk een event voor binnen moeten krijgen. Eventueel zou je met een PKI ervoor kunnen zorgen dat clients elkaars (ingelogde) sessies niet kapen door het token te encrypten en er iets client-specifieks en gebruik-per-geval specifieks (nonce) in te vereisen. (zoals wel meer asynchrone inlog systemen werken)

    Wat betreft de naamgeving van event-phases (van de vorige dag): dat lijkt me in ieder geval niet een goede omschrijving, omdat een event gebeurd is, ik verwacht geen fases daarin. Het verzoek om iets te doen en of het gebeurd is of niet zijn natuurlijk wel gerelateerd, maar dat zou je prima tot uitdrukking kunnen brengen door een request-id. Misschien zijn er wel meerdere verzoeken die tot hetzelfde resultaat leiden? Ook het argument dat je zo eenvoudig de failed-events er tussenuit kunt halen, klopt niet helemaal, denk ik. Zoiets is net zo eenvoudig, zo niet eenvoudiger te realiseren door hier een failed eventtype voor te reserveren, die je als ‘trait’ meegeeft aan elk van die events (of als type in een eigen list, of als interface). Dat is dan volledig losgekoppeld van een fasering en er is eenvoudig een eigen hiërarchie in aan te brengen als je dat wilt.

    Hebben jullie CouchDB nog vergeleken met MongoDb? Ik heb begrepen dat Mongo wat beter werkt op windows en betere query functionaliteit biedt. Zie ook http://www.mongodb.org/display/DOCS/Comparing+Mongo+DB+and+Couch+DB en http://howard.vanrooijen.co.uk/blog/2010/04/04/a-dotnet-developer-guide-to-mongodb-and-norm/ voor wat vergelijkingsmateriaal.

    De naamgeving van de actors blijft (ook al zijn het actors ipv classes/interfaces) een lastige: noem je ze naar het patroon of noem je ze naar een vergelijkbaar iets in de ‘buitenwereld’? Als de overlap heel duidelijk is, dan zou ik hem naar datgene in de buitenwereld noemen, maar die is niet altijd aanwezig. Het is vaak wel een goede vraag om te stellen waarom die daar niet aanwezig is (implementeer je bv misschien écht nieuwe functionaliteit die niet voorheen door mensen uitgevoerd werd?)

    Geplaatst op 14 april 2010 om 8:41 Permalink