Concepten
Data als bestanden
Waarom Nimbly data als gewone bestanden in Git bewaart, wat erbuiten blijft en hoe het snel blijft zonder database.
Nimbly bewaart data als gewone bestanden in de repository van de applicatie. Er is geen databaseserver. Een resource is een map, de definitie ervan is een JSON-bestand, en elk record is een eigen JSON-bestand. Dat is een bewuste keuze, en die bepaalt hoe een Nimbly-project wordt gebouwd, gedeeld en gedraaid.
ext/data/projects/
.meta the definition: fields, validation, indexes, permissions
4f1c9a2e7b... one record
9d03b6e1c4... another record
Eén definitie, de rest volgt
Een resource wordt één keer beschreven, in zijn .meta. Vanuit die definitie levert Nimbly het overzicht en de formulieren in de admin, de validatie, de REST API en de OpenAPI-beschrijving ervan, rechten per handeling, de geschiedenis van elke wijziging, en de context die de ingebouwde agent gebruikt. Voeg een veld toe aan de .meta en dat alles volgt. Er zijn geen migraties: een nieuw veld is in oudere records gewoon leeg tot iemand het invult.
Eén id per record, overal
Elk record krijgt bij het aanmaken een korte willekeurige id, zoals 1ztuvo56o5vze6t0. De id is de bestandsnaam van het record, en verandert nooit.
Databases nummeren hun rijen meestal 1, 2, 3. Die nummers horen bij één database: een andere omgeving deelt dezelfde nummers uit aan andere records, dus data verplaatsen of samenvoegen betekent records hernummeren en elke verwijzing ernaar aanpassen. In Nimbly bestaat dat probleem niet:
- Een record is overal hetzelfde record: in ontwikkeling, op staging, in productie, in Git, in de API en in een export.
- Verwijzingen blijven kloppen. Een veld dat naar een ander record wijst, bewaart de id daarvan, en blijft naar hetzelfde record wijzen waar de data ook heen gaat.
- Content verhuist vrij. Records die op verschillende machines of branches zijn gemaakt, voeg je samen zonder botsingen, en een import in een andere site houdt elke id.
- Je eigen ids werken ook. Een record kan de id krijgen die het in een ander systeem heeft, zoals een ordernummer.
Bezoekers zien deze ids nooit: publieke adressen gebruiken een leesbare slug, zoals /projects/harbour-atlas.
Geen databaselaag
Omdat de data bestanden zijn, ontbreekt een hele laag van een gebruikelijke webstack gewoon:
- geen databaseserver om apart te installeren, te draaien, te beveiligen, af te stellen en te back-uppen,
- geen ORM of querylaag tussen de code en de data,
- geen migraties, en geen schema om gelijk te houden tussen ontwikkeling, staging en productie,
- geen verbindingsinstellingen of database-inloggegevens,
- geen dumps om te exporteren en importeren om een site te delen of te verhuizen.
Een Nimbly-site is een map: code, content en data samen. Dat houdt de stack klein, de hosting eenvoudig en elk deel van de applicatie leesbaar.
Content staat in Git, naast de code
Omdat records bestanden in ext/ zijn, krijgt content versiebeheer samen met de code die hem gebruikt:
- Geschiedenis en terugdraaien. Elke contentwijziging is een commit die je kunt bekijken, vergelijken en terugdraaien, in dezelfde geschiedenis als de templates die hem tonen.
- Branches. Een featurebranch heeft zijn eigen content: een nieuwe resource met voorbeeldrecords, een herschreven pagina, een aangepast menu. Review en merge content net als code.
- Kleine, duidelijke diffs. Eén record is één bestand, dus een wijziging raakt alleen dat record, en twee mensen die verschillende records bewerken, zitten elkaar nooit in de weg.
- Omgevingen blijven gelijk. Ontwikkeling, staging en productie delen content via de repository. Werken redacteuren in productie, dan commit de geplande
ext:synchun wijzigingen en pusht ze terug, zodat de volgende deploy en elke developer erop voortbouwen. Zie Omgevingen.
Clonen, en de site is er
Wie de repository van de applicatie clonet, krijgt elk record, elke paginatekst, instelling, vertaling en elk mediabestand mee. Draai de setup en de lokale site toont meteen de echte content: geen database om te installeren, geen dump om te importeren, geen verbinding om in te stellen. Hetzelfde geldt voor een AI-codeeragent: die leest de datadefinities en de records als bestanden, naast de code die ze gebruikt.
Wat buiten Git blijft
Niet alles wat een site bevat, hoort in een repository. De setup schrijft een ext/.gitignore die dit per omgeving houdt:
| Per omgeving bewaard | Waarom |
|---|---|
| Gebruikersaccounts | Wachtwoorden en persoonsgegevens reizen niet mee met de code; elke omgeving heeft zijn eigen admin |
| Sessies, jobwachtrij, schedulerstatus | Runtime-status die elke minuut verandert |
| Geschiedenis van records | 90 dagen bewaard op de omgeving waar de wijziging is gedaan |
| Caches en verkleinde afbeeldingen | Worden opnieuw gemaakt wanneer nodig |
.env |
Inloggegevens en de PEPPER waar versleutelde velden van afhangen |
Een repository bevat dus de content en structuur van de site, en een back-up van een productiesite dekt ook wat Git niet heeft. Zie Back-up en herstel.
Snel zonder database
Een bestand lezen is snel, en Nimbly leest niet wat het niet nodig heeft:
- Indexen. Een veld in de lijst
indexkrijgt een opzoekpad, zodat een slug in een adres zijn record direct vindt. - Lijstcache. Voor elk veld waarop een pagina filtert of sorteert, staan de waarden van alle records samen in één cachebestand, dus een pagina van 20 uit 50.000 leest 20 records.
- Querycache. Queryresultaten worden gecachet en vervallen bij elke schrijfactie, of die nu van de site, de API, Git of een sync komt.
- Gesplitste mappen. Een map met heel veel bestanden maakt het bestandssysteem traag.
"splitdir": truein de.metavan een resource verdeelt de records over twee niveaus submappen op basis van de UUID, zodat elke map klein blijft. Zet het aan voor een resource die groeit voorbij ongeveer 10.000 records; verder verandert er niets, niet in templates, libraries of de API.
Met gesplitste mappen en indexen kan een resource zo'n 100.000 records aan. Schrijfacties op één resource gebeuren één voor één, zodat records consistent blijven, ook met meerdere containers op een gedeeld volume.
Resources ontwerpen met dit in gedachten
- Eén resource per soort ding. Projecten, mensen en sectoren zijn aparte resources; een
select-veld verwijst van de ene naar de andere. - Indexeer wat je opzoekt. Slugs, e-mailadressen, externe ids.
- Houd records gericht. Een record wordt in zijn geheel gelezen: zet grote of zelden gebruikte data in een eigen resource in plaats van in een groeiend veld.
- Gebruik de systeemvelden. Elk record heeft
uuid,_created,_modifieden wie ze heeft gemaakt; daar heb je geen eigen velden voor nodig.