# 08. Модель данных

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

- **Author:** Фёдор Чищин
- **Level:** advanced
- **Reading time:** ~25 минут чтения
- **Published:** 2026-05-23
- **Source:** https://fedorthinks.com/ru/learn/software-architect-08-data-model

---

## 1. Главная идея

Модель данных должна защищать смысл бизнеса, а не только удобно обслуживать текущий экран.

### Данные стареют медленнее кода

> Интерьер можно поменять быстро. Неправильно залитый фундамент остаётся с домом надолго.

Фреймворк, UI и даже язык программирования могут смениться. Но таблицы, события, ключи, исторические записи и внешние отчёты продолжают жить. Поэтому data model — одно из самых дорогих архитектурных решений.

Архитектор должен явно понимать инварианты: что всегда должно быть истинно. Например: заказ не может быть оплачен без payment intent; проводка не может исчезнуть; баланс не должен стать отрицательным без разрешённого overdraft.

### Нормализация и денормализация — не религия

> Архив хранит оригиналы документов. Витрина делает копии для быстрого просмотра. Обе формы нужны, но для разных задач.

Нормализация снижает дублирование и риск рассинхрона. Денормализация ускоряет чтение и упрощает read model. В OLTP-системах важны транзакции и консистентность текущего состояния. В OLAP важны аналитические запросы, история и агрегации.

Ошибка — пытаться одной моделью идеально обслужить и checkout, и realtime dashboard, и бухгалтерский отчёт. Часто нужна source-of-truth модель и отдельные projection/read models.

## 2. Пример и антипример

Хорошая модель делает невозможные состояния невозможными или хотя бы явно незаконными.

### Пример: pricing snapshot в заказе

> Чек в магазине хранит цену на момент покупки, даже если завтра товар подорожает.

Order хранит не только `product_id`, но и snapshot цены, валюты, налогов, скидки и названия на момент покупки. Catalog может измениться, но исторический заказ остаётся корректным.

Это небольшая денормализация, которая защищает бизнес-смысл. Без неё отчётность и поддержка клиентов станут зависеть от текущего состояния каталога.

### Антипример: универсальная таблица events_json

> Склад без полок кажется гибким: всё можно положить куда угодно. Потом никто не находит нужное.

Команда складывает все бизнес-сущности в одну таблицу с JSON, потому что «так гибче». Сначала быстро. Потом появляются миграции внутри JSON, неявные схемы в коде, медленные запросы, отсутствие constraints и невозможность понять, какие данные валидны.

JSON полезен для расширяемых атрибутов и событий, но не должен заменять модель там, где есть строгие инварианты.

#### Самопроверка

- Какие инварианты защищает база, а какие только код?
- Какие данные являются source of truth?
- Что будет с историей при изменении справочника?
- Как выглядит безопасная миграция этой модели?

Следующий модуль: [Состояние](./software-architect-09-state).
