Importeren van data uit externe bronnen. We doen het altijd fout. Ik ook.

Ingediend door Dirk Hornstra op 01-feb-2023 21:48

Als je een website/webapplicatie maakt, maak je vaak gebruik van externe data. Zo heb ik in het verleden een eigen site opgezet waarbij ik de data van mijn Fitbit opvraag uit de API van Fitbit. En bij TRES hebben we natuurlijk ook websites waarbij data uit externe systemen komt.

Wat is je eerst reflex? Je krijgt data, maar die komt niet overeen met het datamodel wat je in je eigen website/webapplicatie hebt. Dus je gaat meteen de boel "omkatten" naar jouw eigen datastructuur. Heb ik ook gedaan, want ik heb mijn "stappen" en "afstand" data, precies wat ik wil hebben. Wat kunnen mij die "calorieën" schelen? Helemaal niets, dus die laat uit mijn datamodel weg.

Je snapt het al, deze reflex is fout, je doet het niet goed. En ja, ik deed het dus ook niet goed.

Waarom is dit niet goed dan? Daar kan ik een aantal redenen voor geven.

1. Clean code: een functie moet "één ding doen", single responsibility principle.
Als programmeurs gaan we hier vaak mee in de fout, we maken een functie "import" en daar krijg je een lap code die wat je binnen krijgt omzet naar wat je binnen je eigen applicatie kunt gebruiken. Maar je moet dit "scheiden". Want nu doet je code niet één ding (de data ophalen uit een externe bron: importeren) maar ook de data omzetten naar jouw datastructuur (transformeren). Mede door dit te scheiden maak je ook jouw code beter leesbaar én beter testbaar! Want reken er maar op dat je op een later tijdstip vragen krijgt of je moet iets aanpassen. En dat is waarschijnlijk na ruim een jaar, je hebt al 12 maanden niet meer in die code gewerkt en er wordt van je verwacht dat je het binnen een uurtje fixt.

2. Er gaat wat fout, ligt het "bij hun" of ligt het "bij ons"?
Niets vervelender dan een klant die belt en zegt: sinds de vorige import staat er geen enkel product meer op onze website. Dan is het fijn om in één oogopslag te kunnen zien: komt het door "hun", de partij die jou de data aanlevert of ligt het "aan jou" en zul je in de code moeten duiken om te debuggen en te fixen? Man-uren zijn duur. Het is zonde dat jij een uur in de code zit te wroeten en daarna tot de conclusie komt dat er een lege feed aangeleverd wordt. Of dat er data aangeleverd is die niet voldoet aan de afspraken (business rules) en de aanleverende partij aan de bak mag om het te fixen.

3. Verlies van data.
Het is voorgekomen. Er staat veel data in een feed, maar op onze website hebben we maar 40% van die data nodig. Dus die data nemen we niet over in ons datamodel. Na een half jaar zegt de klant echter: "We willen deze 3 velden er ook bij hebben.". Je kunt de transformatie nu aanpassen, maar dat werkt dan alleen bij nieuwe data. "Oude" data wordt niet meer bijgewerkt en mist dus die 3 velden, terwijl het in het verleden wel aangeleverd is (en wij het weggegooid hebben). Hadden we nog de originele data, dan hadden we ook die "oude data" kunnen bijwerken. Dat geldt hier ook voor mij, mocht ik wat fanatieker met calorieën aan de slag gaan, wat krijg ik binnen, dan zou ik ook kunnen zien dat door zoveel stappen extra te zetten ik weer extra calorieën kan verbranden.

4. Wijziging van je eigen datamodel.
Naast het vorige punt (velden toevoegen die er al zijn), kan het ook zijn dat jouw eigen datamodel wijzigt. Een veld wat optioneel was, wordt nu ineens verplicht. Maar omdat je import en transformatie in één functie hebt moet je ook daar nu die procedure helemaal aanpassen. Mogelijk is een aangeleverd veld bij de leverancier wél optioneel. Dus je zult deze met een dummy waarde willen vullen. Of misschien die aangeleverde data niet in jouw data opnemen. Als je een losse "transformatie" module hebt wat gekoppeld zit aan jouw datamodel is dat beter door te voeren dan een stuk rechtuit-rechtaan code binnen één importfunctie.

5. Wijziging van het datamodel van de leverancier.
De leverancier leverde de data aan in XML formaat, maar besluit nu via JSON de data over de lijn te gooien. Het mappen van velden naar de data die binnen komt moet nu in jouw code heel anders uitgevoerd worden. Als je alles-in-één-code hebt zul je in de XML situatie misschien door een boom heen lopen, door de nodes en zo de data opvragen/opslaan. Met de System.Text.Json-namespace kun je een stuk JSON veel makkelijker naar een object omzetten en daar de eigenschappen van instellen.

