Доменні події
Що таке доменна подія
Розділ із назвою «Що таке доменна подія»Доменні події — це механізм розв’язування залежностей, який дає модулям змогу спілкуватися без прямих залежностей. Замість того щоб модуль A викликав модуль B, модуль A публікує подію, а модуль B на неї підписується.
DomainEventBus — це внутрішньопроцесна широкомовна шина
публікації/підписки. Видавці викликають publish(event); підписники
споживають типізований потік on<T>(). Кожна подія реалізує DomainEvent і
несе occurredAt.
Шина працює всередині cc_server. Кожен видавець і кожен живий підписник
живуть там. Жоден клієнт не бачить цієї шини. Клієнт бачить лише дібрану
підмножину, яку сервер перевидає у вигляді фреймів JSON-RPC notifications/*.
Навіщо взагалі шина
Розділ із назвою «Навіщо взагалі шина»Без подій кожна реакція між модулями — це імпорт. Шляху сповіщень довелося б імпортувати модуль агентів, модуль pull request-ів, тікети й зустрічі лише для того, щоб знати, коли показати спливне сповіщення, — а кожен із них тоді мав би знати, що сповіщення існують. Тікети імпортували б конвеєри, щоб завершений тікет міг просунути запуск. Граф залежностей затягується у вузол, а спільне ядро перестає бути спільним.
Публікація події це розриває. Шлях сповіщень підписується на двадцять один клас подій і не імпортує жодного з модулів, які їх породжують. Диспетчер тригерів конвеєрів підписується на всю шину і нічого не знає про pull request-и, тікети чи зустрічі. Спостережуваність агрегує результати запусків, не зазираючи в диспетчеризацію.
Ціна звична: видавець не може сказати, чи хтось слухає, а слухач, який ніколи
не створюється, залишається мовчки неактивним. TicketRemoteSyncHandler —
постійний приклад: він оголошений, покритий тестами і ніколи не під’єднаний у
продакшені.
Чим шина не є
Розділ із назвою «Чим шина не є»Варто точно визначити одну річ, якою шина не володіє.
Журнал аудиту не є проєкцією потоку подій. Кожна операція RPC, що змінює
стан, за замовчуванням проходить аудит — RepoOp.audited типово має значення
true, а операція відмовляється від аудиту, лише оголосивши audited: false, що
є свідомою дією, видимою в самій операції. Диспетчер додає рядок «хто що
зробив і звідки» одразу після успішного виклику. DomainEventAuditBridge не
існує. ActivityLogPersister записує рядки activity_log з подій
ActivityLogged; це окремий канал, відмінний від аудиту RPC.
Шина подій є транспортом для сповіщень і для автоматизації. Але вона не є системою обліку, що слугує джерелом істини.
Чим шина керує
Розділ із назвою «Чим шина керує»- Сповіщення. Двадцять один клас подій сервер перетворює на фрейми
notifications/*, записує їх у довговічний канал кожного робочого простору й транслює під’єднаним сесіям. Фрейм, у якомуworkspace_idуказує на робочий простір, учасником якого користувач сесії не є, відкидається на сервері; далі клієнт фільтрує решту до того робочого простору, який переглядає. - Автоматизація на основі подій. Тригери конвеєрів підписуються на шину й автоматично запускають відповідні конвеєри (див. нижче).
- Реакції життєвого циклу. Створення робочого простору закладає його агента CEO та агентів-спеціалістів, вбудовані шаблони конвеєрів і стартові набори eval. Завершений запуск агента відновлює призупинений крок конвеєра, закриває завдання, живить супервізора цілей, може розбудити вказаного перевіряча й може повторно запустити лідера команди. Змерджений PR або видалений простір запускає збирання сміття в worktree.
- Синхронізація між постачальниками. Локальні зміни тікетів надсилаються до налаштованих постачальників координатором, який слухає п’ять подій тікетів.
Категорії подій
Розділ із назвою «Категорії подій»Події охоплюють усю поверхню продукту. На високому рівні:
- Робочі простори, агенти й репозиторії — створення робочого простору, завершення запуску, реєстрація репозиторію, оновлення навичок
- Pull requests — публікація, зміни статусу, мерджі, запити на перевірку, згадки, стеження за авторськими PR, PR, виявлені ззовні
- Повідомлення — повідомлення, простори та перебіг підготовки просторів
- Тікети — створення, призначення, перепризначення, деталі, статус і три кінцеві результати
- Завдання — упорядковані фрейми життєвого циклу для одного запуску, щоб клієнти могли їх упорядковувати й дедуплікувати
- Конвеєри — лише кінцеві події запуску (завершено, скасовано, не вдалося)
- Спостережуваність — записи журналу активності та пороги бюджету
- Ідентичність і членство — учасника додано, видалено, роль змінено
- Календар і зустрічі — сплив авторизації, початок зустрічей і завершення записів
- Rigs — перехоплення керування, збирання та неочікуване закриття
Пам’ять, оркестрація, документи планів, артефакти, схвалення, приймання синхронізації тікетів, оновлення календаря, злиття worktree, створення користувачів, запрошення та відкликання пристроїв не є доменними подіями; див. довідку.
Щоб отримати повний каталог — усі 53 конкретні класи подій, їхні корисні навантаження та 24 підписники, які їх споживають — див. довідку з доменних подій. Та сторінка — це інвентар; ця — модель.
Контроль доступу в реальному часі спирається на два різні механізми
Розділ із назвою «Контроль доступу в реальному часі спирається на два різні механізми»Події членства й події пристроїв виглядають симетричними, але такими не є, і це важливо, якщо ви міркуєте про те, як швидко набуває чинності відкликання.
Відкликання пристрою справді завершує його сесії. Сервер стежить за таблицею прив’язаних пристроїв і протягом секунд розриває кожну живу сесію, чий пристрій вибув з активного набору.
Видалення учасника не закриває сокет. WorkspaceMemberRemoved
споживається: LocalRpcServer і RemoteRelayHost відкидають підписки на
робочі простори, прикріплені до цього користувача, а RemoteEventForwarder
анулює свій кеш членства, щоб подальші фрейми для цього робочого простору не
надсилалися. Тим, що відхиляє наступний RPC, усе ще залишається рольова
перевірка, яка заново визначає членство при кожному виклику — тож їхній
наступний запит відхиляється з «Not a member of this workspace». Ефект настає
негайно для всього, що вони спробують зробити; просто досягається він не
розривом з’єднання.
Тригери конвеєрів
Розділ із назвою «Тригери конвеєрів»Тригери конвеєрів підписуються на доменні події й автоматично запускають
відповідні конвеєри. PipelineTriggerDispatcher слухає всю шину, а потім:
- Завершується достроково, якщо тип події не належить до тих, які він уміє зіставити з корисним навантаженням
- Знаходить увімкнені тригери, що відповідають цьому типу події, у всіх робочих просторах
- Фільтрує кожен до його власного робочого простору й застосовує його фільтр збігу корисного навантаження
- Запускає виконання для кожного тригера, що залишився
Найважливіше обмеження — крок 1. Шина несе 53 типи подій; селектор додавання
тригера запускає виконання для цих чотирнадцяти (гілки корисного навантаження
EventPayloadMapper):
ExternalPrDetected, PullRequestPublished, PullRequestStatusChanged,
PrMerged, MessageReceived, TicketAssigned,
TicketCompleted, TicketFailed, TicketCancelled,
BudgetThresholdCrossed, RepoAdded, MeetingRecordingStopped, SkillUpdated
і SpaceDeleted. Ніщо інше не може запустити конвеєр.
Як клієнт усе це бачить
Розділ із назвою «Як клієнт усе це бачить»RemoteEventForwarder перетворює події класу сповіщень на фрейми й надсилає
їх через RPC під’єднаним сесіям, де перетворювач фреймів робить із них
сповіщення в застосунку. Тонкий клієнт бачить лише проєкцію потоку подій
сервера, не володіючи жодним виконанням — і саме в цьому суть
розділення на тонкого клієнта.
Щоправда, не кожен надісланий фрейм є сповіщенням. Потік життєвого циклу завдань і перепризначення тікетів передаються наживо, щоб керувати інтерфейсом, але вони не записуються в канал сповіщень і не показують спливного сповіщення.
Дивіться також
Розділ із назвою «Дивіться також»- Конвеєри та автоматизація: що запускає тригер
- Тікети та делегування: призначення агенту чи команді
- Архітектура: як події вписуються в правило залежностей
- Довідка з доменних подій: повний каталог і його підписники
- Налаштувати тригери конвеєра
- Налаштувати сповіщення