← Terug naar projects
15 min
Slicekit: full-stack .NET + React SaaS-template

Slicekit: full-stack .NET + React SaaS-template

Een productieklare SaaS-template die de saaie 80% al voor je geregeld heeft: een event-driven .NET 10 API (vertical slices, DDD, CQRS, messaging) gekoppeld aan een typed React 19 SPA, plus een Astro-marketingsite. Passkeys, TOTP, OAuth, fine-grained permissions, een volledig afgebouwd adminpaneel, een hash-chained audit log, complete observability en CI zitten er allemaal in. Gebouwd omdat ik bij elk project hetzelfde fundament opnieuw zat te bouwen. Eén keer kopen onder een commerciële licentie, en de code is van jou.
  • .NET
  • C#
  • React
  • TypeScript

Inhoudsopgave

  1. Elke keer weer dezelfde eerste maand
  2. Wat Slicekit is
  3. Waarom deze stack
  4. Wat er in zit
  5. Auth
  6. Het adminpaneel
  7. Audit log en AVG
  8. De machinekamer
  9. De frontend
  10. Naar productie
  11. Features weggooien
  12. De checkout draait op mijn eigen tools
  13. Wat ik heb geleerd
  14. Huidige status

Bij elk project bouwde ik hetzelfde fundament opnieuw voordat ik ook maar één regel kon schrijven van het ding waarvoor ik eigenlijk werd betaald. Authenticatie met sessions en refresh tokens. Een adminpaneel om gebruikers te beheren. Een manier om te zien wat er gebeurde als er iets misging. Een typed client tussen de API en de UI zodat beide helften synchroon bleven lopen. En zodra er iets in productie ging in de EU moest het ook nog aan de AVG voldoen: data-export, accountverwijdering, toestemming. Elke keer ging de eerste maand op aan basiswerk in plaats van aan het product.

Het frustrerende is dat dit helemaal niet het interessante werk is. Het zijn gewoon de basisvereisten. Bijna elke SaaS heeft het nodig, het is overal grotendeels hetzelfde, en het goed voor elkaar krijgen kost gewoon veel tijd. Daarom heb ik Slicekit gebouwd: het fundament dat ik zelf graag had gehad, één keer netjes in elkaar gezet en getest, zodat het eerste wat je bouwt je product is en niet de boilerplate.

Elke keer weer dezelfde eerste maand

Een nieuwe SaaS starten vanuit een lege repository kost je weken voordat je iets hebt dat een gebruiker te zien krijgt. Je zet authenticatie op, en authenticatie alleen is al een project op zich: password hashing, sessions, refresh-token rotation, two-factor, OAuth, account lockout, password reset. Dan heb je een adminkant nodig om de boel te kunnen draaien, dus bouw je user management, het toekennen van permissions en een manier om een sessie in te trekken als er iets niet klopt. Dan besef je dat je geen idee hebt wat je systeem in productie doet, dus voeg je logging, metrics en tracing toe. En dan vraagt een klant in Europa om zijn gegevens en kom je erachter dat de AVG geen simpel vinkje is.

Er zijn boilerplate-templates, maar de meeste stoppen bij een loginpagina en een netjes opgemaakt dashboard. De lastige onderdelen, juist die waar de weken in gaan zitten, worden overgeslagen. En wat er wel in zit is vaak een mix van wat op dat moment populair was, zonder een samenhangende architectuur eronder. Uitbreiden voelt dan al gauw als werken tegen de template in plaats van erop voortbouwen.

Ik wilde iets anders: een compleet, uitgesproken fundament waar de keuzes al gemaakt zijn, de lastige onderdelen opgelost zijn, en de structuur consistent genoeg is dat je eigen features toevoegen logisch aanvoelt.

Wat Slicekit is

Slicekit is een full-stack SaaS-template in drie delen: een event-driven .NET 10 API, een typed React 19 single-page app, en een Astro-marketingsite, alle drie met dezelfde conventies. Je koopt het eenmalig onder een commerciële licentie, kloont het, en begint te bouwen. De volledige repository is van jou.

