Event Sourcing: негіздері
Event Sourcing дегеніміз не?
Event Sourcing (ES) - бұл домендік нысандардың күйін «ағымдық жол» түрінде емес, барлық оқиғаларды сипаттайтын өзгермейтін оқиға журналы ретінде сақтау тәсілі. Агрегаттың ағымдағы күйі оның оқиғаларын ораумен (replay) алынады, ал оқуға арналған кез келген көріністер осы журналдың үстінен проекция ретінде құрылады.
Негізгі салдарлар:- Тарих - «ақиқаттың бастапқы көзі», жай-күй - тарихтың проекциясы.
- Кез келген жағдайды қайта жаңғыртуға, тексеруге және түсіндіруге болады (аудит).
- Жаңа түсініктер мен талдауларды қосу ескі «суреттерді» көшіруді талап етпейді - оқиғаларды жоғалту жеткілікті.
Негізгі терминдер
Агрегат - нақты инварианттармен (Order, Payment, UserBalance) үйлесімділіктің домендік бірлігі.
Оқиға - өткен ('payment. authorized`, `order. shipped`).
Event Store - агрегат шегіндегі оқиғалар тәртібін қамтамасыз ететін аппенд-онли журналы.
Агрегаттың нұсқасы - соңғы қолданылған оқиғаның нөмірі (optimistic concurrency үшін).
Снапшот - орауды жеделдету үшін жай-күйінің мерзімдік бедері.
Проекция (read-модель) - оқу/іздеу/есептілік үшін материалданған түрі (жиі - асинхронды).
Бұл қалай жұмыс істейді (командалар ағыны → оқиғалар → проекциялар)
1. Клиент ('CapturePayment', 'PlaceOrder') командаларын жібереді.
2. Агрегат инварианттарды валидациялайды және егер бәрі жақсы болса, оқиғаларды тудырады.
3. Оқиғалар Event Store дүкеніне нұсқасын (optimistic concurrency) тексерумен қосылады.
4. Проекция процессорлары оқиғалар ағынына жазылды және read-модельдерін жаңартады.
5. Келесі пәрмен үшін агрегатты жүктеу кезінде жағдай қалпына келтіріледі: снапшот (егер бар болса) → снапшоттан кейінгі оқиғалар.
Оқиға дизайны
Міндетті атрибуттар (ядро)
json
{
"event_id": "uuid",
"event_type": "payment. authorized. v1",
"aggregate_type": "Payment",
"aggregate_id": "pay_123",
"aggregate_version": 5,
"occurred_at": "2025-10-31T10:42:03Z",
"payload": { "amount": 1000, "currency": "EUR", "method": "card" },
"meta": { "trace_id": "t-abc", "actor": "user_42" }
}
Ұсынымдар:
- Атауы: 'domain. action. v{major}`.
- Аддитивтілік: жаңа өрістер - опциондық, ескілердің мағынасын өзгертпей.
- Минимализм: оңай қалпына келтірілетін деректерді қайталамай, тек фактілер.
Келісімшарттар мен схемалар
Схемаларды (Euro/JSON Schema/Protobuf) белгілеңіз және CI сәйкестігін тексеріңіз.
«Сындыратын» өзгерістер үшін - оқиғаның жаңа мажорлық нұсқасы және көші-қон кезеңіне 'v1 '/' v2' қатар жарияланымы.
Бәсекелестік қолжетімділік: optimistic concurrency
Ереже: егер 'expected _ version = = current_version' болса ғана жаңа оқиғаларды жазуға болады.
Жалған құжат:pseudo load: snapshot(state, version), then apply events > version new_events = aggregate. handle(command)
append_to_store(aggregate_id, expected_version=current_version, events=new_events)
//if someone has already written an event between load and append, the operation is rejected -> retray with reload
Осылайша біз бөлінген транзакцияларсыз инварианттардың тұтастығына кепілдік береміз.
Снапшоттар (орауды жеделдету)
Әрбір N оқиға немесе таймер бойынша снапшот жасаңыз.
Храните `snapshot_state`, `aggregate_id`, `version`, `created_at`.
Snapshot кейін оқиғаларды үнемі тексеріп, қуып жетіңіз (тек бедерге ғана сенбеңіз).
Snapshots-ты логтан қайта жасауға болатындай етіп шешіңіз («сиқырлы» өрістерді сақтамаңыз).
Проекциялар және CQRS
ES табиғи CQRS үйлесімді:- Write-модель = агрегаттар + Event Store.
- Read-модельдер = оқиғалармен жаңартылатын проекциялар (Redis карточкалары, іздеу үшін OpenSearch, есептер үшін ClickHouse/OLAP).
- Проекциялар бірдей емес: сол 'event _ id' дегенді қайта өңдеу нәтижені өзгертпейді.
Схемалардың эволюциясы және үйлесімділігі
Additive-first: өрістерді қосыңыз; түрлерін/семантикасын өзгертпеңіз.
Күрделі өзгерістер үшін: оқиғаның жаңа түрлерін шығарыңыз және проекция миграторларын жазыңыз.
Өтпелі кезеңде ('v1' + 'v2') қос жазбаны қолдаңыз және барлық проекциялар дайын болғанда 'v1' дегенді алып тастаңыз.
Қауіпсіздік, PII және «ұмыту құқығы»
Тарих көбінесе сезімтал деректерді қамтиды. Тәсілдер:- Оқиғаларда PII-ні барынша азайтыңыз (деректердің орнына идентификаторлар, егжей-тегжейлер - қорғалған жақтарда).
- Крипто өшіру: өрістерді шифрлаңыз және өшіру сұралған кезде кілтті жойыңыз (оқиға қалады, бірақ деректер қол жетімді емес).
- Жаңартылған оқиғалар: 'user. piiredacted. v1 'проекциялардағы сезімтал өрістерді ауыстырумен (тарих өңдеу фактісін сақтайды).
- Ретенция саясаты: Кейбір домендер үшін оқиғалардың бір бөлігін WORM қоймасында мұрағаттауға болады.
Өнімділік және масштабтау
Партиялану: агрегат ішіндегі тәртіп маңызды - 'aggregate _ id' бойынша партияланыңыз.
Суық бастау: снапшоттар + мерзімді «тығыздау» орамы.
Batch-append: оқиғаларды бір транзакциямен топтаңыз.
Backpressure және DLQ проекция процессорлары үшін; лаг (хабарлардың уақыты мен саны) өлшеңіз.
Event Store индекстеуі: '(aggregate_type, aggregate_id)' және уақыт бойынша жылдам қатынау.
Тестілеу
Агрегаттар үшін Specification tests: «Командалар → Күтілетін оқиғалар» сценарийі.
Projection tests: оқиғалар ағынын көрсетіңіз және материалданған күйді/индекстерді тексеріңіз.
Replayability tests: стендте «нөлден» проекцияларды қайта тексеріңіз - қорытындының сәйкес келетініне көз жеткізіңіз.
Chaos/latency: кідірістер мен қосарларды инжекторлаңыз, ұқсастықты тексеріңіз.
Домен мысалдары
1) Төлемдер
Оқиғалар: 'payment. initiated`, `payment. authorized`, `payment. captured`, `payment. refunded`.
Инварианттар: 'authorized' жоқ 'capture' болмайды; сома теріс емес; валюта өзгермейді.
Проекциялар: «төлем карточкасы» (KV), транзакцияларды іздеу (OpenSearch), есептілік (OLAP).
2) Тапсырыстар (e-commerce)
Оқиғалар: 'order. placed`, `order. paid`, `order. packed`, `order. shipped`, `order. delivered`.
Инварианттар: күй диаграммасы бойынша статустардың ауысуы; 'shipped' дейін болдырмау мүмкін.
Проекциялар: пайдаланушы тапсырыстарының тізімі, мәртебелері бойынша SLA-дашбордтар.
3) Баланстар (қаржы/iGaming)
Оқиғалар: 'balance. deposited`, `balance. debited`, `balance. credited`, `balance. adjusted`.
Қатты инвариант: баланс кетпейді <0; 'operation _ id' пәрмендері ұқсас.
Сыни операцияларды тікелей агрегаттан (қатаң келісу), UI - проекциядан (eventual) оқиды.
Event Store үлгілік құрылымы (ДБ нұсқасы)
events
`event_id (PK)`, `aggregate_type`, `aggregate_id`, `version`, `occurred_at`, `event_type`, `payload`, `meta`
Индексі: '(aggregate_type, aggregate_id, version)'.
snapshots
`aggregate_type`, `aggregate_id`, `version`, `state`, `created_at`
Индексі: '(aggregate_type, aggregate_id)'.
consumers_offsets
'consumer _ id', 'event _ id '/' position', 'updated _ at' (проекциялар мен ретлейлер үшін).
Жиі қойылатын сұрақтар (FAQ)
Барлық жерде ES пайдалану міндетті бе?
Жоқ. ES аудит, күрделі инварианттар, қайталану және деректердің әртүрлі көріністері маңызды болғанда пайдалы. Қарапайым CRUD үшін бұл артық.
«Өзекті жағдай» сұрауларына не істеу керек?
Не проекциядан оқыңыз (жылдам, eventual), не агрегаттан оқыңыз (қымбат, бірақ қатаң). Сыни операциялар әдетте екінші жолды пайдаланады.
Kafka/стрим-брокер қажет пе?
Event Store - ақиқат көзі; брокер оқиғаларды жобалаушыларға және сыртқы жүйелерге тарату үшін ыңғайлы.
«Ұмыту құқығымен» не істеу керек?
PII-ні барынша азайту, сезімтал өрістерді шифрлау және проекцияларда крипто-өшіру/редакцияны қолдану.
Ескі деректерді қалай көшіру керек?
Оқиғаларды ретроспективті генерациялау сценарийін жазыңыз («ре-хайстори») немесе «жай-күй-қалай» деп бастаңыз және оқиғаларды тек жаңа өзгерістер үшін жариялаңыз.
Антипаттерндер
Event Sourcing «әдет бойынша»: домендік пайдасыз жүйені қиындатады.
Fat events: PII және қосарланған үлкейген payload '- тежегіштер және комплаенс проблемалары.
Optimistic concurrency жоқтығы: жарыс кезінде инварианттарды жоғалту.
Қалпына келтірілмейтін проекциялар: реплея/снапшот → қол фикстері жоқ.
Домендік оқиғалар ретінде шикі CDC: БД схемаларының жылыстауы және қатаң байланыс.
Ішкі және интеграциялық оқиғаларды араластыру: сыртқа тұрақтандырылған «витринаны» жариялаңыз.
Өндіруге арналған чек-парақ
- Агрегаттар, инварианттар және оқиғалар (атаулар, нұсқалар, схемалар) анықталды.
- Event Store агрегат пен optimistic concurrency шегінде тәртіпті қамтамасыз етеді.
- Снапшоттар мен оларды қайта құру жоспары енгізілген.
- Проекциялар іспетентті, DLQ және лагтың метрикасы бар.
- Схемалар CI-де валидацияланады, нұсқалар саясаты - құжатталған.
- PII ең аз, өрістер шифрланады, «ұмыту» стратегиясы бар.
- Проекциялардың репликасы стендте тексерілді; апаттық қалпына келтіру жоспары бар.
- Дашбордтар: аппендтің жылдамдығы, проекцияның артта қалуы, қолдану қателіктері, ретрайлардың үлесі.
Жиынтығы
Event Sourcing жүйенің тарихын бірінші сыныпты артефактқа айналдырады: біз фактілерді тіркейміз, олардан жай-күйді шығарамыз және кез келген көріністі еркін жасаймыз. Бұл аудит, өзгерістерге төзімділік және талдау икемділігін береді - схемалардағы тәртіп, бәсекелестік бақылау және сезімтал деректермен сауатты жұмыс жағдайында.