8.4 KiB
MijnBesluit — architectuur
Wat het is
Een platform om je beslissingen bij leven vast te leggen in een digitale kluis, en om berichten te versturen naar familie en vrienden — nu of in de toekomst, en ook pas na je overlijden.
De vorm in één plaatje
Website (Blazor) Android / iOS (MAUI)
| |
| in-proces | TLS-socket, poort 5200
v v
+---------------------------------------------+
| MijnBesluit.Server (dispatcher, sessies) | <- alleen voor de socketkant
+---------------------------------------------+
\ /
v v
+---------------------------------------------+
| MijnBesluit.Application |
| AccountService, FamilyService, VaultService, |
| MessageService, LegacyService + crypto |
+---------------------------------------------+
|
+---------------------------------------------+
| MijnBesluit.Domain (entiteiten + regels) |
+---------------------------------------------+
|
+---------------------------------------------+
| MijnBesluit.Infrastructure (EF Core) |
+---------------------------------------------+
|
MariaDB + versleutelde bestanden op schijf
De regels staan één keer, in Application en Domain. De website en de app zijn allebei niet meer dan een manier om diezelfde regels aan te roepen. Wat op de website niet mag, kan dus ook via de app niet — dat hoeft nergens tweemaal te worden afgedwongen.
De projecten
| Project | Wat het doet | Externe packages |
|---|---|---|
MijnBesluit.Domain |
Entiteiten en de regels: eigendom van profielen, overlijdensprocedure, vrijgave van berichten | geen |
MijnBesluit.Application |
De handelingen: registreren, uitnodigen, kluis, berichten, nalatenschap. Plus alle versleuteling | geen |
MijnBesluit.Protocol |
Het socketprotocol: framing, de server-luisteraar en de client voor de app | geen |
MijnBesluit.Server |
Vertaalt socketverzoeken naar Application-aanroepen; sessiebeheer | geen |
MijnBesluit.Infrastructure |
EF Core op MariaDB, sleutelbestand, bestandsopslag, e-mail | EF Core, Pomelo |
MijnBesluit.ServiceBeheer |
De Windows-service installeren, verwijderen, starten en stoppen | geen |
MijnBesluit.SocketServer |
De zelfstandige service voor de app, het beheerschermpje en de achtergrondtaken | Hosting, WinForms |
MijnBesluit.Web |
De website in Blazor | geen |
MijnBesluit.SelfCheck |
Controleert de regels zonder database of webserver | geen |
Dat de eerste vier geen enkel extern package nodig hebben is met opzet: daardoor zijn de regels
en het protocol te bouwen en te controleren zonder dat er ook maar iets geïnstalleerd hoeft te
worden, en is MijnBesluit.Protocol zonder gedoe te hergebruiken in de MAUI-app.
Geen webservice, geen IIS
De website is Blazor Server en roept de Application-laag rechtstreeks aan, in hetzelfde proces. Er zit dus geen webservice tussen die serialiseert, over HTTP praat en weer deserialiseert.
De app praat met MijnBesluit.SocketServer: een zelfstandig draaiende Windows-service (64 bits)
met een eigen TCP-luisteraar. Daar komt geen IIS aan te pas.
Dezelfde exe is ook zijn eigen installatieprogramma: start Windows hem, dan draait de server;
start jij hem met de rechtermuisknop als administrator, dan verschijnt een schermpje met een
poortnummer en een knop Install — of UnInstall als hij al geïnstalleerd is. Zie
docs/SocketServer-installeren.md.
dotnet publish src\MijnBesluit.SocketServer -c Release -r win-x64 --self-contained false -o D:\MijnBesluit VS\MijnBesluit\publish
Binnen de service:
- één thread accepteert verbindingen;
- elke verbinding krijgt een eigen thread met een blokkerende leeslus;
- elk verzoek gaat naar zijn eigen thread, zodat een trage databaseactie de volgende verzoeken van diezelfde app niet ophoudt. Elk verzoek krijgt daarmee ook zijn eigen databasecontext.
Hoe een profiel van eigenaar wisselt
Dit is de kern van hoe MijnBesluit zich verspreidt.
- Jan maakt een account. Hij krijgt een profiel (dat hij meteen zelf bezit) en een familieproject.
- Jan voegt zijn dochter Eva toe. Er komt een profiel voor Eva, beheerd door Jan. Jan kan alles aan dat profiel wijzigen.
- Jan stuurt een uitnodiging naar Eva's e-mailadres. In de database staat alleen de SHA-256 van het uitnodigingstoken; het token zelf bestaat enkel in die ene e-mail.
- Eva klikt op de link en kiest een wachtwoord. Op dat moment wordt het profiel geclaimd:
claimed_by_user_idwordt gevuld enmanaged_by_user_idleeggemaakt. - Vanaf nu kan Jan niets meer aan Eva's profiel wijzigen. Hij ziet haar nog steeds in zijn familie staan, met het label "geclaimd".
- Eva krijgt haar eigen familieproject en nodigt op haar beurt anderen uit.
De regel staat op één plek: ProfileOwnership.CanEdit. Alles wat een profiel wil wijzigen gaat
daarlangs.
Overlijden en nalatenschap
-
Je wijst beheerders aan, op volgorde. Zij zien dat meteen staan onder Aan mij toevertrouwd, met hun volgorde erbij — daar reken je tenslotte op als het zover is. Meer dan de aanwijzing zien ze niet: geen datum, geen aantallen, geen kluis.
Weghalen kan ook (
legacy.remove-manager). Dat hoort erbij: een beheerder kan zelf overlijden, en aanwijzen zonder kunnen terugnemen is geen keuze. Eén geval kan niet — iemand die je kluis al heeft gekregen. Dan is de procedure afgerond en heeft hij gezien wat erin stond; die regel weghalen zou alleen de administratie opschonen en niet wat er gebeurd is. -
Iedereen uit je kring kan melden dat je bent overleden: de mensen die jij hebt toegevoegd en die hun uitnodiging hebben aangenomen. Eén melding per gebruiker, afgedwongen met een unieke sleutel in de database.
-
Minstens één van die meldingen komt van een aangewezen beheerder. Zolang die er niet is, begint er niets: geen bericht aan de betrokkene, geen termijn die verloopt.
Waarom niet "alleen beheerders mogen melden", wat voor de hand lag: dan heb je evenveel beheerders nodig als er meldingen vereist zijn. Wijs je er twee aan terwijl er drie meldingen nodig zijn — wat de meeste mensen zullen doen — dan kan de procedure nooit worden afgerond en gebeurt er bij een overlijden niets. Stilzwijgend, want niemand merkt het.
Zo werkt het nu dus niet. Het aantal haal je met je kring, want die merkt een overlijden als eerste en is groot genoeg. De beheerder is de stem die er in elk geval bij moet zitten. Twee verschillende soorten mensen moeten het dus eens zijn voordat er iets in beweging komt.
Blijft die beheerdersmelding uit, dan zegt het nalatenschapsscherm dat met zoveel woorden (
WachtOpBeheerder). En wie nog niemand heeft aangewezen, krijgt daar een waarschuwing: zonder beheerder blijft de kluis ook na een overlijden dicht. -
Zodra aan allebei is voldaan (standaard drie meldingen, per profiel instelbaar), krijgt de betrokkene zelf een e-mail: klopt dit niet, laat het weten.
-
Reageert die persoon binnen de bezwaartermijn (standaard 14 dagen), dan vervallen alle meldingen en gebeurt er niets.
-
Reageert die persoon niet, dan wordt het overlijden bevestigd. De aangewezen nalatenschapsbeheerders krijgen toegang tot precies die kluisitems die zijn gemarkeerd als "vrijgeven na overlijden" — de rest blijft dicht. Berichten met "pas als ik er niet meer ben" worden bezorgd.
Een lopende procedure ziet er bij de beheerder precies zo uit als iemand die springlevend is. Dat is met opzet: anders kon hij aan zijn eigen scherm aflezen dat er meldingen binnen zijn over iemand die dat nog kan tegenspreken. Pas bij stap 5 verandert de kaart.
Later kan een koppeling met de BRP (de opvolger van de GBA) stap 1 tot en met 4 vervangen door
één betrouwbare bron. De rest van de code verandert dan niet: alleen wie DeathConfirmedAt
vult, verandert.
Achtergrondtaken
AchtergrondTaken in de SocketServer draait elk kwartier en doet drie dingen: berichten bezorgen
waarvan de datum is bereikt of de afzender bevestigd is overleden, bezwaartermijnen die zijn
afgelopen afronden, en verlopen sessies opruimen.
Tijd
Alles wordt in UTC opgeslagen en pas op het scherm omgerekend naar lokale tijd. Een bericht dat op 25 december bezorgd moet worden, verschuift zo nooit door zomertijd.