Перейти к содержанию

E-02 · Что вскрыл прогон#

Копится по ходу, применяется на этапе 6. Правки в шаблоны по ходу не вносятся.

Особая задача этого прогона — не только найти новое, но и проверить четырнадцать правок, сделанных по итогам E-01. Поэтому здесь два раздела: находки и отчёт о проверке правок.

После этапа 2 (D-01 v1.1 → ADR-0001)#

№ Находка Где чинить
1 Центральное решение кейса не покрыто ни одним документом. «Материализовать ленту или собирать при чтении» — не выбор хранилища, не границы, не связь в процессе и не кэш. Ближе всего D-03, но материализованная лента не кэш: её потеря не промах, а необходимость пересборки, и требования к долговечности свои. Решение пришлось принять вне метода Новый документ решения: материализация производных данных
2 Системное соотношение чтений к записям не переносится на отдельное хранилище. В системе 100 чтений на 1 запись, а хранилище лент видит перевёрнутую картину: 12 000 чтений против 17 400 вставок в пике. Материализация переносит работу на запись — в этом её смысл. D-01 спрашивает про соотношение, не уточняя, чьё D-01, вопрос 2
3 Объём оказался параметром, а не данностью. Расход памяти линеен по длине материализованной ленты: 1 000 идентификаторов — 320 ГБ, 200 — 64 ГБ. D-01 спрашивает «каков объём», предполагая, что он дан. Здесь он выбирается, и выбор определяется метрикой поведения пользователей D-01, вопрос 3

После этапа 3 (D-02 v3.0 → ADR-0002)#

№ Находка Где чинить
4 D-02 не спрашивает, какая декомпозиция уже есть. Все семь вопросов — о системе, ни одного о её текущем разрезе. Но нулевой вариант «оставить как есть» без этого не сформулировать, а текущий разрез вдобавок сам подлежит проверке дисквалификаторами: здесь он оказался частично по техническим слоям, то есть по анти-форме D-02, раздел 1
5 Порог связности изменений нигде не назван. Документ говорит «посчитать долю», но не говорит, какая доля здорова. 68 % — это плохо или нормально? Пришлось взять 80 % из условий пересмотра ADR-0002 в E-01, то есть из примера, а не из метода D-02, раздел 3
6 Ведущая форма разреза свалила в одну строку поддомен и ограниченный контекст. Это разные вещи и лежат в разных пространствах: поддомен — в пространстве задачи (чем занимается бизнес, что существовало бы и без нас), ограниченный контекст — в пространстве решения (какую модель мы проводим и где кончается её действие). Они часто совпадают, и к этому стремятся, но совпадение — цель, а не данность: устаревшая модель обслуживает два поддомена одним контекстом, и именно это здесь и было (web держал ленту, профили и подписки). Строку надо разделить на две формы с разными условиями применимости D-02, раздел 4
7 Критерий «по языку» не опознаётся без имени. Колонка «по какому признаку режет» говорит «по языку: где термин меняет значение» — это единый язык (ubiquitous language) Эванса, и опора на него — не наблюдение, а метод: граница контекста проходит там, где ломается язык. Читатель, не знавший этого, воспримет критерий как эвристику «слова разные — режем тут». Эванс есть в источниках раздела, но в самом критерии не назван D-02, раздел 4

Находки 6 и 7 — из разбора, а не из прогона. Обе всплыли в обсуждении после этапа 3 и записаны здесь, чтобы не потеряться до этапа 6. Находка 6 к тому же не была записана тогда, когда я сказал, что записал её, — запись появилась только сейчас.

После этапа 4 (D-04 v2.0 → ADR-0003)#

