Моделирование данных
Описать, о чём продукт на самом деле: какие в нём сущности, как они связаны и что о них всегда должно быть верно. Это делается до схемы базы и до API, и всё, что строится дальше, наследует принятые здесь решения.
Почему это важно. Ошибка в модели — самая дорогая в исправлении, потому что к моменту, когда она начинает мешать, в проде уже лежат данные, уложенные по ней. Экран можно перерисовать за полдня; связь, которая должна была быть многие-ко-многим, — нельзя.
Что нужно понимать
- Что представляет собой объект реального мира — независимо от экрана, на котором он показан
- Какие связи один-к-одному, один-ко-многим, многие-ко-многим — и какие из них ещё поменяются
- Что должно быть верно всегда и где именно это правило обеспечивается
- Что хранится, а что вычисляется
- Важна ли история или достаточно текущего состояния
Ключевые темы
Сущности и связи
- Как выделять сущности из предметной области, а не из интерфейса
- Кардинальность связей и цена ошибки в ней
- Связующие таблицы и момент, когда связь заслуживает собственной сущности, потому что несёт данные
- Обязательное и необязательное — и что на самом деле означает здесь null
Ограничения и инварианты
Правила, которым данные обязаны соответствовать, — где бы они ни проверялись.
- Уникальность, внешние ключи, check-ограничения
- Правила, которые может гарантировать база, и правила, доступные только приложению
- Почему одно место, где правило гарантируется, лучше трёх мест, где оно проверяется
Нормализация и её пределы
- Как убрать дублирование, чтобы факт хранился в одном месте
- Когда денормализация — осознанное решение ради производительности
- Как распознать схему, денормализованную случайно
Изменения со временем
- Миграции и то, как писать их, чтобы они отработали на реальных данных
- Мягкое удаление, журнал изменений, история событий
- Версионирование модели, от которой уже зависят клиенты
Уровни
| Уровень | Как это выглядит |
|---|---|
| Junior | Добавляет поля и таблицы так, чтобы заработал экран, который сейчас перед ним. |
| Middle | Выделяет сущности из предметной области, не ошибается в кардинальности и полагается на ограничения, а не только на валидацию. |
| Senior | Предвидит, как модели придётся меняться, пишет миграции, безопасные для боевых данных, и понимает, когда денормализовать намеренно. |
Практика
Для начала
-
Постройте модель от предметной области Возьмите фичу, описанную словами, и выведите из неё сущности до того, как нарисуете хоть один экран.
-
Исправьте кардинальность Найдите связь один-ко-многим, которая по жизни должна быть многие-ко-многим, и переделайте её.
-
Добавьте настоящее ограничение Перенесите в базу правило, которое сейчас проверяется в коде приложения.
Глубже
-
Напишите миграцию, которая не может потерять данные Разделите таблицу или смените тип колонки на уже существующих данных.
-
Смоделируйте историю Превратите то, что хранит только текущее состояние, в то, что умеет ответить на вопрос «а как это выглядело месяц назад».
-
Спроектируйте под двух потребителей Опишите сущность, которой пользуются и мобильное приложение, и админка, так, чтобы ни одно из них её не перекосило.
Проверьте себя
- Как отличить настоящую сущность от поля, которое просто оказалось на экране?
- По каким признакам видно, что связи вот-вот понадобится собственная таблица?
- Какие правила в вашем проекте проверяются в коде, но не гарантируются базой?
- Как вы решаете, хранить значение или вычислять его?
- Когда изменение модели заставляло вас трогать заметно больше кода, чем вы рассчитывали?
- Как сделать изменение схемы безопасным для таблицы на миллион строк?
Ресурсы
- Designing Data-Intensive Applications — книга Мартина Клеппмана и основной источник о том, как на самом деле взаимодействуют модели данных, хранение и согласованность. Одна вторая глава уже окупает потраченное время.
- PostgreSQL: Data Definition — что настоящая база умеет гарантировать за вас. Прочитайте главу про ограничения, даже если никогда не пишете SQL руками.
- Patterns of Enterprise Application Architecture catalog — словарь для разговора о том, как встречаются объекты и таблицы, от Active Record до Data Mapper. Краткое описание каждого паттерна доступно бесплатно.
- Database normalization — простой справочник по нормальным формам. Стоит один раз прочитать внимательно, чтобы потом отличать осознанную денормализацию от случайной.
- Serverpod: database models — как всё это выглядит именно в Dart, включая связи и генерируемый код.