Je eigen project opzetten is één commando. Het scaffold-script kloont de template en hernoemt in één keer alles over de drie apps heen: de projectnaam en namespaces, de domeinen, het support-e-mailadres en de API-key prefix. Het genereert je dev- en production secrets opnieuw, schrijft de env-bestanden, en initialiseert git opnieuw, zodat wat je overhoudt vanaf de eerste commit echt je eigen project is.

curl -fsSL https://slicekit.dev/scripts/new.sh | bash

Het vraagt interactief om de projectnaam en de rest, met verstandige standaardwaarden, zodat je niets hoeft te onthouden. Maar het is ook mogelijk alle instellingen met flags in te stellen, zodat je het kunt gebruiken in scripts of AI het script makkelijk voor je kan runnen.

Waarom deze stack

De voor de hand liggende vraag als je ”.NET” ziet staan: waarom niet Node? De meeste templates gebruiken Node, de hiring pool is groter, en serverless deployen is een klik. Als je een dunne laag over een paar API’s bouwt of zo snel mogelijk een demo nodig hebt, is Node een prima keuze.

Maar Slicekit is bedoeld als fundament voor iets dat je jaren draait. Daar wordt de afweging anders. .NET is een gecompileerde, statisch getypeerde runtime die goed presteert zonder dat je er speciaal voor hoeft te engineeren. Het tooling-ecosystem (SDK, test runners, EF Core, editor-integratie) is door één partij onderhouden op een lang support-venster, zodat je niet een pipeline bouwt van twaalf losse packages die elk op hun eigen schema breken. En C# is saai op de manier waarop infrastructuur saai moet zijn: de patronen zijn bekend, de mensen die het kennen zijn niet schaars, en over drie jaar kun je er nog steeds iemand voor inhuren.

De frontend is een losse React SPA in plaats van een fullstack-framework zoals Next.js. De reden is simpel: de API is een echte API met een eigen contract, niet iets dat aan een framework vastzit. Dat maakt hem bruikbaar voor andere clients, en het houdt de twee helften eerlijk gescheiden.

De marketingsite is Astro. Voor content-zware statische sites is dat de logische keuze: standaard nul JavaScript naar de browser, goede SEO out of the box, en snel zonder dat je er moeite voor hoeft te doen. Docs en blogposts zijn gewoon Markdown-bestanden.

Het centrale idee is de vertical slice. Een feature leeft als een op zichzelf staande slice aan beide kanten van de stack: een map in de API met zijn domein, commands, queries en endpoints, en een bijbehorende map in de frontend met zijn routes, componenten en data-hooks. Features lopen niet door elkaar heen. Dat maakt de codebase navigeerbaar, maakt iets weggooien dat je niet nodig hebt een kwestie van een paar mappen verwijderen, en het werkt ook prettig voor AI-codeeragenten die zo een duidelijke structuur krijgen van waar alles thuishoort.

Het React SPA-dashboard

De SPA direct na het inloggen. Auth, de typed API client en het instellingengedeelte werken meteen volledig.

Wat er in zit

Ik loop het door in de volgorde waarop je een nieuw project zou bouwen. Zo heb ik bepaald wat erin moest, en alles hieronder werkt meteen als je de template kloont.

Auth

Auth is het onderdeel dat stilletjes blijft uitdijen tot het een maand heeft opgeslokt, dus ik heb het als een volwaardige feature behandeld en goed afgemaakt. Inloggen draait op cookie-based sessions waarbij de JWTs in HttpOnly cookies worden opgeslagen, met CSRF-bescherming en refresh tokens die bij elk gebruik roteren. Wordt een gestolen token ooit opnieuw gebruikt, dan pikt de family-based detection dit op en wordt de hele tokenlijn in één klap ongeldig verklaard. De functionaliteiten die je makkelijk voor je uitschuift totdat je beseft dat je ze eigenlijk al had moeten hebben, zitten er al in: passkeys voor modern inloggen zonder wachtwoord (WebAuthn/FIDO2), TOTP two-factor met recovery codes voor als een telefoon kwijtraakt, en OAuth via Google en GitHub achter een interface die je met een extra provider kunt uitbreiden. Nieuwe wachtwoorden worden gecontroleerd tegen Have I Been Pwned en geweigerd als ze gelekt zijn, herhaalde mislukte pogingen zetten een account op slot, en alles wat tot een persoon herleidbaar is wordt versleuteld voordat het de database bereikt.