№ Находка Где чинить
8 Между «границей без общей транзакции» и «локальной транзакцией» есть третий класс — два хранилища внутри одного сервиса. Раздача читает граф подписок из PostgreSQL и пишет в Redis: общей транзакции нет, но и границы по списку документа («другой сервис, другая организация, внешний провайдер») тоже нет. Кусок процесса провалился между двумя классами и не был ни разобран, ни отброшен D-04, раздел 1
9 Дисквалификатор «процесс живёт дольше часа» меряет не тот параметр. Самая длинная раздача — около минуты, порог документа в шестьдесят раз дальше, и всё же состояние в памяти здесь недопустимо: по правилу Литтла одновременных раздач около сорока в пике, а автовыкат идёт несколько раз в день, то есть перезапуск всегда попадает в незавершённые. Решает не длительность, а её произведение на частоту: если оно больше единицы, экземпляры есть всегда D-04, раздел 2
10 Пространство восьми ячеек предполагает сагу — разные шаги у разных участников и компенсации. Раздача — размножение одного шага. Координата определяется формально (aeo), но из неё не следует ничего: компенсировать нечего, шаг один, «дирижёр» — это курсор по списку. Настоящие вопросы участка — размер чанка, место курсора, приоритет длинных раздач, порог гибрида — не задаёт ни один из трёх координатных вопросов. Это третий режим работы документа помимо «даёт ответ» и «вне пространства»: применим и бесполезен D-04, раздел 3
11 «Число участников» при хореографии через общую шину считает не то. Наш процесс двухучастниковый, и дисквалификатор «пять и более» не сработает никогда. Но post.created в Kafka соберёт уведомления, поиск, модерацию, аналитику — и вопрос «что происходит после публикации» станет неотвечаемым при неизменных двух участниках. Считать надо независимых потребителей события, а не шаги нашего процесса D-04, раздел 2
12 У проверочного вопроса «действительно ли инициатору нужен ответ» есть третий ответ. Документ предлагает два: «нужен» и «достаточно идентификатора, уведомим потом». Здесь сработал третий — инвариант закрывается на другом пути целиком: И1 «пользователь видит свой пост немедленно» выглядел требованием синхронной вставки, а оказался требованием к пути чтения, где механизм подмешивания и так обязан быть ради гибрида. Синхронный участок не отвергнут — его не существует D-04, раздел 1

Находки 8 и 10 — об одном месте с разных сторон. Восемь ячеек описывают процессы, у которых несколько разных шагов у нескольких участников. Массовая раздача одного шага в такую рамку не ложится ни на входе (её не удаётся классифицировать), ни внутри (координата ничего не даёт). Чинить это правкой формулировок, скорее всего, нельзя — нужен либо явный отвод такого случая, либо отдельное решение под производные данные, которое просит и находка 1.

После этапа 5 (D-03 v2.0 → ADR-0004)#

№ Находка Где чинить
13 Дисквалификатор «устарелость недопустима вовсе» предписывает ровно умолчание. Документ говорит: такие данные «кэшируются с явной инвалидацией и коротким TTL, и это уже не умолчание». Умолчание дословно — «cache-aside + удаление ключа при записи + короткий TTL как страховка». Это те же слова. Либо дисквалификатор лишний, либо умолчание описано неточно; отличить по тексту нельзя D-03, разделы 2 и 3
14 Таблица отклонений молчит о наложении. Она устроена «условие → отклонение → чем платим», по строке на отклонение, и предполагает, что берут одно. Здесь сложились два — локальный кэш и обновление по событию, — и цена одного погасила цену другого: «тратится на данные, которые могли не понадобиться» исчезает именно потому, что кэш локальный и в карте только крупные авторы. Складывать отклонения можно, но цены при этом не складываются, и об этом нет ни слова D-03, раздел 3
15 Применения документа не независимы, а форма записи предполагает, что независимы. Правило «одно узкое место — одно применение» верное, и здесь оно дало два применения плюс кусок, который не кэш вовсе. Но все три лежат на одном пути чтения, делят бюджет памяти и вытекают из одного числа — порога гибрида. Раздел 6 предлагает записывать по пять строк на применение; собрать их пришлось в один ADR, иначе сумма не видна нигде. Документу нужно место для связанных применений D-03, раздел 6
16 У локального кэша цена названа не вся. Таблица отклонений называет одну — рассогласование между экземплярами. Вторая не названа: отказ локального кэша совпадает с перезапуском процесса, а перезапуски здесь не аварийные, а плановые и частые. Экземпляр с пустой картой отдаёт ленту без половины содержимого, и это не инцидент, а штатный выкат. Прогрев обязателен, но из таблицы этого не следует D-03, раздел 3

Находка 1 получила второй случай. Список крупных подписок читателя меняется редко, читается на каждое открытие — и правильный ход не кэш, а материализация рядом с лентой. Это второй кусок E-02, который пришлось решить вне метода тем же способом. Два независимых случая в одном прогоне — достаточное основание, чтобы документ про производные данные перестал быть пожеланием.

Из разбора после этапа 6 — силы дезинтеграции и интеграции#

Источник: Ford, Richards, «Software Architecture: The Hard Parts», гл. 6 (декомпозиция данных) и гл. 7 (гранулярность сервисов). Материал принесён вне прогона; находки ниже относятся к уже написанным документам.

