Поліморфний зв’язок - це тип відношення, коли один запис може бути пов’язаний із записами з різних таблиць через одну універсальну структуру. Простішою мовою, одна таблиця може посилатися на різні таблиці, а не тільки на одну.
Розберемо приклад
Уявимо, що ми створюємо соціальну мережу і в нас є такі сутності, як posts, photos, videos. Нам потрібно реалізувати можливість додавати коментарі до цих сутностей. Найбільш очевидні способи реалізувати таку задачу це:
- Зробити окрему таблицю під кожен тип контенту, наприклад,
post_comments,photo_comments,video_commentsі так далі. - Також можна зробити універсальну таблицю
commentsі звʼязати її з іншими через foreign key. Наприклад,postId,photoId,videoId.
Ці способи робочі, але при додаванні нових типів контенту нам доведеться або створювати нові таблиці в базі даних, або створювати нові foreign keys, що ускладнить бізнес-логіку API і структуру даних в БД.
Популярним рішенням в подібних ситуаціях є поліморфний звʼязок. Суть його в тому, що ми будемо мати одну універсальну таблицю comments з колонками entity_id і entity_type.
- entity_id - ідентифікатор запису, до якого ми додали коментар. Важливо розуміти, що це не foreign key, а просто число чи рядок (якщо використовуємо UUID чи подібний вид ID).
- entity_type - тип контенту, до якого ми лишили коментар. Наприклад,
postчиphoto. Ця колонка потрібна нам для того, щоб розуміти, по якій таблиці робити пошук. Наприклад, ми хочемо вибрати всі коментарі доpostзID=1, і таким чином ми відфільтруємо тільки ті коментарі, що маютьentity_type=post.
Основне, що потрібно розуміти - це те що в світі не буває ідеальних рішень і якщо ви використовуєте поліморфні звʼязки ви отримуєте гнучкість і відсутність дублювання, але жертвуєте наступним:
- Referential integrity - так як в нас відсутні зовнішні ключі, база даних не може гарантувати цілісність даних і звʼязків між ними. Частково цю проблему можна вирішити додаванням
CHECKіPartial Indexes, але це більше костиль, ніж реальне рішення. - JOIN - запити в базу даних стають складнішими і повільнішими через відсутність зовнішніх ключів. Ця проблема стосується систем з великою кількістю даних і інтенсивним навантаженням, тому в таких випадках краще використовувати способи, про які я згадував спочатку.
- CASCADE - так як вони неможливі без зовнішніх ключів, нам потрібно реалізовувати їх на рівні бізнес-логіки, що має прямий вплив на роботу транзакцій, так як порушує консистентність даних
(Consistency, якщо говорити про ACID).