
03 aug Technische Stage S3-Tool
Afgelopen schooljaar hebben de twee stagiairestudenten Dana Tabatabaie Irani en Maxim Janssens een webapplicatie gebouwd om ons digitaal te ondersteunen tijdens S3 governance meetings. Tijdens de COVID lockdowns afgelopen jaren leek het ons nuttig om verschillende S3 patronen te digitaliseren zodat we ze kunnen blijven gebruiken tijdens online vergaderingen. De kans is groot dat we in post-COVID tijden meer en meer hybride vergaderingen gaan houden waar sommige collega’s fysiek aanwezig zijn en anderen virtueel.
De S3-Tool webapplicatie is eenvoudig in opzet: De vaste flow die we toepassen moet visueel heel duidelijk worden gemaakt en deelnemers moeten zowel op computer als smartphone kunnen deelnemen. S3-patronen zoals Check-in, Check-out, Rondjes, Consent Besluitvorming en Rolselectie moeten correct en interactief beschikbaar zijn.
Hoewel de functionele opzet relatief eenvoudig is, nemen Maxim en Dana ons graag in mee in de wereld van technolgie die achterliggend toch niet zo eenvoudig blijkt te zijn.
Front-end
Als front-end framework hebben we gekozen voor Vue. Vue is een framework dat op het eerste gezicht veel eigenschappen gemeen heeft met zowel Angular als React, maar met het verschil dat de leercurve bij Vue een pak lager ligt. Het ontwerp van Vue zorgt ervoor dat je geleidelijk aan verschillende functionaliteiten van het framework kan oppikken, zonder dat je uitgebreide voorkennis nodig hebt. Dit komt doordat Vue templates eigenlijk gewoon HTML zijn, maar dan met hier en daar wat extra syntax in de vorm van HTML-attributen. Standaard HTML kan dus steeds zonder enige wijzigingen in een Vue template worden geplaatst.
Bovenop Vue gebruikten we ook nog Nuxt. Nuxt is een framework dat alle configuratie van een Vue-applicatie afhandelt. Via een commandline-tool kan je alles naar wens instellen, en Nuxt zorgt er dan op zijn beurt voor dat alles werkt zoals het hoort. Daarnaast zorgt het er ook voor dat je gebruik kan maken van routing gebaseerd op bestandsnamen, waardoor je de router zelf niet meer moet instellen, maar gewoon je templatebestanden in een bepaalde structuur kan zetten, net zoals dat bij een statische website het geval zou zijn.
Nuxt voorziet daarna ook de optie om zelf middleware en plug-ins voor de applicatie te schrijven. Deze terminologie is een beetje verwarrend, maar het principe is vrij simpel. Middleware zal elke keer dat er een routewijziging plaatsvindt op de applicatie worden gedraaid, en een plug-in wordt gedraaid bij het openen van de applicatie. Middleware leent zich dus perfect om navigation guards te maken, terwijl je met plug-ins bepaalde functies beschikbaar kan maken voor je applicatie, of event listeners kan instellen.
In onze applicatie gebruikten we middleware voor twee verschillende zaken. Als eerste gebruikten we het om bepaalde routes af te schermen, zodat enkel ingelogde gebruikers hier toegang tot hadden. Daarnaast hadden we ook routes die toegankelijk waren via de breadcrumbs van de applicatie, maar waar eigenlijk niets te zien was. Om ervoor te zorgen dat de gebruiker direct naar de juiste pagina zou doorverwezen worden, hebben we ook daar gebruik gemaakt van middleware.
Plug-ins gebruikten we voor nog veel meer zaken, we hebben maar liefst 8 plug-ins geschreven voor onze applicatie. Hier zal ik me beperken tot de belangrijkste redenen waarom we van plug-ins gebruik hebben gemaakt.
Als eerste hebben we een plug-in gemaakt die ervoor zorgt dat alle falende Axios requests werden opgevangen, en dan keek of de reden van falen een 401 (Unauthorized) was. In dat geval werd ervan uitgegaan dat de idtoken (of accesstoken) van de gebruiker verlopen was. De plug-in haalt daarna aan de hand van de refresh token een nieuwe idtoken op. Daarna past de plug-in de originele request aan en geeft daarbij de nieuwe idtoken mee, en stuurt dan het antwoord van het request terug naar waar dat het request oorspronkelijk werd opgeroepen. In het geval dat het request naar Microsoft Graph werd gedaan, werd er niet een nieuwe idtoken meegegeven, maar wel een nieuwe accesstoken, aangezien Microsoft Graph hiervan gebruik maakt.
Daarnaast hebben we nog verschillende plug-ins geschreven die als enige doel hebben een globale functie te registreren die we in alle componenten van de applicatie kunnen gebruiken.
Tot slot hebben we ook nog functies geschreven die in staat zijn om at runtime configuratiewijzigingen van libraries door te voeren. Een concreet voorbeeld hiervan; we maakten gebruik van het momentjs library om datums om te vormen tot leesbare tekst. Als de gebruiker de taal van de applicatie verandert, zal een plug-in naar dit event luisteren en als reactie daarop de taal van momentjs aanpassen.
Back-end
Voor de back-end werd er gekozen voor .NET. Wat .NET zo krachtig maakt, zijn de talloze integraties die Microsoft al heeft voorzien en makkelijk kunnen worden toegevoegd aan eender welk .NET-project. Daarnaast is .NET sinds versie 5.0 ook volledig cross-platform, en kan de back-end van Scim dus op Windows, Linux, macOS en Cloud draaien.
De belangrijkste feature van .NET was voor onze applicatie zonder twijfel SignalR. SignalR is een simpele abstractie van verschillende realtime webtechnologieën en zorgde er dus voor dat we bij het houden van een meeting dezelfde informatie op het scherm van elke deelnemer in realtime konden tonen. SignalR is volledig geïntegreerd in ASP.NET en functionaliteiten zoals authenticatie werkten daar dus ook gewoon. Het enige dat wij hadden moeten voorzien was een centrale hub waar alle endpoints van een meeting beschikbaar werden gesteld.
Naast het creëren van de verschillende endpoints was de volgende uitdaging om het verloop van een meeting performant te laten draaien. Het uitvoeren van veranderingen op een meeting zorgt voor heel wat overhead. Zo zal een meeting entiteit eerst opgehaald moeten worden uit de SQL Database. Elke meeting bevat een veld waarin de vooruitgang van de meeting zich in geserialiseerde vorm bevindt. Deze zal eerst via de ingebouwde functionaliteit van Entity Framework Core gedeserialiseerd moeten worden. Hierna kunnen de veranderingen op het verkregen object toegepast worden. Daarna zullen de voorgaande stappen in omgekeerde stappen uitgevoerd moeten worden om het object terug in de database op te slaan.
Om het eerste deel van dit algoritme te omzeilen, wordt er een singleton gecreëerd waar alle geserialiseerde meetings zich in een dictionary bevinden. Bij het starten van een meeting wordt dit uit de database opgehaald en in de dictionary gestoken. Voortaan worden updates op deze in-memory meeting uitgevoerd en nadien geschreven naar de database waardoor er een rond-trip naar de database wordt verminderd.
Boven op deze optimalisatie werd ervoor zowel SignalR als onze API-endpoints gebruik gemaakt van velen DTO’s die enkel de nodige informatie bevatten zodat alles nog vloeiender verloopt. Om de DTO’s om te zetten naar de domeinobjecten en omgekeerd, werd er gebruik gemaakt van AutoMapper. De omzettingen verliepen feilloos eens er (custom) profielen voor werden gemaakt.
Voor de basis API-endpoints gebruikten wij ASP.NET in combinatie met MediatR wat voor een naadloze ervaring zorgde. Door gebruik te maken van de validatie bag konden wij een of meerdere custom error codes meegeven aan de antwoorden wat de development proces vereenvoudigde.
Om de database up-to-date te houden met de steeds veranderende domeinentiteiten, maakten wij gebruik van Fluent Migrator. Door telkens nieuwe migrations te schrijven en/of de seeder aan te passen, verkregen wij niet enkel een correcte database, maar ook een historiek van alle veranderingen die op de tabellen werden uitgevoerd.
Het laatste, en naar mijn mening toe een van de belangrijkste punten van de back-end, is onze logger. Wij hebben gebruik gemaakt van NReco om alle endpoint-activiteiten te loggen. Dit zorgde niet enkel voor tijdens de development voor een makkelijkere debug ervaring, maar zorgt ook dat fouten op run-time uitgeschreven worden naar bestanden gefilterd per dag.