№ Находка Где чинить
17 D-02 не различает модульность и гранулярность, и из-за этого на двух прогонах ответил на два разных вопроса. В E-01 решался вопрос гранулярности — дробить ли вообще (ответ: модульный монолит). В E-02 — вопрос модульности: где провести линию между уже существующими сервисами. Документ один, вопросы разные, и заметить подмену было нечем: в нём нет слов для этой разницы. Раздел 4 тоже смешивает уровни — «по поддомену» это модульность, «по профилю нагрузки» и «по скорости изменений» — гранулярность D-02, разделы 1 и 4
18 У D-02 нет сил интеграции как самостоятельной половины решения. Есть дисквалификаторы (запреты) и оси (сравнение кандидатов), но нет названных сил, тянущих к объединению и взвешиваемых против сил разделения. Конкретно отсутствует «общий код» — ни вопроса, ни оси, ни дисквалификатора. «Отношения в данных» покрыты частично: мы считаем число писателей на сущность, но не связность таблиц внешними ключами, триггерами и представлениями — это другой вопрос. «Рабочий процесс» как довод против разреза не назван: ось «глубина синхронной цепочки» ловит его косвенно и как свойство кандидата, а не как встречную силу D-02, разделы 1–3
19 Декомпозиция данных — отдельное решение, которого в наборе нет. D-01 выбирает хранилище под нагрузку, D-02 — границы сервисов; вопрос «делятся ли сами данные и по каким критериям» не задаёт никто. В E-02 он решался дважды и оба раза внутри чужих документов: перенос графа подписок к ленте (ADR-0002) и раскладка «PostgreSQL под посты и граф, Redis под ленты» (ADR-0001 плюс ADR-0004). У Форда и Ричардса под это шесть сил дезинтеграции и две интеграции — готовый костяк. Находка шире находки 1 и, возможно, поглощает её: материализация производных данных — частный случай декомпозиции данных Новый документ решения; пересечь с находкой 1
20 Архитектурный квант нигде не проверяется, а разные цели по доступности его требуют. Вход E-02 задаёт 99,95 % на чтение ленты и 99,9 % на публикацию; ADR-0002 разводит их по разным сервисам. Но вопроса «лежат ли эти части в одном кванте — общая база, общий выкат, синхронная связь» не задал ни один документ, а без него разные цели недостижимы: части одного кванта имеют общую доступность, какие бы числа ни стояли в требованиях. Это дисквалификатор, а не соображение D-02, раздел 2

Предупреждение об импорте. Списки Форда и Ричардса — перечни соображений, а не дисквалификаторы. Перенести все шесть плюс четыре колонками значит получить ровно то, против чего написан мастер-шаблон: аккуратно заполненную таблицу, в которой ничто не различает варианты. В ADR-0002 ось уже пришлось вычеркнуть по этой причине. Брать надо выборочно — то, что даёт дисквалификатор (архитектурный квант, находка 20) или встречную силу (общий код, рабочий процесс, находка 18), а не ещё один столбец для заполнения.

Отчёт о проверке правок E-01#

Правка Проверена? Как
Три степени достоверности ([замер] / [расчёт] / [нет данных]) ✅ работает На системе с историей преобладает [замер] — ровно наоборот, чем в E-01, где не было ни одного. Обе крайности документ выдержал
[нет данных] блокирует только в различающей оси ✅ сработала и остановила решение Ось «объём и стоимость владения» здесь различает кандидатов. Пустая ячейка заблокировала выбор, метрику выгрузили за час, и решение изменилось: при 320 ГБ выигрывала Cassandra, при 64 ГБ — Redis. В E-01 та же ось была пустой и безвредной
Организационные дисквалификаторы вперёд технических ✅ работает в обе стороны В E-01 сработали три организационных, в E-02 — ни одного. Проверка заняла минуты в обоих случаях
Дисквалификация может оставить один вариант ✅ подтверждена как один из двух режимов В E-01 матрица не понадобилась, в E-02 не сработал ни один дисквалификатор и всё легло на сравнение. Теперь видно, что это два штатных режима работы документа, а не норма и отклонение
Строка «Не решает» в шапке ✅ пригодилась сразу Именно она помогла быстро установить, что материализация — не задача D-01 (находка 1)
Порог реестра ограничений — три-четыре записи ✅ Четыре записи после первого ADR, как и в E-01. Порог верный
[тариф] как местный словарь пометок ✅ Правило «словарь свой у каждого решения» отработало без оговорок

Дополнено после этапа 3#

