Как описать схему базы данных для модели библиотеки с сущностями автор, книга, читатель и связями между ними
Что проверяют
Умеете ли вы увидеть в простой предметной области связи «многие ко многим», задать ограничения на уровне базы и аккуратно отобразить схему в Go. Следом обычно просят написать запрос «какие книги сейчас на руках у читателя» или спрашивают, как выдать второй экземпляр той же книги.
Как отвечать
Сначала задайте вопросы: бывает ли у книги несколько авторов, есть ли в фонде несколько экземпляров. Ответ почти всегда «да», и он меняет схему:
authors:id bigint generated always as identity,full_name text not null;books:id,title,isbn text unique,published_year. Колонкиauthor_idздесь нет;book_authors: связь «многие ко многим», составной первичный ключ(book_id, author_id)и внешние ключи на обе таблицы;book_copies: конкретный экземпляр сinventory_number unique. Выдают экземпляр, а не книгу как произведение;readers:id,full_name,email unique,registered_at;loans:copy_id,reader_id,issued_at timestamptz not null default now(),due_at,returned_at. ЗначениеNULLвreturned_atзначит, что экземпляр на руках.
Инвариант «экземпляр нельзя выдать дважды» держит база, а не Go-код: create unique index on loans (copy_id) where returned_at is null. Проверка в сервисе без такого индекса проигрывает гонке двух запросов.
Со стороны Go: миграции через goose или golang-migrate, доступ через pgx и database/sql, запросы удобно генерировать sqlc. Nullable-поля отображаются в *time.Time, sql.NullTime или pgtype.Timestamptz.
Где спотыкаются
Кладут author_id прямо в books. Соавторов тогда не выразить, и появляются дубли книги под каждого автора.
Сканируют returned_at в обычный time.Time. На первой невозвращённой книге драйвер падает с ошибкой преобразования NULL.
Хранят флаг is_available в books рядом с таблицей выдач. Два источника правды расходятся после первого же сбоя, и библиотека «теряет» книги, которые лежат на полке.