Приходилось ли проектировать таблицы и модели данных с учетом ограничений и констрейнтов
Что проверяют
Считаете ли вы базу местом, где живут инварианты, или надеетесь, что всё проверит код на Java. Ключевая мысль, которую ждут: приложение не единственный, кто пишет в таблицы. Есть второй инстанс сервиса, миграции, скрипты поддержки, соседняя система, и договориться с ними можно только через ограничения в схеме.
Как отвечать
Перечислите ограничения и назначение каждого: первичный ключ, внешний ключ с осознанным поведением при удалении, not null, unique, в том числе составной, check на диапазон и значение по умолчанию.
create table account (
id bigserial primary key,
client_id bigint not null references client(id) on delete restrict,
currency char(3) not null,
balance numeric(19,4) not null default 0 check (balance >= 0),
unique (client_id, currency)
);Свяжите это со своим стеком. Аннотации JPA вроде @Column(nullable = false) и @UniqueConstraint влияют только на генерацию схемы и сами ничего не проверяют, а Bean Validation отрабатывает до похода в базу. Уникальность способом «сначала выбрать, потом вставить» под конкурентной нагрузкой не работает: два потока проходят проверку одновременно. Надёжен только уникальный индекс плюс обработка нарушения целостности в коде. Деньги держите в numeric и BigDecimal, не в числах с плавающей точкой.
Где спотыкаются
Все проверки лежат в сервисном слое, а схема принимает что угодно. Достаточно одной ручной правки или второго инстанса приложения, чтобы в таблице появились данные, которые код считает невозможными.
Второе: каскадное удаление ставят по привычке на все связи. Однажды удаление одной записи уносит половину дерева, и восстановить это можно разве что из резервной копии. Для бизнес-сущностей безопаснее запрет удаления и мягкое скрытие флагом.
Третье: вешают not null или уникальность на большую живую таблицу одной миграцией. Она встаёт на блокировке и кладёт сервис. Правильный порядок другой: колонка без значения по умолчанию, заполнение пачками, затем ограничение, в Postgres через not valid и последующую валидацию.