Voor autorisatie heb ik bewust geen rollen gebruikt. Rollen voelen netjes totdat je een supportmedewerker hebt die gebruikers mag inzien maar niet verwijderen. Daar is geen rol voor, dus maak je er een. Dan nog een, voor iemand die ook accounts mag unlocken maar geen wachtwoorden mag resetten. Dat eindigt altijd hetzelfde: meer rollen dan features, en niemand die nog weet wat support_tier_2 eigenlijk mag. Slicekit werkt met 33 losse permissions per actie in plaats daarvan, gedeclareerd in één bestand. Diezelfde set stuurt ook de UI aan, zodat API en frontend nooit uit de pas lopen. Een gebruiker ziet nooit een knop die hij niet mag gebruiken. Een API key krijgt precies de permissies die je hem geeft en niet meer. Rate limiting, security headers en het correct omgaan met reverse-proxy headers zitten er ook gewoon in.

Het beveiligingsgedeelte van de instellingen

Het instellingengedeelte: wachtwoord wijzigen met live policy checks, e-mailadres wijzigen met bevestiging, en two-factor beheren.

Het adminpaneel

Vroeg of laat heeft een product een backoffice nodig, en de meeste templates wijzen er even naar en gaan dan verder. Die van Slicekit is gewoon gebouwd. Je krijgt paginated user management en een detailpagina per account waar je permissions kunt toekennen, een verdachte sessie kunt intrekken, iemands API keys kunt beheren, een account kunt uitschakelen, een lockout kunt opheffen, of namens de gebruiker een password-reset- of verificatie-e-mail kunt sturen. Wil je precies zien wat een gebruiker ziet, dan kun je hem impersonaten, waarbij Slicekit de reden vastlegt en op alles wat tijdens de impersonatie gebeurt een act-claim stempelt, zodat het altijd traceerbaar is dat jij het was die namens hem handelde.

Admin-detailpagina per gebruiker met accountacties

Een gebruikersdetailpagina in het adminpaneel: status, accountacties, en de knoppen om dat account te beheren.

Het permission-model van zonet wordt hier iets tastbaars. Je zet een set wijzigingen klaar, ziet in één oogopslag welke permissions read-only zijn, en past ze in één keer toe, waarbij er niets opgeslagen wordt tot je bevestigt. Iemand tot admin promoveren geeft de volledige set openlijk weer, zodat je altijd kunt zien wie wat mag.

De fine-grained permissions-editor

Wijzigingen aan permissions klaarzetten voor een gebruiker. Autorisatie draait op 33 expliciete permissions, en diezelfde set bepaalt wat de UI toont.

Audit log en AVG

De vraag die veel hiervan aandrijft is de vraag die pas opduikt als er al iets is misgegaan: wat is er nou eigenlijk gebeurd? Elke betekenisvolle actie belandt daarom in een hash-chained audit log, een tamper-evident record dat je direct vanuit het adminpaneel kunt lezen, via Loki kunt doorzoeken, en tot in Grafana kunt volgen als je de volledige trace wilt. Je filtert op tijdvenster, categorie en uitkomst, en volgt een enkel request van begin tot eind.

Omdat ik in de EU woon, weet ik ook dat je de AVG niet een week voor de lancering even erbij gooit. Data-export en accountverwijdering met anonimisering horen bij het fundament, zodat Europese projecten er meteen klaar voor zijn.

De weergave van de audit log