6. Opslaan van aangeleverde data
Je moet de originele data opslaan. Afhankelijk van wat voor data je krijgt kun je denken aan een tekstbestand / tekstbestanden, mogelijk met compressie kleiner gemaakt. Want daarmee kun je een aantal problemen die kunnen optreden oplossen of in ieder geval iets minder ingrijpend laten zijn:

6a. Weergave van aangeleverde data.
Die data moet ook inzichtelijk zijn in het beheer-deel van de site. Zo kunnen developers, 1e lijns support en als je het wilt ook de klant zelf controleren wat er aangeleverd is/wordt. Hiermee hoef je niet één of ander testproject uit te voeren om te zien wat er nú binnen komt, want de huidige call kan soms andere data opleveren dan die van een uur geleden.

6b. Restore van aangeleverde data.
De huidige data die aangeleverd is, zorgt dat er geen enkel product meer op de site staat. De leverancier heeft wel een uur de tijd nodig om de feed aan hun kant te herstellen. Maar de data die een uur geleden aangeleverd is, die mag nog wel gebruikt worden. Dan maar wat bestellingen in back-order plaatsen. Als je data-set 1-op-1 afhankelijk is van de live-data van de feed kun je dit niet doen (of je moet zaken patchen). Als je in je code / of in het beheer-deel van de site kunt aangeven dat je de data van import x wilt gebruiken, dan kun je zorgen dat de pijn van de klant beperkt wordt.

6c. Weergave van jouw data.
Uiteindelijk gaat het om de data die jij gebruikt om op de website te tonen. Dus het is goed dat je kunt laten zien welke data in jouw dataset staat.

Nog mooier is dat je het ook kunt tonen gerelateerd aan de aangeleverde data. Zodat je kunt zien hoe bepaalde eigenschappen overgezet worden van import via transformatie naar output.
En ook dat je kunt laten zien welke items niet getoond worden. En waarom deze niet in jouw dataset staan (dus welke business-rules daarvoor gezorgd hebben).

Ook hier, eigen ervaring. Klant komt met een lijst met business-rules. Indien niet gekoppeld aan een merk, niet tonen. Indien geen prijs, niet tonen. Niet gekoppeld aan een leverancier, dan niet tonen. Tijdens de import waren die zaken niet gekoppeld, dus product wordt niet getoond. Op een later tijdstip wordt de leverancier nog aangemaakt en het merk, maar dan wordt niet "automatisch" opnieuw gevalideerd of het reeds geïmporteerde product nu wel getoond mag/moet worden.

Laat duidelijk zijn, dit hoeft niet een fout van jou te zijn, maar ook geen fout van de aanleverende partij. Het kan bij het bouwen van de applicatie "vergeten" zijn, het was een scenario waarbij van tevoren gedacht werd dat het nooit voor zou komen. Of het is wel een fout aan de kant van de leverancier. Of het is wel een fout van "ons". Maar dit is niet bedoeld om iemand ermee om de oren te slaan, het is bedoeld om inzicht te geven in de oorzaak, zodat daarna bepaald kan worden hoe we dit gaan oplossen. Niets is vervelender dan 3 uren onderzoek, waaruit blijkt dat door iets in 5 minuten aan te passen het probleem oplost is.

Het vervolg;

Mijn goede voornemen voor 2023 was om elke week een Techblog-artikel te delen, dat is tot nu toe gelukt. Ook zou ik elke maand een extra artikel delen, dat is meteen de eerste maand van het jaar al niet gelukt. Te druk. Zoals je in het vorige artikel al kunt lezen flink wat tijd aan Docker en PHP8 besteed, iemand die met zijn hosting overgegaan is en geen mail meer kon ontvangen en ik geholpen heb, wat teksten/adressen aanpassen bij een website waar ik vrijwilliger ben, IT-gerelateerde podcasts beluisteren, online een presentatie volgen over authenticatie binnen Azure. En natuurlijk als het een beetje fatsoenlijk weer is een eind(je) wandelen, ik wil dit jaar weer meer stappen lopen dan vorig jaar. Niet erg om die dingen te doen, maar dat gaat dan wel ten koste van andere zaken.

Wel heb ik voor ogen wát voor artikel het moet zijn, ik wil namelijk iedere maand een "Design Pattern" bespreken. Vorig jaar (eindelijk) het boek gelezen, maar goed, sommige principes blijven op papier wat abstract en je vraagt je dan af hoe je dit in de praktijk gaat toepassen. Voor het bovenstaande verhaal zit ik te denken aan het Adapter Pattern, je hebt de aangeleverde data, je weet hoe de data aan jouw kant op wilt slaan en op één of andere manier moet dat omgezet/gerouteerd worden en dit pattern lijkt me daar een goede oplossing voor.

En dat is dan ook meteen een mooie kans om te kijken of wat ik hier zo boven "even makkelijk neergepend heb" in de praktijk ook zo makkelijk uit te werken is. Practise what you preach!