Hoppa till innehåll

Domänhändelser

Domänhändelser är den frikopplingsmekanism som låter funktioner kommunicera utan direkta beroenden. I stället för att funktion A anropar funktion B publicerar funktion A en händelse och funktion B prenumererar på den.

DomainEventBus är en broadcast-buss för publicering och prenumeration inom samma process. Publicister anropar publish(event); prenumeranter konsumerar en typad on<T>()-ström. Varje händelse implementerar DomainEvent och bär occurredAt.

Bussen körs inuti cc_server. Varje publicist och varje levande prenumerant finns där. Ingen klient ser den här bussen. Det en klient ser är ett kurerat urval som servern skickar vidare som notifications/*-ramar i JSON-RPC.

Utan händelser är varje reaktion mellan funktioner en import. Aviseringsvägen skulle behöva importera agentfunktionen, funktionen för pull requests, ärendehanteringen och möten bara för att veta när den ska visa en toast – och var och en av dem skulle då behöva känna till att aviseringar finns. Ärendehanteringen skulle importera pipelines så att ett slutfört ärende kunde föra en körning framåt. Beroendegrafen sluter sig till en knut och den delade kärnan slutar vara delad.

Att publicera en händelse bryter det. Aviseringsvägen prenumererar på tjugoen händelseklasser och importerar ingen av de funktioner som ger upphov till dem. Pipelinetrigger-dispatcharen prenumererar på hela bussen och vet ingenting om pull requests, ärenden eller möten. Observability aggregerar körningsutfall utan att nå in i dispatch.

Kostnaden är den vanliga: en publicist kan inte avgöra om någon lyssnar, och en lyssnare som aldrig konstrueras är tyst och overksam. TicketRemoteSyncHandler är det stående exemplet: den är deklarerad, täcks av tester och kopplas aldrig in i produktion.

Det är värt att vara precis om en sak som bussen inte äger.

Revisionsspåret är inte en projektion av händelseströmmen. Varje muterande RPC-operation granskas som standard – RepoOp.audited har värdet true som standard och en operation avstår bara genom att deklarera audited: false, en avsiktlig handling som syns på själva operationen. Dispatcharen lägger till raden “vem gjorde vad, varifrån” direkt efter ett lyckat anrop. Det finns ingen DomainEventAuditBridge. ActivityLogPersister skriver activity_log-rader från ActivityLogged-händelser; det är ett separat flöde från RPC-granskningen.

Händelsebussen är transporten för aviseringar och för automatisering. Den är inte det bokföringssystem som är den auktoritativa källan.

  • Aviseringar. Tjugoen händelseklasser omvandlas av servern till notifications/*-ramar, spelas in i ett beständigt flöde per arbetsyta och sänds till anslutna sessioner. En ram vars workspace_id anger en arbetsyta som sessionsanvändaren inte är medlem i förkastas på servern; klienten filtrerar sedan resten till den arbetsyta den tittar på.
  • Händelsedriven automatisering. Pipelinetriggers prenumererar på bussen och startar automatiskt matchande pipelines (se nedan).
  • Livscykelreaktioner. Att skapa en arbetsyta seedar dess VD och specialister, de inbyggda pipelinemallarna och start-evalsviterna. En slutförd agentkörning återupptar ett pausat pipelinesteg, stänger en uppgift, matar målövervakaren, kan väcka en namngiven kontrollant och kan trigga en ledare på nytt. En sammanslagen PR eller en borttagen yta utlöser skräpsamling av worktrees.
  • Synk mellan leverantörer. Lokala ärendeförändringar skickas ut till konfigurerade leverantörer av en koordinator som lyssnar på fem ärendehändelser.

Händelserna omfattar hela produkten. På en övergripande nivå:

  • Arbetsytor, agenter och repos – skapande av arbetsyta, slutförd körning, registrering av repo, uppdateringar av skills
  • Pull requests – publicering, statusändringar, sammanslagningar, granskningsbegäranden, omnämnanden, bevakning av egna PR:er, PR:er som upptäckts externt
  • Meddelanden – meddelanden, ytor och förlopp för etablering av ytor
  • Ärenden – skapa, tilldela, omfördela, detaljer, status och de tre slutliga utfallen
  • Uppgifter – sekvenserade livscykelramar för en dispatchad körning, så att klienter kan sortera och avdubbla dem
  • Pipelines – endast avslutande körningshändelser (slutförd, avbruten, misslyckad)
  • Observability – poster i aktivitetsloggen och budgettrösklar
  • Identitet och medlemskap – medlem tillagd, borttagen, roll ändrad
  • Kalender och möten – autentisering som löper ut, möten som startar och inspelningar som avslutas
  • Stationer – övertagande, upprensning och oväntad stängning

Minne, orkestrering, plandokument, artefakter, godkännanden, intag av ärendesynk, kalenderuppdatering, sammanslagning av worktrees, skapande av användare, inbjudningar och återkallning av enheter är inte domänhändelser; se referensen.

Den fullständiga katalogen – alla 53 konkreta händelseklasser, deras nyttolaster och de 24 prenumeranter som konsumerar dem – finns i referensen för domänhändelser. Den sidan är inventariet; den här är modellen.

Behörighetskontroll i realtid vilar på två olika mekanismer

Avsnitt med rubriken “Behörighetskontroll i realtid vilar på två olika mekanismer”

Medlemskaps- och enhetshändelser ser symmetriska ut men är det inte, vilket spelar roll om du resonerar om hur snabbt en återkallning får effekt.

Att återkalla en enhet avslutar verkligen dess sessioner. Servern bevakar tabellen över parkopplade enheter och avslutar alla aktiva sessioner vars enhet har lämnat den aktiva mängden, inom några sekunder.

Att ta bort en medlem stänger inte socketen. WorkspaceMemberRemoved konsumeras: LocalRpcServer och RemoteRelayHost släpper användarens anslutna arbetsyteprenumerationer, och RemoteEventForwarder ogiltigförklarar sin medlemskapscache så att fler ramar för den arbetsytan inte skickas. Det som nekar nästa RPC är fortfarande rollgrinden, som löser upp medlemskapet på nytt vid varje anrop – så deras nästa begäran avvisas med “Not a member of this workspace”. Effekten är omedelbar för allt de försöker göra; den uppnås bara inte genom att lägga på luren.

Pipelinetriggers prenumererar på domänhändelser och startar automatiskt matchande pipelines. PipelineTriggerDispatcher lyssnar på hela bussen och gör sedan följande:

  1. Avbryter tidigt om inte händelsetypen är en som den vet hur den ska mappas till en nyttolast
  2. Slår upp aktiverade triggers som matchar den händelsetypen, i varje arbetsyta
  3. Filtrerar var och en till sin egen arbetsyta och tillämpar sitt matchningsfilter för nyttolasten
  4. Startar en körning för varje trigger som återstår

Den viktiga begränsningen är steg 1. Bussen bär 53 händelsetyper; väljaren för att lägga till trigger startar en körning för dessa fjorton (grenar i EventPayloadMapper för nyttolast): ExternalPrDetected, PullRequestPublished, PullRequestStatusChanged, PrMerged, MessageReceived, TicketAssigned, TicketCompleted, TicketFailed, TicketCancelled, BudgetThresholdCrossed, RepoAdded, MeetingRecordingStopped, SkillUpdated och SpaceDeleted. Inget annat kan starta en pipeline.

RemoteEventForwarder översätter händelserna i aviseringsklassen till ramar och skickar dem över RPC till anslutna sessioner, där en rammappare gör dem till aviseringar i appen. En tunn klient ser därför en projektion av serverns händelseström utan att äga någon körning – vilket är hela poängen med uppdelningen med tunn klient.

Men inte varje skickad ram är en avisering. Strömmen för uppgiftslivscykeln och omfördelning av ärenden vidarebefordras live för att driva gränssnittet, men de spelas inte in i aviseringsflödet och visar ingen toast.