title: Статья 04 — Модели данных и выбор хранилища source: подготовлено 2026-09-17, TASK-45.44; каркас — brief.md §04; развёртка knowledge-base.md §3–3.1 (мосты в §5–6); источники — DDIA 2-е изд. гл. 3–4 (TASK-45.39), Alex Xu гл. 5–6 (TASK-45.38), эталоны classic-designs.md §1–3
Статья 04: модели данных и выбор хранилища
Четвёртая статья серии — про главный контент этапа 4 («детали + БД», 18 минут секции): какие классы хранилищ существуют, как выбирать между ними и как они устроены внутри. Критерий «умею, если» из брифа: для любого кейса называю класс хранилища + 2 аргумента за и 1 против. Обратите внимание на форму критерия: не «назвать продукт» и не «назвать класс» — назвать класс и обосновать трейдоффом. «Возьмём Cassandra» без гарантий и критериев — ошибка №5 методички, и это самый частый способ провалить именно этот этап.
Место раздела в каркасе: статья 03 (03-scaling.md) закончилась словами «стейт — в правильных местах» — эта статья отвечает, какие это места; статья 05 (05-replication-sharding.md) — как выбранные хранилища переживают рост и отказы. Связка с соседями: KV-класс и кэш — родня, но кэш как слой — отдельный раздел 06; событийная синхронизация проекций — материал раздела 07; гарантии согласованности, которыми мы платим за выбор — раздел 09; поведение хранилищ при отказах — раздел 08. Глубина — ../knowledge-base.md §3–3.1; первоисточник обеих половин статьи — DDIA гл. 3 (модели) и гл. 4 (движки).
1. Что на самом деле выбираем: модель, гарантии, масштаб
Вопрос «SQL или NoSQL?» поставлен неправильно — и это первая фраза, которую стоит произнести на секции. Выбираем не лагерь, а пакет из трёх решений:
- Модель данных — в какой форме живут данные: таблицы со связями, документы-агрегаты, пары ключ-значение, граф, столбцы. Модель определяет, какие запросы естественны (один оператор), а какие — мучительны (ручные циклы в приложении). DDIA: приложение — это слои моделей (объекты кода → модель данных → байты на диске), и каждый слой задаёт словарь следующего.
- Гарантии — транзакции, изоляция, целостность: строгие у реляционных СУБД (ACID, schema-on-write, ограничения и внешние ключи), ослабленные у большинства NoSQL (eventual consistency, schema-on-read). Это мост в раздел 09.
- Масштаб и характер нагрузки — read или write, точечные или range-запросы, какой объём и RPS: здесь выбор решают не «религии», а устройство движков (§5) и данные оценок (статья 02).
Классическая пара трейдоффов внутри «модели»: schema-on-write против schema-on-read. Реляционная схема — это защита: испорченную запись в неё не вставить, ошибка ловится в момент записи. Гибкая схема документных БД — это скорость эволюции, но ошибки структуры переносятся в рантайм и становятся ответственностью приложения. Формула ответа: «гибкую схему беру там, где данные реально вариативны (каталог, контент, события разных типов), и не беру там, где домен чёткий и за данные платят деньгами».
И обязательная оговорка про конвергенцию: противостояние давно размылось — в PostgreSQL живёт полноценный JSONB, Mongo научился multi-document-транзакциям, managed-слои приносят шардирование в SQL. Поэтому «у нас реляционная СУБД» больше не означает «не масштабируется», а «у нас NoSQL» не означает «нет транзакций». Спорят не тезисы, а конкретные трейдоффы — их и называем.
2. Классы хранилищ: карта с критерием «2 за — 1 против»
Десять классов, которыми описывается почти любая схема. Для каждого — представители и формат брифа: два аргумента за, один против.
| Класс | Представители | За ×2 | Против ×1 | Кейсы |
|---|---|---|---|---|
| Реляционная | PostgreSQL, MySQL | ACID-транзакции и ограничения целостности; сложные запросы и JOIN, зрелость экосистемы | масштаб записи упирается в одну ноду, шардинг — руками | деньги, заказы, связи сущностей, источник правды; до единиц ТБ |
| Документная | MongoDB, Couchbase | агрегат «документ = объект домена» читается одним куском (локальность); гибкая схема | JOIN и связи между агрегатами слабые, транзакции между документами — боль | профили, каталоги, контент с вариативной структурой |
| Key-value | Redis, DynamoDB | O(1) по ключу, гигантский RPS на ноду; горизонтальный масштаб и TTL | нет JOIN и сложных запросов; Redis — память (дорого, ограничен объём) | сессии, счётчики, фичи-флаги, кэш, hot path по известному ключу |
| Wide-column | Cassandra, HBase, Bigtable | write-throughput на последовательных записях (LSM); равномерное партиционирование по ключу, линейный масштаб | модель заточена под запросы по ключу партиции; ad-hoc-запросы и точечные обновления — плохо | приём событий, истории сообщений, write-heavy временные ряды |
| Колоночная (аналитика) | ClickHouse, BigQuery | агрегации по миллиардам строк со сканом столбцов; сжатие в разы | не для OLTP: нет точечных UPDATE, транзакции слабые | логи, аналитика, отчёты, event-store «для чтения» |
| Поисковая | Elasticsearch, OpenSearch | инвертированный индекс: полнотекст, морфология, fuzzy, фасеты | не источник правды — eventual-проекция; дорогая запись | поиск по тексту, автодополнение, лог-поиск |
| Time-series | Prometheus, VictoriaMetrics, InfluxDB | retention и даунсемплинг из коробки; быстрые оконные агрегаты | специализированная модель — только время как ось | метрики, мониторинг, телеметрия |
| Графовая | Neo4j | index-free adjacency: траверсы связей N-го порядка за постоянное время на шаг | нужна редко; массовые обходы большого графа дороги | соцграф-запросы, рекомендации, fraud-цепочки |
| Объектная | S3, GCS, MinIO | почти бесконечный масштаб и дешевизна; durability 11 девяток | высокая латентность, нет запросов по содержимому | медиа, бэкапы, стейджинг для batch |
| Event log | Kafka | append-only лог: реплей истории, retention, десятки МБ/с на партицию | не queryable, порядок гарантирован только внутри партиции | интеграция сервисов, event sourcing, конвейеры |
Три уточнения, которые отличают уверенный ответ:
- Wide-column и «колоночная» — разные классы, несмотря на похожие слова. Wide-column (Cassandra) — про запись потока строк с гибким набором колонок, семейство KV; колоночная (ClickHouse) — про чтение агрегатов, хранение по столбцам. «Колоночный» в первом случае — модель строки, во втором — физическая раскладка байтов.
- KV и wide-column — ближайшие родственники: оба про «ключ → данные», оба на LSM-движках (§5). Отличие в модели: у KV значение непрозрачно, у wide-column внутри ключа — отсортированный набор колонок, что даёт range-запросы внутри партиции (история диалога — канонический кейс).
- Графовая БД — самая редкая покупка. Запрос «друзья друзей» в реляционной СУБД — один JOIN и работает нормально; графовая оправдана, когда связи — сами данные и глубина траверсы не ограничена (рекомендации, цепочки мошенничества). На секции честнее сказать «соцграф держу в шардированной реляционной по user_id, а глубинные обходы — отдельно» — это и есть трейдофф-мышление, а не подбор продукта под слово из задания.
3. Таблица «кейс → хранилище»
Сводная таблица для этапа 4: типовой кейс из секций уровня «инстаграм/твиттер» → класс → чем обосновываем. Формат каждой строки — готовый ответ по критерию: класс + за + против.
| Кейс в дизайне | Класс / представитель | Два «за» | Один «против» (и что платим) |
|---|---|---|---|
| Заказы, платежи, кошельки | Реляционная, PostgreSQL | ACID + ограничения целостности; JOIN и сложные отчёты | write-масштаб одной ноды — шарды руками (мост в 05) |
| Сессии, фичи-флаги, rate-limit счётчики | KV, Redis | O(1) и TTL из коробки; сотни тысяч RPS на ноду | данные в памяти: дорогой объём, обязательны TTL и eviction |
| Кэш лент, hot-путь чтения | KV, Redis-кластер | латентность в микросекундах; consistent hashing масштабирует кластер | кэш может врать — нужна инвалидация (раздел 06) |
| Профиль пользователя, каталог товаров | Документная, MongoDB | агрегат одним чтением; гибкая схема под вариативные поля | связи между агрегатами и JOIN — в приложении |
| Посты, статьи (источник правды) | Реляционная, шардированная по росту | целостность и предсказуемые запросы; 70 ТБ/год — шардами живут спокойно | горячий путь чтения мимо SQL — нужен кэш-слой (06) |
| Истории сообщений чата | Wide-column, Cassandra (или реляционная по conversation_id) | write-heavy на LSM; range по времени внутри партиции диалога | вторичные запросы мимо ключа партиции — scatter-gather |
| Приём событий, логи, метрики приёма | Wide-column / колоночная, Cassandra / ClickHouse | последовательная запись на LSM; сжатие столбцов | нет точечных UPDATE, спайки компактации (§5) |
| Аналитика и отчёты по миллиардам строк | Колоночная, ClickHouse | скан и агрегат столбцов с сжатием; SQL-совместимость | не для точечных обновлений — питаем из конвейера |
| Полнотекстовый поиск, автодополнение | Поисковая, Elasticsearch | инвертированный индекс: морфология, fuzzy, фасеты | проекция с лагом и дорогой записью — не источник правды |
| Метрики мониторинга продукта | Time-series, VictoriaMetrics | retention/даунсемплинг автоматом; оконные агрегаты | специализированная модель — не кладём туда домен |
| Фото, видео | Объектная, S3 + CDN | дешевизна и durability; разгрузка всего остального | латентность — лечим CDN; метаданные — отдельно в RDBMS |
| Интеграция сервисов, fan-out публикации | Event log, Kafka | реплей и retention; порядок в партиции | не queryable — читающие проекции строим отдельно |
Правило чтения таблицы: один и тот же сервис почти всегда занимает несколько строк — это нормально и правильно. Лента из эталона — пять хранилищ сразу (§7).
4. Источник правды один: полихранилищная архитектура
Как только хранилищ становится больше одного, появляется главный риск этапа 4 — и ошибка №10 методички («не рассмотреть частичные отказы хранилищ»). Каноническая архитектура, которой отвечаем:
Источник правды один — реляционная СУБД. Всё остальное — производные проекции с известным лагом.
- Проекция устарела или потерялась → она перестраивается из источника (пересев кэша, реиндекс поиска, пересчёт read-модели). Деградация латентности, не потеря данных — это витрина раздела 08.
- Синхронизация проекций — не «как-нибудь реплицируется», а событийная: outbox-паттерн + CDC (транзакция в источнике пишет событие в тот же коммит, конвейер раскладывает по проекциям; идемпотентность потребителя обязательна — мост в раздел 07, гарантии — в 09).
- Лаг каждой проекции называется числом: поиск отстаёт на секунды, кэш — на миллисекунды, аналитика — на минуты. «Известный лаг» — это SLO, а не приписка.
Формулировка на секцию целиком: «источник правды — PostgreSQL: деньги и связи; hot-путь — Redis-зеркало с инвалидацией по событиям; поиск — Elasticsearch, реиндексируется из источника; аналитика — ClickHouse, питается конвейером. Расхождение проекций ловим мониторингом лага, восстанавливаем пересевом из источника». Одна фраза — и видно, что этап 4 у вас в руках.
5. B-tree vs LSM: «а как внутри вашей БД?»
Вопрос-зонд этапа 4, отличающий «назвал продукт» от «понимаю хранилище». Оба семейства — OLTP-движки, ответ держится на одной антитезе: обновлять на месте против писать только добавлением.
5.1 Прародитель: append-only лог
Простейшая БД — файл, в который записи дописываются в конец (пример DDIA: две bash-функции db_set/db_get). Запись O(1) и молниеносная (последовательная), но чтение — линейный скан. Вся история движков — ответ на вопрос «как искать по логу быстро»: hash-индекс в памяти (нет range), а дальше — два великих ответа: LSM и B-tree.
5.2 LSM-tree: запись как последовательные сегменты
LSM (RocksDB, Cassandra, HBase, LevelDB): запись попадает в memtable (отсортированная структура в памяти) → при заполнении сбрасывается на диск как SSTable — иммутабельный отсортированный файл со sparse-индексом и компрессией блоков → фоновая компактация сливает сегменты как mergesort. Удаление — tombstone (маркер, зачистка при компактации); Bloom filter отсекает чтения отсутствующих ключей. Стратегии компактации: size-tiered (сливаем равные свежие) vs leveled (уровни по размеру) — компромисс write-амплификации и пространства.
Что получаем: запись — только последовательная, отсюда высокий write-throughput и дешёвая компрессия. Что платим: чтение старого ключа может требовать обхода нескольких SSTable; range-запросы слабее (Bloom диапазоны не ускоряет); компактация конкурирует за IO с запросами → латентность-спайки и backpressure при недогоне.
5.3 B-tree: страницы и обновление на месте
B-tree (PostgreSQL, MySQL, почти вся классика): данные в страницах фиксированного размера (4–16 КиБ), обновление на месте, поиск за O(log n) — на практике глубина 3–4 уровня, чего хватает на сотни терабайт при ветвлении ~500. Заполнение страницы → page split; надёжность записи — WAL: сначала журнал с fsync, потом страница. Вариант copy-on-write (LMDB) даёт снапшоты без блокировок.
Что получаем: предсказуемое точечное чтение (всегда 3–4 уровня) и сильные range-запросы. Что платим: write-амплификация — одна логическая запись тянет WAL + перезапись страницы; случайные записи дороже последовательных.
5.4 Сравнение и выбор
| LSM | B-tree | |
|---|---|---|
| Запись | быстрее: только последовательные сегменты, ниже write-амплификация | дороже: WAL + страница целиком на месте |
| Чтение | старый ключ — несколько SSTable; спайки от компактации | предсказуемо: 3–4 уровня страницы |
| Range | слабее (Bloom не помогает) | сильно (лист — отсортированный диапазон) |
| Компрессия | отличная (иммутабельные сегменты) | скромнее |
| Фон | компактация ест IO → джиттер латентности | ровнее, но page split + WAL |
| Где живёт | Cassandra, HBase, RocksDB, LevelDB, ClickHouse-семейство | PostgreSQL, MySQL, большинство RDBMS |
Нюанс, который любят слушать: на SSD последовательная запись всё равно быстрее случайной (erase block ~512 КиБ, GC контроллера), а write-амплификация изнашивает flash — поэтому LSM на flash-нодах особенно уместен.
Формула выбора: write-heavy поток (приём событий, истории, метрики) → LSM-семейство; читающий профиль с range-запросами и транзакциями (домен, деньги) → B-tree-классика. И связка с уже выбранными классами: это не два независимых решения — «Cassandra» из §2 автоматически означает LSM с его спайками, «PostgreSQL» — B-tree с его записью. На секции фраза «выбор класса у меня совпал с выбором движка: write-heavy кейс → LSM-семейство» закрывает зонд одним махом.
6. Индексы: платим записью за чтение
Индекс — любая дополнительная структура, которая существует только чтобы ускорить чтение, оплаченная замедлением записи и местом. Отсюда оба правила разом: индексы строим под access pattern (иначе зачем платим) и не строим «на всякий случай» (каждый лишний — налог на каждый INSERT/UPDATE).
Минимальный словарь, который нужен на секции:
- Кластерный (первичный) — сам порядок хранения данных: по ключу строки лежат физически (InnoDB — кластерный по PK). Определяет, что range-запрос по этому ключу читает последовательно.
- Вторичный — отдельная структура: ключ индекса → ссылка на строку. Hash — только equality-lookup; B-tree — и точечные, и диапазоны, и сортировку. По умолчанию в RDBMS — B-tree.
- Составной — по нескольким колонкам с правилом крайнего левого префикса: индекс
(user_id, created_at)обслуживает «все посты пользователя по времени», но не «всё за сегодня по всем пользователям». Это правило прямо отвечает, почему индексы проектируют от запросов. - Покрывающий — содержит все колонки запроса: чтение не идёт к строке вообще (index-only scan). Честная цена: индекс толще, запись дороже.
В распределённых системах (мост в статью 05, там развёрнуто) вторичные индексы делятся на local — каждый шард ищет сам, запрос по всем шардам (scatter-gather, дорого) — и global — индекс сам пошардирован по ключу индекса, поиск в один прыжок, но отстаёт от записи и страдает от hot spot'ов. Формула та же: «частые вторичные запросы → global ценой лага; редкие → local + scatter-gather».
И связка с лестницей масштабирования статьи 03: индексы — первая ступень, дешевейший способ ускорить чтение до всяких реплик, кэшей и шардов. «Сначала посмотрел планы и индексы» — зрелая фраза; «сразу поставим кэш» — нет.
7. Прикладное: лента и чат — критерий брифа
Применяем всё выше к двум сервисам-эталонам (../classic-designs.md §2–3). Это готовый скелет этапа 4: каждое хранилище схемы — классом, представителем и обоснованием.
Лента новостей (инстаграм-класс) — пять хранилищ, каждое по своей строке таблицы §3:
- Посты — реляционная, шардированная при росте (70 ТБ/год — оценка из статьи 02): источник правды, целостность, запрос «пост по ID» — по ключу шарда. Против — горячий путь чтения мимо SQL: поэтому дальше кэш.
- Ленты — KV, Redis-кластер по user_id через consistent hashing: лента = ключ
user_feed:<id>, микросекунды на горячем пути. Против — кэш может врать: инвалидация по событиям (раздел 06), пересев из источника. - Соцграф — отдельная реляционная БД по user_id: горячий запрос постинга «подписчики автора» — одним шардом; глубинные рекомендации не на горячем пути (там, если дойдут, — графовое хранилище или офлайн-обходы).
- Медиа — S3 + CDN, метаданные — в той же реляционной БД постов.
- Поиск и статистика — Elasticsearch как проекция + ClickHouse для аналитики, питание через outbox/CDC (§4).
Формула этапа 4: «пять хранилищ — потому что пять access pattern'ов; источник правды один, остальные — проекции с лагом».
Мессенджер — спорный кейс, на нём показывают зрелость:
- Сообщения — write-heavy история с range-чтением «диалог по времени» → либо wide-column (Cassandra: партиция = conversation_id, история внутри партиции, LSM тянет поток записей), либо реляционная с шардами по conversation_id (транзакции и предсказуемость ценой ручного шардинга). Называть оба варианта и трейдофф — сильнее, чем выбрать один: LSM даёт запись, но спайки и слабые вторичные запросы; B-tree даёт транзакции, но write-масштаб строим шардами.
- Онлайн-доставка — не хранилище вовсе: stateless-шлюзы + реестр
user_id → gateway_idв Redis с TTL (отдельно от истории); непрочитанные/счётчики — Redis (TTL, O(1)).
8. Ошибки этого раздела
Подмножество топ-10 ошибок (../methodology.md §4), относящееся к выбору хранилища:
| Ошибка | Как выглядит | Противоядие |
|---|---|---|
| «Возьмём Cassandra» (ошибка №5) | Продукт назван, гарантии и критерии — нет | Формат брифа: класс + 2 за + 1 против (§1–2) |
| «NoSQL, потому что масштаб» | Без чисел; read-heavy кейс на репликах SQL живёт спокойно | Сначала оценки (статья 02), потом класс; «влезает в одну ноду до единиц ТБ» |
| Одна БД на всё | Поиск LIKE'ом по таблице, аналитика JOIN'ом по миллиардам строк | Полихранилище: класс под каждый access pattern (§3) |
| Проекции без плана синхронизации (ошибка №10) | «Поиск как-нибудь реплицируется» | Источник правды + outbox/CDC + мониторинг лага (§4) |
| «Схема не нужна — гибкость» | Schema-on-read переносит порчу данных в рантайм | Schema-on-write — защита; гибкость только там, где реально вариативно (§1) |
| Redis как основная БД | Память под терабайты, persistence «настроим потом» | KV — hot path и временный стейт; правду держит RDBMS (§2) |
| LSM «быстрее на записи» везде | Не названы спайки компактации и дорогие чтения старых ключей | Таблица §5.4: write-heavy → LSM, читающий профиль → B-tree |
| Индекс на каждый столбец | Запись платит за десяток структур | Индексы под access pattern; составные с левым префиксом (§6) |
| Графовая БД «потому что соцсеть» | Neo4j под «друзья друзей», который живёт в одном JOIN | Глубина траверсы как критерий; соцграф на шардах по user_id (§2, §7) |
9. Что назвать на секции (чек-лист раздела)
Обязательные фразы и действия, по которым видно, что раздел освоен. Полный чек-лист этапов — ../methodology.md §7; канон — ../knowledge-base.md §3.
На этапе 2 (сущности и API): - Каждое хранилище на схеме называть классом и представителем, не просто «БД»: «посты — реляционная, PostgreSQL», «ленты — KV, Redis-кластер». - Уже здесь проговорить роли: «это источник правды, это проекция» — архитектура полихранилища начинается с первого блока, а не с этапа 4.
На этапе 4 (детали + БД) — ядро раздела: - Про каждое хранилище — класс + 2 аргумента за + 1 против. Это формальный критерий брифа; минимум один полный разбор сделать вслух целиком. - «Выбираю не SQL vs NoSQL, а модель данных + гарантии + масштаб» — снять ложную дихотомию до того, как интервьюер её задаст. - Источник правды один (RDBMS), остальные — проекции с известным лагом + как синхронизируются (outbox/CDC) + что при расхождении (пересев из источника, деградация, не потеря). - На зонд «а как внутри вашей БД»: B-tree против LSM одной минутой — memtable/SSTable/компактация против страниц/WAL; вывод «write-heavy кейс → LSM-семейство, читающий профиль → B-tree» + назвать цену каждого (спайки против write-амплификации). - Индексы — от access pattern: «составной (user_id, created_at) под ленту пользователя; покрывающий под горячий путь; каждый индекс — налог на запись».
На этапе 5 (ресурсы): - Объём хранения → «влезает/не влезает в ноду»: единицы ТБ — реплики без шардов; десятки ТБ/год — шардирование (мост в статью 05), с числами-обоснованием из оценок.
На этапе 6 (эксплуатация): - Отказ проекции (кэш, поиск) → деградация латентности, источник жив; отказ источника → failover (мост в 08) — различать эти два класса отказов явно. - «Лаг проекций — метрика с алертом» — синхронизация без мониторинга считается несделанной.
Сквозные формулы раздела: - «Класс + 2 за + 1 против» — формат любого ответа про хранилище. - «Источник правды один, остальное — проекции с известным лагом». - «Выбираю модель + гарантии + масштаб, а не лагерь». - «Индекс — это платим записью за чтение».
10. Самопроверка «умею, если»
Критерий раздела из брифа: для любого кейса называю класс хранилища + 2 аргумента за и 1 против. Проверяется прогоном с закрытыми материалами: взять 5–7 кейсов из таблицы §3 вразброс (или из эталонов: URL shortener, медиа-хостинг) — на каждый 30–60 секунд: класс, представитель, два «за», один «против» с ценой. Затем зонды: «а как внутри вашей БД?» — LSM и B-tree по минуте с выводом о выборе; «почему не всё в одной БД?» — источник правды + проекции + синхронизация за три фразы; индексы — объяснить составной индекс через крайний левый префикс на примере ленты. Для ленты — полный полихранилищный список §7 от руки, пять хранилищ с ролями. Протокол тренировок и рубрика 0–3 — ../methodology.md §6; сверка с эталонами — ../classic-designs.md §1–3. Если на любой кейс ответ идёт по шаблону «класс → за → против» без пауз, а «как внутри» не вызывает ступора — раздел готов.
Связанные документы
00-overview.md— обзорная статья по всем 12 разделам брифа (TASK-45.40)03-scaling.md— соседняя статья: лестница масштабирования, на которой индексы — первая ступень, а выбор хранилищ — куда ложится стейт05-replication-sharding.md— соседняя статья: репликация и шардирование выбранных хранилищ, local/global вторичные индексы в распределённой системе../brief.md— бриф: раздел 04 с критерием «умею, если»../knowledge-base.md— §3 таблица классов и критерии выбора, §3.1 движки (LSM vs B-tree), §2 числа для «влезает/не влезает»../methodology.md— §2 карточка этапа 4, §4 топ-10 ошибок (№5 и №10 отсюда), §6 протокол тренировок, §7 одностраничная карточка../classic-designs.md— §1 URL shortener, §2 лента (пять хранилищ), §3 мессенджер (conversation_id) — эталоны §7../materials/ddia-2ed/ch-03-data-models.md·../materials/ddia-2ed/ch-04-storage-retrieval.md— источники: полный текст гл. 3–4 DDIA 2-го изд.../materials/alex-xu-vol1/ch-06-key-value-store.md— источник: устройство KV-хранилища (§2, §5)- Соседние статьи:
06-caching.md(KV-класс как кэш-слой) ·07-queues-streams.md(outbox/CDC, конвейеры проекций) ·08-fault-tolerance.md(отказ проекции vs отказ источника) ·09-consistency.md(гарантии, которыми платим за выбор)