Правка Проверена? Как
Ось без различий вычёркивается ✅ сработала впервые Транзакционные разрывы одинаковы у всех трёх кандидатов — атомарности нет нигде. Ось вычеркнута из матрицы. В E-01 та же ось была решающей: один и тот же пункт метода в одном кейсе решает исход, в другом удаляется
D-02 сужен до работающей системы ✅ подтверждена, и сильно Раздел 1 впервые заполнился целиком, по 1 400 задачам за полгода. И он опроверг существующий разрез: 68 % задач в одной границе против здоровых 80 %. Никаким рассуждением этого получить было нельзя — только измерением
Разделение D-02 / D-05 ✅ подтверждено Вопросы разные, источники фактов разные, исходы разные. В E-01 дисквалификаторы отсекли саму идею разреза; здесь сработал один, и решение свелось к сравнению по измеренным осям
Поле «самая дорогая будущая граница» ✅ пригодилось Без него связь между ранжированием (роадмап) и стоимостью удаления поста нигде бы не была записана

Ни одна из проверенных правок не потребовала отката. Одна ([нет данных] в различающей оси) не просто сработала, а изменила исход решения — это первый случай, когда правка метода видна в результате, а не только в процессе.

Дополнено после этапа 4#

Правка Проверена? Как
Условие применимости: граница, за которой нет общей транзакции ✅ работает, но список примеров узок Формулировка шире «границ сервисов» и это подтвердилось. Но её примеры — «другой сервис, другая организация, внешний провайдер» — не покрыли пару хранилищ внутри одного сервиса (находка 8). Правка верна, иллюстрация неполна
Разбор процесса на участки перед применением ✅ сработала сильнее, чем в E-01 Процесс распался на четыре куска: один локальная транзакция, один в aec, один не классифицируется, один не существует вовсе — раздача удалений убрана решением уровнем выше. В E-01 правка отсекла половину процесса; здесь она отсекла три четверти
Третье значение оси согласованности («требуется, но недостижима») ⚠️ не проверено Случая не возникло: атомарность здесь не требуется нигде. Первая из четырнадцати правок, которую прогон не подтвердил и не опроверг. Положительный случай был в E-01, но он-то её и породил, то есть проверкой не является
Дисквалификаторы как обязательные работы, а не отказ ✅ подтверждено вторым кейсом В E-01 два дисквалификатора из пяти дали «да, но»; здесь — тоже два. Похоже, это их обычный режим. Стоит сказать об этом в документе прямо: сработавший дисквалификатор чаще добавляет работу, чем закрывает вариант

Откатов по-прежнему нет, но впервые появилась правка, которую прогон не смог проверить — случая для неё не возникло. Две правки в D-03 ждут этапа 5. Новых находок к этому моменту двенадцать, и пять из них про D-04: документ третьего типа встретил процесс той формы, которой не предполагал.

Дополнено после этапа 5#

Правка Проверена? Как
Главная развилка: инвалидация против короткого TTL ✅ сработала в противоположную сторону В E-01 ключ жил 3 мс при 300 записях/с, и умолчание оказалось неработоспособным. Здесь пост неизменяем, ключ живёт сутки и читается сотни раз в секунду — умолчание подошло целиком, без отклонений. Одна строка развела два кейса по разным краям. Лучшей проверки для правки не бывает
Расчёт — достаточное основание, догадка — нет ✅ и в третьем режиме E-01 дал гринфилд, где расчёт единственно возможен. Здесь работающая система, но участок ещё не построен: замеры 900 и 780 мс относятся к схеме, которой после ADR-0001 не существует. Правка выдержала и этот случай
Разделены «короткая форма» и «короткая запись» ✅ и разделение понадобилось Форма короткая, а записей две, и лежат они внутри полного ADR с контекстом и последствиями. До правки это читалось бы как «запись разрослась → проверьте форму» — ложный сигнал, который увёл бы к полной форме без причины
Дисквалификатор про отказ кэша выдавливает решение о деградации ✅ подтверждено вторым кейсом Сработал снова и снова потребовал D-06, которого нет. Находка 17 из E-01 стоит теперь на двух независимых случаях: схема зала в гринфилде и досбор постов в работающей системе
Отличать разрез по профилю нагрузки от решения о кэше ✅ подтверждена задним числом Разница нагрузки чтения и публикации здесь три порядка, и в ADR-0002 из неё не сделали сервис. Почему — видно только теперь: у пути чтения нет своего языка и владельца данных, это два кэша и одна материализация
Слитый раздел «что мерить и когда вернуться» ✅ В обеих записях показатель и есть условие возврата; разносить было бы нечего

Все правки, для которых прогон дал случай, сработали. Откатов нет. Единственная непроверенная — третье значение оси согласованности в D-04: случая «атомарность требуется, но недостижима» в этом кейсе не возникло.