De hash-chained audit log, doorzoekbaar op tijd, categorie en uitkomst, met deep links naar Grafana.

De machinekamer

Onder de API zit een architectuur waar ik bewust geen bochten heb afgesneden, want dit is het deel dat je later niet meer kunt inbouwen. Domain-driven aggregates raisen events naarmate de business rules worden verwerkt. Wolverine verzorgt de CQRS-flow, met FluentValidation aan de ingang. Die events verlaten het systeem via een transactional outbox in PostgreSQL en gaan via RabbitMQ met at-least-once delivery en idempotent consumers, zodat een bericht het altijd redt, ook als een downstream service een slechte dag heeft. Daaromheen zitten de onderdelen die de meeste producten nodig hebben en die niemand leuk vindt om aan te sluiten: persistence via EF Core met soft-delete filters, caching met FusionCache over Redis, transactionele e-mail via FluentEmail en Razor-templates, en file storage op alles wat S3-compatibel is, met MinIO als lokale invaller. De hele API documenteert zichzelf via OpenAPI 3.1 en Scalar, is versioned, voorzien van health checks, en klaar om background jobs te draaien.

De frontend

De SPA is React 19 op Vite met TypeScript in strict mode. TanStack Router geeft haar file-based routes met typed links en automatic code splitting, TanStack Query regelt de data, en één typed API client zit tussen beide helften in. Die client draagt cookies, CSRF en een silent refresh-and-retry mee, zodat featurecode nooit hoeft na te denken over een verlopende token. Formulieren draaien op React Hook Form en Zod, en een validatiefout van de server komt terug op precies het veld dat hem veroorzaakte. Het wordt geleverd met Engels en Nederlands, met light, dark en system themes, een interface die op diezelfde permissions wordt afgeschermd, en een compleet instellingengedeelte voor profiel, beveiliging, sessions, API keys en privacy.

Naar productie

Het laatste stuk is het werk waarmee je een wijziging kunt vertrouwen voordat hij eruit gaat. OpenTelemetry stuurt traces, metrics en logs naar Tempo, Prometheus, Loki en Grafana, dat wordt geleverd met vier dashboards, zes alert rules en Alertmanager al aangesloten. Vier testprojecten garanderen de kwaliteit: snelle unit tests en architecture tests, feature tests die via Testcontainers tegen een echte database draaien, en API tests. CI bouwt beide apps, draait de volledige suite, en scant bij elke pull request op kwetsbare packages. Een merge naar main levert een multi-stage Docker-image dat naar de registry wordt gepusht, met een software bill of materials en een provenance attestation eraan vast.

Features weggooien

Een template waarmee je alleen dingen kunt toevoegen is maar half nuttig. Niet alle producten hebben alles nodig wat Slicekit levert, dus ik heb er serieus werk van gemaakt om features verwijderbaar te houden. Omdat een feature een vertical slice is aan beide kanten, blijft weggooien beperkt tot die ene slice: verwijder de slice-map in de API, zijn endpoints, en de bijbehorende frontendmap, en volg daarna een korte gedocumenteerde opschoning voor het schema en de events. De architecture tests laten de build daarna falen als er nog ergens iets verwijst naar wat je hebt weggegooid, zodat je geen halve verwijdering kunt achterlaten. De structuur die de code navigeerbaar maakt, maakt het ook veilig om weer terug te snoeien.

De checkout draait op mijn eigen tools

Om de template te verkopen had ik een checkout nodig, en in plaats van een zware e-commerce stack erbij te halen heb ik er een samengesteld uit dingen die ik al had. Kies je de Solo- of Team-licentie, dan vraagt een klein dialoogvenster om twee dingen: je GitHub-username en je e-mailadres. Dat formulier post naar StaticForm, een ander project van mij, dat het doorgeeft aan Stripe en een Checkout-URL teruggeeft. Je betaalt op Stripe, en dat is de enige plek waar je kaartgegevens ooit langskomen.

