Раздел 9 · Backend

Философия границ: линзы · часть 1

senior~30 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Философия границ: линзы · часть 1

Вспомни последний вечный спор в команде: «куда положить этот код?», «выносить нотификации в сервис или рано?», «это один модуль или два?». Спор идёт по кругу, аргументы у всех разумные, решение принимается волевым усилием и через полгода оспаривается заново. Эта пара уроков про то, почему так происходит и как разговаривать иначе.

Разгадка в том, что спорящие смотрят на систему через разные проекции. Один говорит «это один домен» (и прав), второй «но команды две, мы толкаемся на ревью» (и тоже прав), третий «зато данные общие» (и тоже). Граница в системе это не объективный факт, который можно найти, а проекция на конкретную ось. Таких осей около десятка, мы будем называть их линзами, и весь спор «куда положить код» это почти всегда спор о том, чья линза главнее.

Короткий ответ обоих уроков: ничья. Хороший шов совпадает по большинству линз сразу, и оценивать кандидата на границу нужно перебором осей, а не победой самого громкого аргумента. В этой части пять внутренних линз (язык, команды, данные, скорость изменения, отказы), во второй части линзы внешних сил и итоговая методика.

Зачем нужны линзы: анатомия distributed monolith

Сначала анти-пример, который оправдывает весь аппарат. Команда решила вынести из монолита «модуль рекомендаций»: его хотели переписать на другом языке, и это была единственная причина. Линза «технологическая свобода» совпала, остальные нет: домен рекомендаций переплетён с каталогом, владелец у обоих один и тот же отдел, данные читаются из общих таблиц, релизятся изменения всегда парой.

Результат знаком многим и называется distributed monolith: каждая фича теперь требует PR в два репозитория, согласованного деплоя двух сервисов и сетевого вызова там, где был вызов функции. Все минусы микросервисов (сеть, версии, две пайплайны) куплены, ни один плюс (независимость команд, изоляция отказов) не получен, потому что граница прошла поперёк всех осей, кроме одной.

Отсюда рабочее определение: качество шва это число линз, по которым он совпадает. Сильный шов разделяет одновременно два языка домена, две команды, двух владельцев данных и два профиля отказов. Слабый совпадает по одной-двум осям и мстит постоянным «дёрганьем через границу». Senior отличается от middle не знанием паттернов, а привычкой смотреть через несколько линз до того, как резать.

И сразу важная оговорка: линзы работают для любой границы, не только для микросервисов. Папка-модуль в монолите, отдельная схема в БД, отдельная библиотека, отдельный сервис: это одна и та же задача проведения шва, отличается только цена ошибки.

Линза 1 · Язык домена

Классика, знакомая по уроку про DDD: граница там, где слово меняет смысл. Customer в биллинге это запись с tax ID и платёжным методом, Customer в маркетинге это lead с историей кликов. Если две группы людей, говоря одно слово, имеют в виду разное, их модели разойдутся, и одна структура данных на двоих станет полем боя.

Рабочие сигналы этой линзы:

  • эксперты разных отделов постоянно уточняют «ты в каком смысле?»;
  • в таблице растут nullable-колонки, нужные только половине читателей;
  • документация определяет термин через «зависит от контекста».

DDD предложил эту линзу первой и главной, и в этом его ограничение: bounded context отвечает на вопрос «где границы смысла», но молчит про команды, нагрузку и отказы. Здесь она занимает своё место: одна из многих, не королева.

Линза 2 · Команды (закон Конвея)

Мел Конвей в 1968 году сформулировал наблюдение, которое за полвека ни разу не опроверглось: системы повторяют коммуникационную структуру организаций, которые их строят. Четыре команды, пишущие компилятор, сделают четырёхпроходный компилятор. Рут Малан позже добавила следствие, которое стоит выучить дословно: если архитектура системы и архитектура организации конфликтуют, побеждает организация.

Практические выводы линзы:

  • У модуля один владелец. Два владельца на модуль это гонки на ревью, противоречивые решения и параллельные теневые реализации одной фичи. Если у куска кода два хозяина, рассмотри разрез по линии владения.
  • Граница без границы команд иллюзорна. Можно нарисовать идеальные модули, но если все коммитят везде, модули сольются обратно (организация победила).
  • Inverse Conway Maneuver: закон работает в обе стороны, поэтому можно менять структуру команд, чтобы получить желаемую архитектуру. Хочешь отдельный платёжный сервис: сначала выделенная платёжная команда, потом сервис, не наоборот.

Team Topologies дала этой линзе словарь: stream-aligned команды (вдоль потока ценности, владеют фичами end-to-end), platform (внутренняя платформа для остальных), enabling (временно прокачивают другие команды), complicated-subsystem (владеют куском, требующим редкой экспертизы). Полезный тест на разрез: можно ли назвать тип команды, которая будет владеть каждым куском? Кусок без владельца-команды это будущая ничья земля.

Линза 3 · Владение данными

Вопрос линзы: кто пишет в эту таблицу? Здоровый ответ: ровно один модуль или сервис. У каждой сущности один владелец-писатель, остальные либо читают копию (replica, проекция, кеш с TTL), либо ходят к владельцу через API.

Анти-паттерн этой линзы: shared database. Два сервиса пишут в одну таблицу, значит схема таблицы стала публичным API обоих, любой ALTER TABLE требует согласования всех писателей, а инварианты («sold_count не больше capacity») не может гарантировать никто, потому что каждый писатель знает только свои правила. Если два куска системы не могут поделить данные, это сильнейший сигнал, что граница нарисована не там: либо данные должны принадлежать одному из них, либо это на самом деле один кусок.

Заметь, как линза дружит с предыдущими: bounded context из линзы 1 обычно и есть единица владения данными, а команда-владелец из линзы 2 владеет и таблицами своего контекста. Когда три линзы показывают одну и ту же границу, это уже почти вердикт.

Механика организации чтения через границу (репликация, проекции, CDC, outbox) это материал волны про распределёнку; здесь достаточно проектного вывода: чтение через копию легально, запись мимо владельца нет.

дальше после входа

Урок целиком открыт всем, кто вошёл. Войди по своему email, чтобы читать дальше.

Вход пока по приглашению. Если хочешь учиться, напиши Vassiliy.