Прописывал ли связи в диаграмме классов?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Прописывал ли связи в диаграмме классов?
Да, я прописывал связи в UML диаграммах классов как часть требований. Это важно для разработчиков. Расскажу, как я это делаю.
Почему связи важны
Без связей разработчики гадают:
- "Таблица User связана с таблицей Order. Это one-to-many или many-to-many?"
- "Если удалить User, что с его Orders? Удалить или оставить?"
- "Какое поле в таблице Order ссылается на User? user_id или owner_id?"
Это приводит к ошибкам в БД и коде.
С связями всё ясно.
Пример: E-commerce система
Диаграмма классов с связями
┌──────────────────────────┐
│ USER │
├──────────────────────────┤
│ - id: UUID (PK) │
│ - email: String │
│ - name: String │
│ - created_at: DateTime │
├──────────────────────────┤
│ + register() │
│ + updateProfile() │
└──────────┬───────────────┘
│
│ 1 пользователь
│ * много заказов
│ (one-to-many)
│ Cascade delete: YES
│ (если удалить User → удалить его Orders)
│
v
┌──────────────────────────┐
│ ORDER │
├──────────────────────────┤
│ - id: UUID (PK) │
│ - user_id: UUID (FK) │ ← ссылка на User
│ - total_price: Decimal │
│ - status: Enum │
│ - created_at: DateTime │
├──────────────────────────┤
│ + cancel() │
│ + trackOrder() │
└──────────┬───────────────┘
│
│ 1 заказ
│ * много товаров в заказе
│ (one-to-many)
│ Cascade delete: YES
│
v
┌──────────────────────────┐
│ ORDER_ITEM │
├──────────────────────────┤
│ - id: UUID (PK) │
│ - order_id: UUID (FK) │ ← ссылка на Order
│ - product_id: UUID (FK) │ ← ссылка на Product
│ - quantity: Int │
│ - unit_price: Decimal │
├──────────────────────────┤
│ + getTotal() │
└──────────┬───────────────┘
│
│ Many товаров
│ Many заказов
│ (many-to-many через таблицу ORDER_ITEM)
│ Cascade delete: RESTRICT
│ (если удалить Product, оставить ORDER_ITEM с price)
│
v
┌──────────────────────────┐
│ PRODUCT │
├──────────────────────────┤
│ - id: UUID (PK) │
│ - name: String │
│ - price: Decimal │
│ - stock: Int │
│ - category_id: UUID (FK) │ ← ссылка на Category
├──────────────────────────┤
│ + updateStock() │
│ + updatePrice() │
└──────────┬───────────────┘
│
│ Many products
│ 1 category
│ (many-to-one)
│ Cascade delete: RESTRICT
│
v
┌──────────────────────────┐
│ CATEGORY │
├──────────────────────────┤
│ - id: UUID (PK) │
│ - name: String │
│ - description: String │
├──────────────────────────┤
│ + updateName() │
└──────────────────────────┘
Типы связей
1. One-to-Many (1:*)
- User → Orders: один пользователь может иметь много заказов
- Обозначение: user_id в таблице Orders
- На диаграмме: стрелка из User в Order
2. Many-to-One (*:1)
- Products → Category: много товаров в одной категории
- Обозначение: category_id в таблице Products
- На диаграмме: стрелка из Product в Category
3. Many-to-Many (:)
- Products ↔ Orders: много товаров в много заказов
- Обозначение: промежуточная таблица OrderItem
- На диаграмме: две стрелки (Product → OrderItem, Order → OrderItem)
4. One-to-One (1:1)
- Когда User имеет ровно один Profile
- Обозначение: user_id UNIQUE в таблице Profile
- На диаграмме: одна стрелка с пометкой "1..1"
Правила каскадного удаления
Cascade Delete (DELETE)
Если удалить User → удалить все его Orders
Когда использовать:
- User удалён → его заказы теряют смысл
- Comment удален → его replies не имеют смысла
Когда НЕ использовать:
- User удален → сохранить его Orders (для истории)
- Product удалён → сохранить OrderItems (для финансов)
Restrict (RESTRICT)
Если Product имеет OrderItems → его нельзя удалить
Когда использовать:
- Product в истории заказов → важно сохранить для аудита
- Удаляй только через soft delete (флаг is_deleted)
Ошибка: если забыть RESTRICT, удалишь Product → потеряешь информацию о том, что был заказан
Set Null (SET NULL)
Если удалить Manager → в таблице Employee поле manager_id = NULL
Когда использовать:
- Employee может не иметь Manager
- Manager уходит → Employee остаются, но без Manager
Как я документирую связи
В диаграмме + в описании
ОРДЕР → ТОВАР
Тип связи: Many-to-Many
Промежуточная таблица: order_items
Поля:
- order_items.order_id (FK → orders.id)
- order_items.product_id (FK → products.id)
Ограничения:
- order_items.quantity >= 1
- order_items.unit_price >= 0
Каскадное удаление:
- Если удалить order → delete all order_items (CASCADDE DELETE)
- Если удалить product → сохранить order_items (SET NULL на product_id или RESTRICT)
Оптимизация:
- Индекс на order_id (для поиска товаров в заказе)
- Индекс на product_id (для поиска в каких заказах этот товар)
Реальный пример из моей практики
Проект: CRM система
Я забыл указать в диаграмме:
- Может ли Contact иметь много Organizations?
- Может ли Organization иметь много Contacts?
- Это many-to-many?
Разработчики гадали неделю, потом спросили меня.
Оказалось:
- Один Contact → работает в одной Organization (1:1 или много:1)
- Но Contact может иметь много Emails, много Phones (1:many)
Я переделал диаграмму, добавил:
Contact ← Organization (many-to-one)
Contact → Email (one-to-many, cascade delete)
Contact → Phone (one-to-many, cascade delete)
Результат: разработчики написали БД правильно с первой попытки.
Инструменты для диаграмм
draw.io (рекомендую)
Быстро рисуешь классы и связи
Прямые стрелки, пометки 1:*, cascade
Экспортируешь в PNG/SVG
Lucidchart
Формальнее, с правильной UML нотацией
Но сложнее и платнее
PlantUML (для текстовых документов)
@startuml
class User {
- id: UUID
- email: String
}
class Order {
- id: UUID
- user_id: UUID
}
User "1" -- "*" Order : has
@enduml
Частые ошибки при рисовании связей
Ошибка 1: Не указать направление
❌ User ←→ Order (непонятно, кто referenced кого)
✅ User ← Order (Order.user_id ссылается на User.id)
Ошибка 2: Забыть промежуточную таблицу
❌ User ↔ Product (many-to-many без таблицы)
✅ User → Order_Product ← Product (через промежуточную таблицу)
Ошибка 3: Не указать cascade delete
❌ User ← Order (непонятно, что случится при удалении User)
✅ User ← Order [CASCADE DELETE]
Ошибка 4: Забыть foreign key
❌ Order таблица, но нет user_id поля
✅ Order таблица с user_id (FK) полем
Вывод
Когда я пишу диаграмму классов, я обязательно прописываю:
✅ Все таблицы (классы) и поля ✅ Первичные ключи (PK) ✅ Внешние ключи (FK) с названием поля ✅ Тип связи (1:*, :, 1:1) ✅ Cascade delete правила ✅ Текстовое описание для неясных связей
Это экономит часы переделок, потому что разработчик сразу понимает, что нужно реализовать.