Slaagt de betaling, dan vuurt StaticForm drie submit actions af op basis van dat ene formulier:

  • Een webhook naar de GitHub API die je username uitnodigt voor de private repository, zodat je toegang binnen seconden na het betalen in je inbox ligt.
  • Een orderbevestiging naar je e-mailadres.
  • Een melding aan mij dat iemand zojuist Slicekit heeft gekocht.

Dat StaticForm hier stilletjes het saaie werk doet voor Slicekit voelt wel toepasselijk. Allebei gebouwd om de boilerplate weg te nemen, het ene voor website formulieren, het andere voor SaaS’es.

Wat ik heb geleerd

Auth bleek veel uitgebreider te zijn dan ik dacht. Ik begon hieraan met het idee dat authenticatie een min of meer opgelost, kopieer-en-plak-probleem was. Dat is het niet. Passkeys, refresh-token rotation, family-based theft detection, breach checking, lockout en CSRF goed laten samenwerken, en dat ook nog laten blijven werken terwijl ik er omheen dingen veranderde, kostte veel meer tijd dan de delen die een gebruiker daadwerkelijk ziet. Ik heb er flink meer respect voor gekregen voor wat er allemaal onder een loginformulier schuilgaat.

De vertical slice was de beste architectuurkeuze die ik heb gemaakt. Aan het begin moest ik kiezen: features die veel gedeelde code delen, of elke feature gewoon op zichzelf aan beide kanten van de stack. Ik koos voor dat laatste, en ik zou het zo opnieuw doen. Een half jaar later kan ik nog steeds snel vinden wat ik zoek, en ik ben zelden bang om iets te veranderen. Een codebase die ik in mijn hoofd kan houden is meer waard dan een slimme oplossing die ik na drie maanden niet meer snap.

Ik vertrouw het project omdat ik het helemaal heb gelezen. Het was sneller geweest om grote stukken te genereren met AI en gauw erdoorheen te gaan, maar een template is een belofte: iemand betaalt in de veronderstelling dat de lastige onderdelen ook echt kloppen. Dus er ging niets ongelezen in. De architecture tests, de integration tests tegen een echte Postgres, de uren die gingen in het bijschaven van de defaults: daardoor durf ik het te verkopen.

De AVG, data-anonimisering en audit logs had ik nooit eerder goed uitgezocht. Ik wist dat de AVG bestond en had een globaal beeld van wat het in de praktijk inhield (gegevens kunnen opvragen en verwijderen), maar om het volledig goed te doen had nogal wat voeten in de aarde. Ik heb geleerd:

  • Hoe je een account verwijdert zonder de audithistorie kapot te maken.
  • Welke velden je moet anonimiseren en welke je mag bewaren.
  • Hoe een hash-chained audit log werkt zodat je achteraf kunt bewijzen dat er niets mee geknoeid is.

De code was het makkelijke deel. Bij het bouwen en verkopen van een product komt veel meer kijken dan alleen code schrijven. Hoe je het prijst, hoe je een licentie opstelt, wat een onbekende developer in de docs nodig heeft voordat hij het vertrouwt, hoe je het onder de aandacht krijgt bij de mensen die het echt zouden willen. Dat bepaalt allemaal of die code iets waard is voor iemand anders dan ikzelf. Ik ben het meeste ervan nog aan het uitvogelen.

Huidige status

Slicekit is live, met een publieke marketingsite, volledige documentatie, en een gevulde demo-omgeving waar je kunt inloggen als admin of als gewone gebruiker en het hele oppervlak kunt verkennen zonder je aan te melden. Het is een eenmalige aankoop onder een commerciële licentie, Solo of Team, met levenslange updates en een niet-goed-geld-terug-garantie.

Het doel is nog steeds hetzelfde als de reden waarom ik het bouwde: een fundament geven waar de keuzes al gemaakt zijn en de boilerplate opgelost is, zodat je eerste commit een feature is. Een kijkje nemen kan op slicekit.dev.

Heb je feedback, vragen, of wil je er iets op laten bouwen? Neem gerust contact op!