End-to-End IAM, del 1: Arkitekturen bakom Elytra och Ascendra
Det här är del 1 i en serie som går djupare in i end-to-end-iam, ett sidoprojekt där jag byggt ett fiktivt HR- och IGA-landskap kopplat mot en riktig Entra ID-klientorganisation. Den ursprungliga artikeln gav helhetsbilden. Den här delen zoomar in på ett enda beslut: varför Elytra och Ascendra är helt separata system, och vad det kostar respektive ger i praktiken.
Två system, en gräns
Elytra är HR-systemet. Det är den primära källan för anställningsdata: när någon anställs, byter roll eller slutar är det Elytra som bestämmer sanningen. Ascendra är IGA-plattformen (Identity Governance & Administration). Den äger identiteter, roller, provisionering, SSO-anslutningar och granskningsloggar.
I många verkliga organisationer suddas den här gränsen ut över tid. HR-systemet får en integration som skriver direkt till katalogen “för att det är enklare”, eller IGA-plattformen får läsbehörighet till HR-databasen “bara för att slippa vänta på en händelse”. Var och en av de genvägarna känns rimlig i stunden, men de gör det svårare att veta vem som faktiskt äger ett beslut, och svårare att byta ut endera systemet senare.
Jag satte därför tre hårda regler för projektet:
- Separata Convex-miljöer för Elytra och Ascendra
- Ingen delad databas
- Ingen direkt kommunikation mellan systemen
Convex är en plattform för serversidan som samlar databas, serverfunktioner och realtidsuppdateringar i en och samma lösning. Att köra Elytra och Ascendra som två helt separata Convex-miljöer innebär att de inte bara är logiskt separerade i kod, utan faktiskt körs som två fristående system utan någon som helst möjlighet att av misstag läsa varandras data.
Den enda kopplingen: en utkorg
Om systemen inte får dela databas eller anropa varandra direkt, hur vet Ascendra över huvud taget att någon anställts eller slutat?
Svaret är en utkorg: en logg som Elytra bara lägger till händelser i, och som aldrig ändras eller rensas i efterhand. Elytra publicerar fyra typer av händelser dit:
employee.createdemployee.updatedemployee.activatedemployee.terminated
Ascendra läser aldrig Elytras databas. Den läser bara den här loggen, med jämna mellanrum, och kommer ihåg var den senast slutade läsa med hjälp av en markör som pekar på den senaste händelsen Ascendra redan hämtat. Nästa gång Ascendra läser loggen hämtar den bara det som tillkommit sedan dess. Utifrån de här händelserna bygger Ascendra upp sin egen bild av varje identitet, helt oberoende av hur Elytra internt lagrar samma information.
Det här mönstret kallas ofta händelsestyrd integration: den ena sidan publicerar fakta om vad som hänt, den andra sidan läser av och tolkar dem själv, händelse för händelse. Ingen av sidorna behöver veta hur den andra är byggd inuti.
Varför inte bara ett direktanrop?
Det enklaste alternativet hade varit att låta Elytra anropa Ascendra direkt när något ändras, ett vanligt direkt anrop mellan systemen. Jag valde bort det av några skäl:
- Otät koppling. Om Elytra måste veta hur man pratar med Ascendras gränssnitt, är systemen inte längre oberoende. Ett ändrat dataformat i Ascendra riskerar att gå sönder på Elytras sida också.
- Motståndskraft. Om Ascendra är nere när en anställning skapas i Elytra, ska det inte spela någon roll. Händelsen ligger kvar i utkorgen tills Ascendra är tillbaka och kan läsa in den.
- En sanning om ordning. Eftersom utkorgen bara växer i en riktning och läses i ordning med hjälp av en markör, kan Ascendra alltid lita på att den ser händelserna i samma ordning som de faktiskt hände, även om den själv legat nere en stund.
Priset för det här är att det uppstår en kort fördröjning innan de båda systemen är helt överens igen: det finns ett litet tidsfönster där Elytra vet något som Ascendra ännu inte hunnit läsa in. I ett HR/IGA-scenario är det en rimlig avvägning. Några sekunders eller minuters fördröjning innan ett nytt konto dyker upp i Entra ID är sällan kritiskt. Att aldrig riskera att en avslutad anställning “försvinner” mellan systemen är desto viktigare, och det är precis vad den periodiska reconciliation som beskrivs i del 3 av den här serien tar hand om.
Vad arkitekturen faktiskt köper
Den här uppdelningen speglar hur många större organisationer redan separerar HR från sin IGA-plattform: HR äger sanningen om vem som är anställd, IGA-plattformen äger vad de anställda faktiskt får tillgång till. Att bygga det själv, i miniatyr, gjorde det tydligt varför mönstret finns: det gör det möjligt att byta ut eller skala respektive system för sig, det gör felsökning enklare eftersom varje händelse är ett spårbart faktum i en logg, och det tvingar fram en tydlig ägandegräns som annars lätt suddas ut i en monolit.
Nästa del i serien går igenom vad som händer på andra sidan av den gränsen: hur Ascendra tar de här händelserna och faktiskt provisionerar riktiga konton i Entra ID via Microsoft Graph.
