De laatste paar jaren is Google Firebase ((M)BaaS) succesvol geweest met wat we tegenwoordig een Realtime Database noemen. Dit is een tool waardoor ontwikkelaars een back-end database makkelijker kunnen opstellen. Vele ontwikkelaars kiezen voor een realtime database, door de lage barrières, snelle queries en lage kosten. Dit systeem kan voor problemen zorgen waardoor gezocht moet worden naar alternatieven voor Google Firebase.
Google Firebase probeert een groter publiek te bereiken met hun lancering van een aparte back-end Cloud Firestore. Ondanks dat Cloud Firestore vele functies ondersteunt waar Realtime Database ontwikkelaars jaren op hebben gewacht is Cloud Firestore niet gewoon een tweede versie. Het heeft een compleet nieuwe architectuur en richt zich op andere prioriteiten, zoals data integriteit en complexe queries.
Bij Glamorous Goat hebben we de kans gehad om met beide systemen te werken. Deze ervaring met beide services willen we graag met jullie delen. We zullen in detail de verschillende sterke en zwakke punten behandelen. Daarnaast zullen we kijken naar de updates die Google Firebase heeft gelanceerd voor beide systemen sinds de aankondiging van Cloud Firestore. Met dit artikel proberen we inzicht te creëren of je Cloud Firestore of Realtime Database moet gebruiken voor jouw toekomstige projecten.
Voordat we dieper ingaan op de verschillen zullen we eerst naar de basis kijken. Dit doen we door naar de belangrijkste functies van beide systemen te bekijken, zodat beide systemen beter vergeleken kunnen worden.
Realtime Database is een cloud-hosted NoSQL database met SDK support voor Android, web en iOS. Het integreert makkelijk met andere tools van Google Firebase met betrekking tot het opslaan van files, autorisatie, analytics, etc. Deze tool slaat data op in JSON files. Realtime Database beheert zelf updates wanneer een toestel offline is, waardoor veranderingen automatisch worden gesynchroniseerd wanneer je verbonden bent met een netwerk.
Ontwikkelaars vinden het geweldig hoe snel een Realtime Database back-end kan worden opgesteld. Je hoeft je geen zorgen te maken om onderdelen zoals hardware, implementaties en schaalbaarheid. Binnen een paar minuten kan je een redelijke basis back-end server klaar hebben, waardoor je meer kunt richten op de unieke en leuke aspecten van jouw app.
Met Realtime Database moet je de meeste systeem applicatie code op de client zelf schrijven. Deze aanpak heeft zijn voor en nadelen, afhankelijk hoe je het bekijkt. De meeste logica moet zelf worden aangepast in de applicatie van jouw client. Dit betekent dat je de code moet dupliceren voor web, Android en iOS tenzij je een Firebase Cloud Function bouwt om deze queries op te pakken. Als je van de laatste optie gebruik maakt, verlies je wel een hoop ingebouwde Realtime SDK functies.
De client logica vereist dat je jouw eigen data validatie schrijft. Met Realtime Database zijn er geen concepten van data types beschikbaar. Je kunt waardes als een reeks nummers opslaan, gebruik maken van vaste objecten met een index als sleutels. De reeks zijn als het ware objecten met een index die gebruikt worden als sleutels. Het is aan jezelf om deze data types te beheren.
Queries reageren bijna real time door gebruik te maken van Realtime Database en dit werkt fantastisch. Het is hierbij wel belangrijk dat je de datastructuur gepland hebt op een manier dat alle queries die je maakt worden ondersteund in jouw apps. Dit is niet makkelijk. Ondanks dat het snel is, ondersteund Realtime Database alleen queries van een veld. Vele ontwikkelaars zijn echter teleurgesteld dat queries van data niet worden ondersteund door functies die ontwikkelaars gebruiken met een meer traditionele database.
Realtime Database suggereert dat een oplossing hiervoor is om data te denormaliseren en platter te maken. Dit is alleen makkelijker gezegd dan gedaan. Je moet namelijk alle locaties van een data element opzoeken die zijn gekopieerd voor het geval dat deze data moet worden geupdate. Het is ook erg lastig wanneer nieuwe functies op lange termijn worden toegevoegd zonder dat je jouw bestaande data schema drastisch moet veranderen.
We hebben kunnen zien dat Realtime Database niet de beste keuze is voor iedereen. Dus wat heeft Google Firebase veranderd met de ontwikkeling van Cloud Firestore?
Cloud Firestore is de nieuwere database dat meer lijkt op het traditionele schema. De NoSQL database is namelijk georganiseerd in collecties. Deze collecties bevatten documenten die op hun beurt nog meer (sub) collecties en databestanden hebben. Een document is een enkele JSON object structuur dat opgeslagen sub collecties bevat van meer documenten of belangrijke data. Collecties en documenten kunnen worden gezien als tabellen en rijen in een relationele database, aangezien het op deze manier het meeste wordt gebruikt.
De velden binnen een document hebben nu verschillende types waaronder: nummers, objecten, boolean, strings, reeksen, 0 waardes, datum timestamps, referenties naar andere documenten en geopoints. Dit zorgt voor een enorme verbetering voor ontwikkelaars om bugs op te pakken door types die niet goed bij elkaar passen en data integriteit te beheren. Referenties kunnen helpen om data denormalisatie te vermijden en verminderen het aantal locaties dat je moet beheren om dubbele data te voorkomen. Hoewel je queries met sub collecties of binnen referenties kunt uitvoeren is het wel mogelijk om deze referenties te gebruiken om direct data gerelateerd aan een lokaal document op te vragen.
Cloud Firestore biedt een meer robuust querie support dan Realtime Database. Hier zullen we later dieper op ingaan.
Aangezien Realtime Database en Cloud Firestore beide producten zijn van Google Firebase is het niet verrassend dat ze veel met elkaar gemeen hebben. Beide producten zijn makkelijk te integreren in een project met weinig setup stappen. Daarnaast werken ze goed samen met andere services die worden aangeboden door Google Firebase en met zichzelf.
Voor een Google Firebase project kun je beide een Realtime Database en Cloud Firestore opzetten, zodat jouw klant kan beslissen welke beter is. Andere services zijn: autenticatie, opslaan van bestanden, crash reports, cloud functies, Firebase cloud messaging en analytics. Al deze tools kunnen succesvol worden gebruikt met een project. De meeste functies kunnen slechts met Realtime Database of Cloud Firestore worden gebruikt.
Beide platformen kunnen hun data beschikbaar stellen aan admin gebruikers via de Firebase Console. Deze console volgt dezelfde paradigma in beide databases. Hiermee kun je top level nodes/collecties browsen en dieper ingaan op de data totdat je gevonden hebt waar je naar op zoek was. Beide platformen bieden geen mogelijkheid aan om dieper te zoeken. Het is bruikbaar in situaties waarbij je de sleutels van objecten die je wilt zien weet en dat je hier gelijk naartoe kunt schakelen. Realtime Database en Cloud Firestore laten je toe om data te verwijderen, editen of toe te voegen en types in Cloud Firestore te specificeren. Dit komt van pas wanneer je net begint.
Realtime Database en Cloud Firestore richten op efficiëntie en snelheid. Het is makkelijk om luisteraars op te stellen die een cloud code of local client code op gang kunnen stellen wanneer belangrijke data wordt gemodificeerd. Gebruikers ontvangen hierdoor razendsnelle updates. Deze updates kunnen offline worden toegepast en later worden gesynchroniseerd.
Verschillende operaties kunnen worden samengevoegd in transacties of batches. Deze zijn belangrijk zodat multi-step processen jouw data niet vervuilen als er halverwege iets misgaat. Als laatste gebruiken beide services security op basis van Firebase regels. Dit is een configuratie document die aangeeft welke locaties beperkte lees en schrijf toestemming hebben.
Vervolgens zullen we kijken naar belangrijke factoren en verschillen waarbij je rekening moet houden als je beide oplossingen evalueert.
Waarschijnlijk een van de grootste verbeteringen van Cloud Firestore is de meer robuuste querying ondersteuning. Waarbij Realtime Database queries het alleen toelaten om een enkele sorteer parameter of conditie toe te passen is het met Cloud Firestore mogelijk om documenten te vinden door verschillende velden met elkaar te vergelijken.
Terwijl sommige samengestelde queries gebruik maken van een index kan Cloud Firestore deze aanmaken (met een klein beetje werk van jezelf). Als je een query uitvoert die gebruik maakt van een index, zal deze query mislukken. Wel zal je een URL ontvangen die met met de Firebase Console gebruikt kan worden om de index te genereren. Het zou mooi zijn als dit meer gestroomlijnd is. Het uitvoeren van API queries zal mislukken en om vervolgens de URL’s een voor een te kopiëren en plakken is niet leuk. Om elke index handmatig te creëren is nog minder leuk. Ondanks dat de reeks nu een eersteklas datatype is, zijn querying reeksen nog niet ondersteund buiten deze producten.
Door de simpele data structuur van Realtime Database kan je queries laten zoeken naar velden die beginnen met jouw query (of eindigen met jouw query wanneer je een set van jouw strings achterstevoren opslaat). Dit wordt niet ondersteund door Cloud Firestore. Daar en tegen moet je gebruik maken van vergelijkings operatoren (bv. > of >=) met jouw query. Dit kan wel of niet gelijkwaardig zijn afhankelijk van de data waar je naar op zoek bent. De meest bekende toepassing van deze functie in Realtime Database is responsive zoek resultaten voor gebruikers die iets typen in een zoekveld waarnaar ze geïnteresseerd zijn. Deze functie werkt goed met een lijn code in Realtime Database, maar bij Cloud Firestore moet je meer ingewikkelde zoekfuncties schrijven als je opzoek bent naar een voor zichzelf sprekende prefix.
Een fantastische functie van Realtime Database wat niet beschikbaar is bij Cloud Firestore is het gemak waarmee data geïmporteerd en geëxporteerd kan worden (simpele JSON files) in de Firestore Console. Deze functie is met name erg handig bij het migreren van data. Denk hierbij aan het migreren van data van een ontwikkelings omgeving naar een andere omgeving. Daarnaast is het makkelijk wanneer niet ontwikkelaars snel data moeten wijzigen of toevoegen aan de database. Hierbij zijn geen API of scripts nodig, alleen een paar klikken op de knop om totaal nieuwe datasets te uploaden voor jouw app.
Een andere oplossing is om gebruik te maken van een nette announcement die Realtime Database onlangs heeft gemaakt. Dit is de mogelijkheid om gebruik te maken van de Firebase Command Line Interface (CLI) tool om scripts en automatisch data migraties en querying te verwerken. Met de CLI tools hoef je niet langer het limited Console dashboard te gebruiken.
Helaas zitten admins bij Cloud Firestore vast, omdat ze documenten, velden en collecties een voor een moeten verwijderen, toevoegen of aanpassen.
Zoals eerder benoemd richt Realtime Database zich op realtime updates via web sockets. Dit is een geweldige manier om de aanwezigheid van gebruikers in samenwerkende apps te beheren. Met Realtime Database hebben ontwikkelaars toegang tot tools die nodig zijn om te bepalen welke gebruikers actief zijn online en gebruik maken van de app in real time. Ondanks dat je met Cloud Firestore data listeners kan opstellen ondersteund Cloud Firestore niet natively de aanwezigheid van gebruikers. Hiervoor is Realtime Database nodig om een oplossing te kunnen implementeren.
Een ander belangrijk punt om mee te nemen zijn de kosten die gepaard gaan met beide platformen en hoe dat in verhouding staat met de data opbouw die je nodig hebt voor jouw app. Via deze link kun je de kostenstructuur van beide platformen bekijken. Natuurlijk is er meer waar je rekening mee moet houden.
Het komt er op neer dat de kosten van Realtime Database toenemen in verhouding met de totale hoeveelheid data die je verstuurd via de database ongeacht de hoeveelheid. API calls hiervoor nodig zijn. Bij Cloud Firestore nemen de kosten toe voor elke API call die je maakt ongeacht de hoeveelheid data die je verstuurd en ontvangt. Dit betekent dat als je heel veel kleine transacties hebt, zoals locatie updates of live chat berichten het beter is om gebruik te maken van Realtime Database. Aan de andere kant als je van plan bent meer traditionele RESTful verzoeken te maken waarbij de app minder calls maakt met een groter response gaat de voorkeur uit naar Cloud Firestore.
Gelukkig staan de toepassingen van beide services in verhouding met de kostenstructuur. Er is echter een opmerking voor beide services waar je rekening mee dient te houden. Elke query die je maakt met Realtime Database is een deep query. Dit betekend dat je alle nested child nodes en al hun child nodes etc. zal terug ontvangen. Je moet dus goed nadenken over jouw architectuur om ervoor te zorgen dat je niet onnodig veel data herhaaldelijk ontvangt, aangezien je betaald voor elke data transfer met elke query. Aan de andere kant is elke query die je maakt met Cloud Firestore shallow, waardoor je alleen top level data en referenties ontvangt. Dit vereist dat je subsequente API calls moet maken voor nested data. De plannen zijn vrij genereus voor beide databases. Dit is misschien niet direct nodig, maar zou goed van pas kunnen komen voor lange termijn succes met jouw app.
Met de kosten structuur van beide oplossingen in gedachten moet je je richten op een plat ontwerp en een normaliseerde datastructuur bij Realtime Database. Met deze structuur kan je jouw app richten op vele kleine verschillende en snelle real-time queries, die indruk zullen maken bij jouw gebruikers.
Cloud Firestore is niet zo snel als Realtime Database, zelfs niet wanneer je gebruik maakt van de real-time listeners. De afweging die hier tegenover staat is dat Cloud Firestore een verbeterde query support, shallow queries, referentie data types en collectie-document-subcollectie paradigma heeft. Hiermee kun je data vastzetten en jouw data normaal houden. Schaalbaarheid wordt hierdoor verbeterd, zonder dat de prestaties hieronder leiden.
Waar je wel op moet letten is dat je query’s niet kunt uitoefenen met sub collecties in Cloud Firestore. Hierdoor kan je niet alles vastzetten wat je zou willen. Dit betekent dat je een query kan toepassen op een enkele collectie of subcollectie, maar niet door verschillende documenten kan zoeken voor waardes van vasten velden. Om deze reden zal je waarschijnlijk nog steeds een relatief plate data structuur toepassen bij Cloud Firestore.
Veel ontwikkelaars voelen zich meer thuis met het gebruik van Cloud Firestore SDK’s dan met Realtime Database SDK’s. Collecties en documenten zullen meer vertrouwd aanvoelen voor ontwikkelaars met een SQL achtergrond. Het creëren en uitvoeren van Cloud Firestore queries zal hierdoor natuurlijker aanvoelen dan het gebruik van Realtime Database’s listeners.
Het schrijven van queries voor Realtime Database zal goede planning en een nieuwe gedachtegang vereisen. Dit is omdat elke query bij Realtime Database een listener is die eenmalig geactiveerd kan worden, eenmalig gesloten en geactiveerd kan worden of opengehouden kan worden voor toekomstige updates. The listener wordt onmiddellijk geactiveerd met een response van de huidige stand van zaken van de resultaten. Hierdoor worden niet altijd de gewenste resultaten behaald. Je moet dan leren omgaan om bepaalde resultaten te negeren en actie te ondernemen op nieuwe updates die belangrijk voor je zijn. Door gebruik te maken van een heftige gedenormaliseerde dataset bij Realtime Database betekent dat je voorbereid moet zijn voor situaties waarbij simpele updates gemaakt door gebruikers verschillende updates zullen vereisen. Dat zijn updates van kopieën van de data die in andere nodes zitten verspreid over jouw database.
Het is misschien vanzelfsprekend om dit te benoemen, maar beide producten zitten in een ander stadium van hun levenscyclus. Realtime Database bestaat al jaren en is succesvol toegepast in verschillende projecten. Het heeft meerdere belangrijke updates over de tijd ontvangen en bugs die zijn verwijderd. Cloud Firestore aan de andere kant is pas een paar maanden oud sinds maart 2018 en bevindt zich nog steeds in het beta stadium.
Gedurende deze zes maanden hebben we meerdere nieuwe functies gezien voor Realtime Database. Hieronder vallen verbeterde beveiliging en multi-database sharing. Updates voor Cloud Firestore zijn met name gericht op het verbeteren van bugs. Dit is natuurlijk en het zal wat tijd kosten voordat er grote nieuwe functies en verbeteringen zullen uitkomen voor Cloud Firestore.
Voordat je gaat beginnen aan jouw volgende Google Firebase app zullen we een aantal functies bekijken die beide Realtime Database en Cloud Firestore op dit moment niet ondersteunen. Eerder hebben we aangegeven dat Cloud Firestore querying support heeft verbeterd, maar het heeft nog een lange weg te gaan in vergelijking met de query opties die beschikbaar zijn met een SQL database framework.
Je zal zien dat beide platformen “not equal to” query’s niet toestaan. Dit houdt in dat je expliciet moet zijn voor elke waarde die je zoekt of meerdere queries voor verschillende waardes moet maken. Vervolgens moet je de resultaten in the client of via de Firebase Cloud Function gebruiken om een vergelijkbaar resultaat te behalen. Firebase wilt zijn queries snel houden, waarvan dit het gevolg is. Het kan dus erg lastig zijn als je deze functie nodig hebt in jouw app.
Een andere native query filter die ontbreekt is de “string containing string” query. Het gevolg is dat je niet efficiënt blokken text kan doorzoeken of gedeeltelijk strings bij elkaar kan passen in jouw dataset. Dit probleem kan worden opgelost met behulp van third party indexing services zoals Algolia of ElasticSearch op aanraden van Google Firebase zelf. Deze oplossing is alleen ver van ideaal. Ondanks dat het met Cloud Firestore mogelijk is om queries toe te passen op meerdere velden is het niet mogelijk om een query op te stellen voor data met meer dan een filter restrictie. Zo kan je bijvoorbeeld niet zoeken naar een gebruiker met een hogere score van 9,000 in de afgelopen 24 uur.
Alle bovenstaande limitaties zouden niet zo vervelend zijn om mee om te gaan als het platform een manier aanbiedt om logische of samengestelde queries uit te laten voeren. Helaas bieden beide Realtime Database of Cloud Firestore deze functies niet aan. Dit kan hinderlijk zijn om mee om te gaan afhankelijk van jouw data. Het enige alternatief is om alle individuele queries zelf aan te maken (locaal of in een Cloud Functie) en deze resultaten aan elkaar te koppelen. Nog een andere reden waarom schema architectuur planning erg kan helpen is om de hoeveelheid werk voor zulk soort queries te verminderen.
De laatste limitatie die we kunnen benoemen is dat er geen project queries beschikbaar zijn op beide platformen. Project queries kunnen handig zijn wanneer je de hoeveelheid velden die teruggestuurd worden naar jouw cliënt met een query wilt verminderen. Wanneer jouw entity vele key-value paren bevat, maar als er alleen maar een interessant is moet je het hele object opvragen in Google Firebase om de enkele waarden te bekijken die je nodig hebt.
Realtime Database en Cloudfirestore zijn geen vervanging voor elkaar maar bieden ontwikkelaars de optie om een database uit te kiezen afhankelijk van hun behoeften. Daarnaast biedt het de mogelijkheid om gebruik te maken van het grote ecosysteem van Google en Firebase services binnen hun eigen systemen.
Hopelijk komt deze handleiding van pas wanneer je een geweldige Google Firebase app gaat ontwikkelen. Wellicht ben je er juist achter gekomen dat Firebase niet de oplossing is voor jouw volgende project. Bij Glamorous Goat hebben we succes gehad met beide methodes, Realtime Database en Cloud Firestore. We hebben hierbij wel goed alle voor en nadelen tegen elkaar afgewogen. Met elk project dat je start moet je zorgvuldig plannen anders zal je tijd en geld verliezen als je de verkeerde tool gebruikt.
Ik help je graag met al je vragen. Je mag me altijd even bellen of mailen.