Системный дизайн кажется искусством, пока не разложен в процедуру. На деле это метод — последовательность шагов, у каждого из которых есть конкретный результат. Эта статья — сам метод; строительные блоки и сквозной пример — в соседних.
Почему нельзя сразу рисовать квадратики
Типичная ошибка: архитектор слышит задачу и сразу начинает рисовать компоненты. Через десять минут на доске красивая схема — и никто не знает, выдержит ли она 10 000 запросов в секунду или 100, зачем там три очереди и что произойдёт, если упадёт база.
Проблема в том, что схема нарисована до того, как заданы вопросы. Метод ниже задаёт их в правильном порядке.
Шаг 1. Функциональные требования
Что система делает: 5–8 главных сценариев словами пользователя. «Отправить уведомление в push и email», «пользователь управляет подписками», «продукт видит статистику доставки».
Важно зафиксировать и анти-требования — чего сознательно не делаем. Без них объём дизайна определяет самый тревожный участник обсуждения: «а что если пользователей будет миллиард?», «а если хотят работать в оффлайне?» — и система разрастается бесконечно.
Результат шага: список сценариев + список того, что выходит за рамки.
Шаг 2. Нефункциональные требования
Это числа и обязательства, из которых вырастает весь дальнейший дизайн:
- Нагрузка: сколько запросов в секунду на запись и чтение, каков пик относительно среднего.
- Объёмы: размер одной записи × количество записей × срок хранения = место на диске; как растёт за год.
- Скорость ответа: целевые показатели на ключевые операции.
- Доступность: что случится при отказе и сколько «девяток» реально нужно (каждая девятка в четыре девятки — это в 10 раз дороже трёх девяток).
- Согласованность данных: где нужна строгая («прочитал то, что только что записал»), где достаточно получить обновлённые данные чуть позже — по каждой операции, не «вообще».
Требования, которые бизнес не назвал, назначаются явно как допущения — и записываются. Проектирование под невысказанные требования — главный источник избыточной сложности.
Шаг 3. Оценки на салфетке
Back-of-envelope — грубая арифметика, которая отсекает целые классы решений ещё до того, как нарисован первый квадратик:
100 млн уведомлений/день ≈ 1200/сек в среднем, пик ×5 ≈ 6000/сек
Запись ~1 КБ → 100 ГБ/день → 36 ТБ/год сырых данных
Чтение истории: 10 млн активных пользователей × 5 заходов ≈ 600 RPS чтения
Точность ±50% достаточна: оценка нужна, чтобы выбрать класс решения (одна PostgreSQL-база / PostgreSQL с партиционированием / отдельное хранилище под историю), а не конкретный сервер. Считать стоит на пике, а не на среднем — системы падают именно на пиках.
Шаг 4. Контракты
API ключевых операций: адреса или события, структуры запросов и ответов, идемпотентность, коды ошибок — на уровне сигнатур, без полной спецификации.
Зачем контракт раньше схемы компонентов: он обнаруживает скрытые требования. «А как клиент узнаёт статус доставки?» — этот вопрос, заданный при проектировании контракта, добавляет в дизайн новую операцию. Если его не задать сейчас, она всплывёт в коде — переделывать дороже.
Шаг 5. Модель данных
Главные сущности, их ключи, объёмы и паттерны доступа. Паттерны доступа — это ответ на вопросы: что читается по ключу, что фильтруется, что агрегируется, что только дописывается. По этим ответам выбирается хранилище, а не по привычке.
Здесь же — владение данными: какая часть системы является источником правды для каждой сущности.
Шаг 6. Схема: компоненты и поток
Только теперь — квадратики. Компоненты, их обязанности, синхронные и асинхронные связи. На каждую стрелку стоит ответить: это синхронный вызов или асинхронное событие? Выбор влияет на поведение при отказах и задержку.
Хорошая проверка готовой схемы — прогнать по ней каждый сценарий из шага 1 пальцем: «запрос пришёл сюда, дальше очередь, дальше...». Сценарий, который не проходит по стрелкам, — дыра в дизайне.
Шаг 7. Горячие точки
В каждой системе есть 1–2 места, где концентрируется сложность: самая нагруженная таблица, компонент с состоянием, точка слияния потоков. Метод требует их назвать и углубиться: ключи шардирования, схема горячей таблицы, поведение очереди при отставании потребителей.
Остальное дизайнится крупными мазками — углубляться везде одинаково означает не углубиться нигде.
Шаг 8. Поведение при отказах
По каждому компоненту: что видит пользователь, когда этот компонент недоступен? Какие операции продолжают работать в упрощённом режиме, какие останавливаются, что накапливается и где?
Здесь важна связь с идемпотентностью: если система умеет повторять запросы при сбое, повторные запросы не должны создавать дубликаты. Это свойство дизайна — его не добавить «потом».
Шаг 9. Эволюция
Не «дизайн на 10 лет», а честная проверка: какое из принятых решений будет больно менять? Такие места стоит защитить интерфейсом; остальное переживёт рефакторинг. Плюс — явный план первой версии: что строим сейчас, что сознательно откладываем.
Принципы, которые удерживают метод
Четыре принципа, нарушение любого из которых обесценивает остальное:
- Дизайн — производная от ограничений. Нет «правильной архитектуры уведомлений вообще»; есть архитектура под 100 тысяч уведомлений в день и под 100 миллионов — и это разные системы.
- Числа решают споры. «Потянет ли PostgreSQL?» — не вопрос мнений: считается на салфетке за две минуты. Любое «кажется, не потянет» обязано превратиться в оценку.
- Простейшее достаточное. Каждый компонент в схеме должен оправдываться требованием, а не привычкой или модой.
- Компромисс называется вслух. Каждое решение что-то получает и чем-то платит; дизайн без списка «чем платим» — реклама, не инженерия.
Коротко
- Нельзя рисовать компоненты до числовых требований — схема не имеет ответа, рассчитана ли она на реальную нагрузку.
- Функциональные требования фиксируют и что делаем, и что сознательно не делаем.
- Оценка на салфетке с точностью ±50% отсекает целые классы решений, считать нужно на пике.
- Контракт API проектируется до схемы компонентов — он обнаруживает скрытые операции.
- Хранилище выбирается по паттернам доступа, не по привычке.
- Схему компонентов проверяют, прогоняя по ней каждый сценарий вручную.
- В каждой системе 1–2 горячие точки — их разбирают глубоко, остальное крупными мазками.
- Поведение при отказах — это часть дизайна, не доработка после запуска.
Что почитать дальше
- Строительные блоки системного дизайна — словарь компонентов, из которого собирается шаг 6.
- Сквозной пример: система уведомлений — весь метод на одной задаче.
- Оформление и защита дизайна — как превратить результат в документ.