Sequence Diagram (Диаграмма последовательности)

Урок 4 из 5

Урок 04.04: Sequence Diagram (Диаграмма последовательности)

Цель урока

Научиться проектировать и документировать взаимодействие между компонентами системы с помощью Sequence Diagram. После этого урока вы сможете:

  • Строить диаграммы последовательности для любых сценариев (от простого запроса к БД до распределённой Saga)
  • Понимать, когда и почему Sequence Diagram — главный инструмент аналитика при интеграции
  • Использовать комбинированные фрагменты (alt, opt, loop, par, break, critical, ref)
  • Проектировать асинхронные взаимодействия через очереди сообщений
  • Писать сложные PlantUML-диаграммы для процессов с ветвлениями, параллелизмом и обработкой ошибок

1. Что такое Sequence Diagram?

Sequence Diagram (диаграмма последовательности) — это UML-диаграмма, которая показывает взаимодействие объектов во времени. Время идёт сверху вниз, каждая стрелка — это сообщение, каждый прямоугольник — активация объекта.

1.1. Аналогия

Sequence Diagram — это сценарий пьесы:

Акт 1: Пользователь нажимает «Оплатить»
Акт 2: Фронтенд отправляет POST /api/payments
Акт 3: Бэкенд проверяет баланс
Акт 4: Бэкенд вызывает платёжный шлюз
...

Каждая стрелка — реплика персонажа. Время течёт от поднятия занавеса (начало сценария) до финальных аплодисментов (конец).

1.2. Место Sequence Diagram среди других диаграмм UML

Диаграмма Показывает Когда использовать
Use Case Кто что делает в системе Начало: определить границы и функциональность
Activity Поток работ, ветвления, роли Описать бизнес-процесс или алгоритм
Sequence Взаимодействие объектов во времени Спроектировать интеграцию, API, сценарий
Class Сущности, атрибуты, связи Спроектировать доменную модель
State Machine Состояния объекта и переходы Описать жизненный цикл (заказ: новый → оплачен → отгружен)

Sequence Diagram — мост между архитектурой и кодом. Если Use Case отвечает на вопрос «ЧТО?», то Sequence Diagram — на вопрос «КАК именно?».


2. Почему Sequence Diagram — главный инструмент аналитика при проектировании интеграций?

2.1. Интеграция — это всегда последовательность вызовов

Любая интеграция — это протокол. Протокол — это набор правил: кто, кому, что, когда и в каком порядке говорит. Sequence Diagram — идеальный способ визуализировать этот протокол.

Пример: интеграция фронтенда и бэкенда — это не просто «фронтенд зовёт бэкенд». Это:

  1. Фронтенд отправляет POST /api/v1/orders с телом запроса
  2. Бэкенд проверяет авторизацию (JWT-токен в заголовке)
  3. Бэкенд валидирует тело запроса
  4. Бэкенд проверяет доступность товара на складе
  5. Бэкенд бронирует товар (запрос к Inventory Service)
  6. Бэкенд создаёт заказ в БД
  7. Бэкенд отправляет событие order.created в Kafka
  8. Бэкенд возвращает 201 Created с ID заказа
  9. Фронтенд показывает результат пользователю

Без Sequence Diagram разработчик фронтенда и разработчик бэкенда могут по-разному понять последовательность. Фронтенд может ждать ответ до тайм-аута, а бэкенд — требовать повторного вызова.

2.2. Что даёт Sequence Diagram при проектировании интеграций

Аспект Без Sequence Diagram С Sequence Diagram
Порядок вызовов В голове аналитика, додумывается разработчиком Визуально зафиксирован сверху вниз
Формат ответа Может быть не определён Каждый возврат — стрелка с типом ответа и статус-кодом
Альтернативные сценарии Обнаруживаются на этапе разработки или тестирования Закладываются в alt / opt / break фрагменты
Тайм-ауты Сюрприз production Видны как opt / break с условием тайм-аута
Синхронность/асинхронность Путаница Тип стрелки (→ vs ->>) однозначен
Количество API-методов Оценка «на глаз» Каждая вертикальная линия — потенциальный эндпоинт

2.3. Типичный сценарий: фронтенд + бэкенд

При проектировании новой фичи аналитик делает:

Use Case ──► Sequence Diagram (frontend → backend) ──► OpenAPI spec
                                          │
                                          └──► Тест-кейсы (QA)

Sequence Diagram — это контракт между фронтендом и бэкендом:

Фронтенд говорит:
  «Я отправляю POST на /orders с телом {items, total}»
  «Я ожидаю 201 с {orderId, status: "CREATED"}»
  «На 422 я показываю ошибку валидации»

Бэкенд говорит:
  «Да, я принимаю POST на /orders»
  «Сначала проверяю JWT, потом валидирую, потом создаю заказ»
  «Вот какие ошибки я могу вернуть: 400, 401, 422, 409, 500»

Без Sequence Diagram этот диалог идёт устно или в чате — и неизбежно возникают расхождения.

2.4. Sequence Diagram в Lifecycle проекта

Фаза проекта Как Sequence Diagram помогает
Discovery / Анализ Уточнить сценарий с заказчиком: «Так вы представляете этот процесс?»
Архитектура Согласовать интеграцию между командами (кто что вызывает)
Разработка ТЗ для разработчика: порядок вызовов, типы ответов, обработка ошибок
Тестирование Карта тест-кейсов: каждый alt/opt/break → отдельный тест
Документирование Визуальная документация API для новых членов команды

3. Ключевые элементы Sequence Diagram

3.1. Линии жизни (Lifelines)

Lifeline — это пунктирная вертикальная линия, которая показывает существование объекта во времени. Сверху — прямоугольник с именем объекта.

┌──────────────┐
│   Service    │
├──────────────┤
│   Экземпляр  │
└──────────────┘
       │
       │ (пунктир — время идёт вниз)
       │
       ▼

Типы участников (lifelines):

Тип Нотация PlantUML Когда использовать
Actor (внешний пользователь) actor "Имя" Человек или внешняя система, инициирующая сценарий
Participant (участник) participant "Имя" Любой компонент системы (по умолчанию)
Database (БД) database "Имя" База данных, хранилище
Entity (сущность) entity "Имя" Доменная сущность
Control (контроллер) control "Имя" Управляющий компонент (Controller, Service)
Boundary (граница) boundary "Имя" Граничный объект (UI, API Gateway)
Collections (очередь) collections "Имя" Очередь сообщений (Kafka, RabbitMQ)

Правила для Lifeline:

  • Каждой Lifeline давайте осмысленное имя — не «Service 1», а Inventory Service или User Service
  • Если Lifeline представляет конкретный экземпляр — используйте нотацию имя:Тип, например storage:PostgreSQL
  • Lifeline может быть уничтожена (крестик внизу) — если объект временный

3.2. Фокус управления (Activation Bar / Execution Specification)

Activation Bar (или Execution Specification) — узкий прямоугольник на Lifeline. Показывает, когда объект активен — выполняет операцию, ждёт ответа от другого объекта или обрабатывает данные.

       │
  ┌────┴────┐  ← Activation Bar начался (объект получил сообщение)
  │         │
  │  Active │
  │         │
  └────┬────┘  ← Activation Bar закончился (объект вернул ответ)
       │

Правила Activation Bar:

  1. Начало: объект получает сообщение (стрелка входит в Lifeline)
  2. Конец: объект возвращает ответ (пунктирная стрелка) или завершает выполнение
  3. Вложенность: если объект вызывает другой объект, Activation Bar продолжается до возврата
  4. Один бар: у объекта обычно один Activation Bar (UML допускает несколько вложенных, но на практике это редкость)

Пример — вложенность активаций:

Frontend        Backend         Database
   │               │               │
   │── HTTP ──────►│               │  ← Activation Bar у Backend начался
   │               │── SQL ───────►│  ← Activation Bar у DB начался
   │               │◄── rows ─────│  ← Activation Bar у DB закончился
   │◄── HTTP 200 ──│               │  ← Activation Bar у Backend закончился
   │               │               │

Зачем нужны Activation Bar?

  • Показывают, кто сколько времени занят
  • Помогают найти узкие места (один объект долго активен — возможно, нужен асинхронный вызов)
  • Делают диаграмму читаемой: видно, кто выполняет работу последовательно, а кто параллельно

3.3. Сообщения (Messages)

Типы сообщений:

Тип PlantUML Визуал Смысл Пример
Синхронное A -> B: запрос Сплошная стрелка с залитым треугольником A отправляет сообщение и ждёт ответ. Пока B не ответит, A не продолжает. HTTP-запрос: POST /api/v1/orders
Асинхронное A ->> B: событие Сплошная стрелка с открытой стрелкой A отправляет сообщение и не ждёт ответа, продолжает работу. Отправка в Kafka: order.created
Возврат (Reply) B --> A: ответ Пунктирная стрелка B возвращает результат A. Всегда после синхронного вызова. HTTP 200 OK, 201 Created, 409 Conflict
Self-вызов A -> A: метод() Стрелка в себя A вызывает свой внутренний метод. this.validateOrder(), this.calculateTotal()
Create create B \n A -> B: new() Сплошная стрелка с созданным объектом A создаёт новый объект B. Создание сессии, создание DTO
Destroy destroy C Крестик внизу A уничтожает объект C. Закрытие соединения, удаление временного файла

Синхронное vs Асинхронное — ключевое различие

Синхронное сообщение:

Клиент                    Сервер
  │                         │
  ├────── GET /api ────────►│
  │                         │
  │     (клиент ждёт)       │  ← Клиент заблокирован
  │                         │
  │◄───── HTTP 200 ─────────│
  │                         │
  │   (клиент продолжает)   │
  • Поток выполнения блокируется до получения ответа
  • Гарантия: сообщение доставлено (ответ получен)
  • Риск: тайм-аут (сервер не ответил за N секунд)

Асинхронное сообщение:

Producer               Kafka               Consumer
  │                      │                     │
  ├───── send(event) ───►│                     │
  │        не ждёт       │                     │
  │ (продолжает работу)  ├───── receive ──────►│
  │                      │                     │
  • Поток выполнения не блокируется
  • Гарантия: доставка не гарантирована (нужны настройки Kafka: acks=all)
  • Примеры: отправка email, логирование, обновление аналитики

Как выбирать:

Синхронно, если:
  • Клиенту нужен результат сейчас
  • Нужна гарантия доставки
  • Операция быстрая (< 1 сек)
  • Команда: POST / PUT / PATCH / DELETE (часто)

Асинхронно, если:
  • Клиенту не нужен результат немедленно
  • Операция долгая (генерация отчёта, конвертация видео)
  • Это уведомление (email, push, SMS)
  • Высокая нагрузка — нужна буферизация

3.4. Возвратные сообщения (Reply Messages)

Правила возврата:

  1. Каждое синхронное сообщение может иметь возврат (но не обязан — вызов метода void)
  2. Возврат — пунктирная стрелка --> (PlantUML: B --> A: результат)
  3. Возврат завершает Activation Bar у получателя
  4. На возврате можно указать:
    • HTTP-статус-код (201 Created, 409 Conflict)
    • Тип возвращаемых данных (OrderDTO, List<Product>)
    • Идентификатор созданной сущности (orderId: 12345)

Важно: возвраты не должны плодиться. Если у вас 10 синхронных вызовов и 10 возвратов — это нормально. Но не рисуйте возвраты в асинхронных сообщениях — это противоречие (асинхронный вызов по определению не ждёт ответа).


4. Комбинированные фрагменты (Combined Fragments)

Combined Fragment — это прямоугольный блок с оператором в левом верхнем углу, который определяет условия выполнения внутри блока.

4.1. Полный список операторов

Оператор Название Когда использовать
alt Alternative (альтернатива) Если-иначе: несколько взаимоисключающих потоков
opt Optional (опция) Блок выполняется только при условии
loop Loop (цикл) Повторный запрос (пагинация, retry)
par Parallel (параллельно) Параллельные независимые действия
break Break (прерывание) Прерывание сценария при ошибке
critical Critical (критический) Атомарная секция (транзакция)
ref Reference (ссылка) Ссылка на другую диаграмму
neg Negative (негативный) То, что НЕ должно произойти (запрещённая последовательность)
strict Strict (строгий) Строгий порядок сообщений внутри
seq Sequential (последовательный) Слабая последовательность (по умолчанию)
ignore Ignore (игнорировать) Важные, но не показанные сообщения
consider Consider (учитывать) Какие сообщения показаны

4.2. alt — Альтернатива (if-else, switch-case)

Синтаксис PlantUML:

alt условие_1
    A -> B: сообщение 1
else условие_2
    A -> B: сообщение 2
else
    A -> B: сообщение 3 (else по умолчанию)
end

Пример — обработка платежа:

alt [balance >= total]
    Frontend -> Backend: POST /payments {amount, orderId}
    Backend -> PaymentGateway: charge(amount, token)
    PaymentGateway --> Backend: success
    Backend --> Frontend: 201 {paymentId, status: "completed"}
    Frontend -> User: Показать "Оплата прошла успешно"
else [balance < total]
    Backend --> Frontend: 402 {error: "insufficient_funds"}
    Frontend -> User: Показать "Недостаточно средств"
else [payment_gateway_unavailable]
    Backend --> Frontend: 504 {error: "gateway_timeout"}
    Frontend -> User: Показать "Платёжная система временно недоступна"
end

Правила alt:

  • ровно одна ветка выполняется (взаимоисключение)
  • минимум две ветки (иначе — opt)
  • можно использовать else для «ни одно из условий» (как default в switch)

4.3. opt — Опция (необязательный блок)

Отличие от alt: opt — это alt с одной веткой. Выполняется, если условие истинно. Если ложно — блок пропускается.

Синтаксис PlantUML:

opt условие
    A -> B: сообщение
end

Пример — купон на скидку:

opt [couponCode != null]
    Frontend -> Backend: GET /api/v1/coupons/{code}
    Backend --> Frontend: 200 {discount: 15%}
    Frontend -> User: Показать "Применён купон: скидка 15%"
end

Когда opt, а когда alt?

Ситуация Что использовать
Блок выполняется или нет opt
Есть два взаимоисключающих варианта alt (ветка else пустая — некрасиво, лучше opt)
Есть default-ветка (ничего не делать) opt
Три и более варианта alt с несколькими else

4.4. loop — Цикл (повторение)

Синтаксис PlantUML:

loop N раз / while условие
    A -> B: сообщение
end

Три варианта записи:

loop 3 раза
    A -> B: попытка запроса
end

loop while (page.hasNext())
    A -> B: GET /items?page={page}
    B --> A: [ItemDTO]
end

loop until (response.status == 200)
    A -> B: POST /api/v1/retry
end

Пример — Retry Pattern (повтор при ошибке):

loop 3 раза
    Frontend -> Backend: POST /api/v1/payments
    alt [status == 200]
        break
    else [status == 503]
        ' Продолжить цикл
    end
end

Правила loop:

  • Всегда есть условие выхода (иначе — бесконечный цикл)
  • На диаграмме должно быть понятно, когда цикл завершается
  • Для retry используйте loop + break при успехе

4.5. par — Параллельные действия

Синтаксис PlantUML:

par
    A -> B: действие 1
else
    A -> C: действие 2
else
    B -> D: действие 3
end

Пример — параллельные уведомления:

par
    Backend -> EmailService: отправка email с подтверждением
else
    Backend -> PushService: отправка push-уведомления
else
    Backend -> Analytics: логирование события "Заказ создан"
end

Важно: par НЕ означает гонку. Все ветки выполняются (UML не гарантирует порядок внутри par). Если нужно синхронизировать — используйте critical.

4.6. break — Прерывание сценария

Синтаксис PlantUML:

break условие
    A -> B: аварийное завершение
end

Пример — авторизация не пройдена:

Frontend -> Backend: GET /api/v1/orders
Backend -> AuthService: validateToken(token)
alt [token invalid]
    AuthService --> Backend: invalid
    Backend --> Frontend: HTTP 401 Unauthorized
    break
else [token valid]
    AuthService --> Backend: valid
end
' Сюда попадаем только если break не сработал
Backend -> Database: SELECT * FROM orders

Когда break vs alt для ошибки?

Аспект alt break
Поток продолжается после Да (ветка else) Нет (сценарий завершён)
Подходит для Вариантов одного сценария Раннего завершения при ошибке
Визуально Зелёный vs Красный (субъективно) «Точка невозврата»

4.7. ref — Ссылка на другую диаграмму

Синтаксис PlantUML:

ref over A, B : Авторизация пользователя

Назначение: дробление большой диаграммы на части.

Когда использовать ref:

  • Диаграмма стала слишком большой (> 25 сообщений)
  • Один и тот же подпроцесс используется в нескольких сценариях
  • Вы хотите скрыть внутренние детали (например, «Звонок к Payment Gateway»)

Пример дробления:

' Главная диаграмма
Customer -> Frontend: Оформить заказ
Frontend -> Backend: POST /api/v1/orders
ref over Backend, PaymentGateway : Процесс оплаты (см. отдельную диаграмму)
Backend --> Frontend: 201 Created
Frontend --> Customer: Подтверждение заказа

5. Сложный пример: Процесс оплаты заказа через сторонний платежный шлюз

5.1. Постановка задачи

Спроектируем процесс оплаты заказа в интернет-магазине через внешний платёжный шлюз (например, Stripe / YooKassa / Tinkoff Acquiring).

Участники:

Lifeline Тип Роль
Покупатель Actor Человек, совершающий покупку
Frontend Participant UI веб-приложения (SPA)
Backend (Order Service) Participant Серверная логика
Database (PostgreSQL) Database Хранилище заказов и транзакций
Payment Gateway Participant Внешняя платёжная система (Stripe)
Auth Service Participant Сервис авторизации (JWT)
Email Service Participant Сервис отправки уведомлений

Требования к сценарию:

  1. Покупатель инициирует оплату из корзины
  2. Перед оплатой — проверка JWT-токена (авторизация)
  3. Backend резервирует товар на складе (Inventory Service — синхронно)
  4. Backend создаёт транзакцию в БД со статусом PENDING
  5. Backend отправляет запрос на списание средств в Payment Gateway
  6. Платёжный шлюз может ответить успехом, ошибкой или запросить 3D-Secure (дополнительная аутентификация)
  7. При успехе: заказ переводится в статус PAID, отправляется событие в Email Service
  8. Параллельно с уведомлением: аналитика логирует событие
  9. При неудаче: заказ остаётся в статусе PENDING, покупателю показывается ошибка
  10. При 3D-Secure: покупатель перенаправляется на страницу банка, после подтверждения — callback от шлюза

5.2. PlantUML-код

@startuml
skinparam backgroundColor #FEFEFE
skinparam participantPadding 20
title Процесс оплаты заказа через платежный шлюз

' ========== Участники ==========
actor "Покупатель" as customer
participant "Frontend (SPA)" as frontend
participant "Order Service" as backend order 10
database "PostgreSQL" as db order 20
participant "Auth Service" as auth order 5
participant "Inventory Service" as inventory order 15
participant "Payment Gateway\n(Stripe)" as gateway order 25
participant "Email Service" as email order 30
participant "Analytics Service" as analytics order 35

' ========== 1. Инициализация ==========
== Шаг 1: Авторизация и проверка корзины ==

customer -> frontend: Нажать «Оплатить заказ»
activate frontend

frontend -> frontend: Собрать данные корзины\nиз локального состояния

frontend -> auth: GET /api/v1/auth/validate (JWT token)
activate auth

alt [token невалиден / истёк]
    auth --> frontend: HTTP 401 Unauthorized
    deactivate auth
    frontend -> customer: Показать «Сессия истекла, войдите заново»
    stop
else [token валиден]
    auth --> frontend: HTTP 200 {userId, role, exp}
    deactivate auth
end

' ========== 2. Резервирование ==========
== Шаг 2: Проверка и резервирование товара ==

frontend -> backend: POST /api/v1/orders/init {items, total, currency}
activate backend

backend -> auth: validateToken(token)
activate auth
auth --> backend: {userId, role}
deactivate auth

backend -> db: BEGIN TRANSACTION
activate db

backend -> db: SELECT items, stock FROM inventory\nWHERE item_id IN (...) FOR UPDATE
db --> backend: [{itemId, stock}]

alt [по какому-то товару недостаточно остатка]
    backend -> db: ROLLBACK
    deactivate db
    backend --> frontend: HTTP 409 {error: "item_out_of_stock", itemId}
    deactivate backend
    frontend -> customer: Показать «Товар X закончился»
    stop
else [все товары в наличии]
    backend -> db: UPDATE inventory SET stock = stock - 1\nWHERE item_id IN (...)
    db --> backend: rows updated
end

backend -> db: INSERT INTO orders (user_id, status, total, currency)\nVALUES (?, 'RESERVED', ?, ?) RETURNING id
db --> backend: orderId = 12345

backend -> db: COMMIT
deactivate db

' ========== 3. Инициализация платежа ==========
== Шаг 3: Создание платежа в платёжном шлюзе ==

backend -> db: INSERT INTO payments (order_id, amount, currency, status)\nVALUES (12345, ?, ?, 'PENDING')
activate db
db --> backend: paymentId = 54321
deactivate db

backend -> gateway: POST /v1/charges {amount, currency, return_url, metadata}
activate gateway

note right of gateway: Платёжный шлюз может\nзапросить 3D-Secure\n(доп. аутентификация)

alt [платёж выполнен сразу — без 3DS]
    gateway --> backend: 200 {id: "ch_12345", status: "succeeded"}
    deactivate gateway
    note left: Стандартный платёж\nбез дополнительной верификации

else [требуется 3D-Secure]
    gateway --> backend: 200 {id: "ch_12345", status: "requires_action",\nnext_action: {type: "redirect_to_url", url: "https://..."}}
    deactivate gateway

    ' 3D-Secure: перенаправление на страницу банка
    backend --> frontend: HTTP 200 {action: "redirect", url: "https://...", paymentId: 54321}
    deactivate backend

    frontend -> customer: Перенаправить на страницу 3D-Secure банка
    customer -> frontend: Пройти верификацию (код из SMS/приложения банка)
    activate frontend

    ' Callback от шлюза после 3DS
    frontend -> backend: POST /api/v1/payments/confirm {paymentIntentId: "pi_12345"}
    activate backend

    backend -> gateway: GET /v1/charges/ch_12345
    activate gateway

    alt [3DS пройден — платёж подтверждён]
        gateway --> backend: 200 {status: "succeeded"}
        deactivate gateway
    else [3DS не пройден]
        gateway --> backend: 200 {status: "requires_payment_method"}
        deactivate gateway
        backend --> frontend: HTTP 402 {error: "3ds_failed"}
        deactivate backend
        frontend -> customer: Показать «Ошибка верификации,\nпопробуйте другой способ оплаты»
        stop
    end
end

' ========== 4. Финализация заказа ==========
== Шаг 4: Финализация заказа и уведомления ==

backend -> db: BEGIN TRANSACTION
activate db

backend -> db: UPDATE orders SET status = 'PAID',\npaid_at = NOW() WHERE id = 12345
db --> backend: OK

backend -> db: UPDATE payments SET status = 'SUCCEEDED'\nWHERE id = 54321
db --> backend: OK

backend -> db: COMMIT
deactivate db

' Параллельные уведомления
par
    ' Ветка 1: Отправка email
    backend -> email: sendPaymentConfirmation(orderId=12345, email=user@example.com)
    activate email
    note right: Асинхронная отправка\n(через очередь)
    email --> backend: accepted (202)
    deactivate email

else
    ' Ветка 2: Логирование в аналитику
    backend -> analytics: logEvent("order.paid", {orderId: 12345, total, currency})
    activate analytics
    analytics --> backend: logged
    deactivate analytics

else
    ' Ветка 3: Снятие резерва (не обязательно в данном сценарии)
    ' Резерв уже снят на шаге 2 — это просто для демонстрации
end

' ========== 5. Ответ клиенту ==========
backend --> frontend: HTTP 201 {orderId: 12345, status: "paid", paymentId: 54321}
deactivate backend

frontend -> customer: Показать страницу «Заказ оплачен»\nс деталями и номером заказа
deactivate frontend

@enduml

5.3. Разбор диаграммы

Что демонстрирует этот пример:

Элемент Как используется
alt (3 шт.) Валидация JWT, проверка остатков, 3D-Secure (успех/ошибка/фолбек)
par (1 шт.) Параллельная отправка email + аналитика
stop (3 шт.) Прерывание сценария при разных ошибках (401, 409, 402)
Activation Bar У каждого участника при получении/обработке запроса
Синхронные вызовы Все HTTP-запросы (фронтенд → бэкенд, бэкенд → шлюз)
Асинхронные вызовы Email Service (backlog — принял запрос, но не гарантирует моментальную отправку)
Self-вызов frontend -> frontend: Собрать данные корзины — внутренняя операция
Database transaction BEGIN / COMMIT / ROLLBACK — явно показаны
Guard conditions [token валиден], [все товары в наличии] — в квадратных скобках

5.4. Что мы обнаружили в процессе построения

Анализируя процесс оплаты, мы выявили скрытые требования:

  1. Что делать с товаром, если 3D-Secure не пройден? → Оставить резерв или снять? Решение: снимать резерв через 30 минут (фоновый job) — это станет отдельным сценарием.
  2. Что делать с товаром, если платёж завис? → Добавить Compensating Transaction (отмена резерва).
  3. Нужен ли ретрай при ошибке шлюза? → Да, loop 3 с экспоненциальной задержкой.
  4. Кто отвечает за тайм-аут? → Фронтенд ждёт 30 секунд, потом показывает «Сервис временно недоступен».

Без Sequence Diagram эти вопросы были бы обнаружены на этапе разработки или тестирования, с переделками и потерянным временем.


6. Паттерны Sequence Diagram для интеграций

6.1. Паттерн «Трёхзвенка» (3-Tier)

Стандартный паттерн для веб-приложений:

Actor → Frontend (UI) ↔ Backend (API) ↔ Database

Особенности:

  • Frontend отправляет HTTP (синхронно)
  • Backend обрабатывает бизнес-логику
  • Database — хранилище
  • Все вызовы синхронные

Когда использовать: стандартные CRUD-операции, простые бизнес-процессы.

6.2. Паттерн «Посредник» (Mediator / API Gateway)

Actor → API Gateway → Service A
                 ↓
              Service B
                 ↓
              Service C

Особенности:

  • API Gateway — единая точка входа
  • Gateway может агрегировать ответы от нескольких сервисов
  • Gateway — «тонкий» (только маршрутизация) или «толстый» (агрегация, трансформация)

Когда использовать: микросервисная архитектура, когда клиенту нужно собрать данные из нескольких источников.

6.3. Паттерн «Сага (Хореография)» — распределённая транзакция

Service A → Service B → (успех) → Service C → (успех) → Done
                          ↓                     ↓
                     (ошибка)               (ошибка)
                          ↓                     ↓
                     Service B                Service B
                     компенсация              компенсация
                          ↓                     ↓
                     Service A                Service A
                     компенсация              компенсация

Когда использовать: распределённые транзакции в микросервисах (заказ + оплата + доставка).

6.4. Паттерн «Асинхронная очередь» (Event-Driven)

Producer → Message Queue (Kafka / RabbitMQ) → Consumer

Особенности:

  • Producer не ждёт ответа (асинхронный вызов)
  • Consumer может быть не готов (сообщение в очереди)
  • Надёжность: Kafka хранит сообщения на диске

Когда использовать: высокая нагрузка, слабая связность сервисов, уведомления.

6.5. Паттерн «Проверка перед действием» (Validate-Execute)

Actor → Backend: POST /action
Backend → Validation: check()
Validation → Backend: valid/invalid
alt [invalid]
    Backend → Actor: 400 Bad Request
else [valid]
    Backend → Database: INSERT ...
    Backend → Actor: 201 Created
end

Когда использовать: когда валидация сложная и должна выполняться на сервере (не на фронтенде).


7. Sequence Diagram и тест-кейсы

Каждый путь на Sequence Diagram — это тест-кейс.

7.1. Карта тест-кейсов из диаграммы оплаты

# Путь на диаграмме Условие Ожидаемый результат Тест-кейс
1 Happy Path JWT валиден, товары в наличии, платёж без 3DS 201 Created, заказ PAID TC-01: Успешная оплата
2 JWT невалиден Token expired / invalid 401 Unauthorized, оплата не начинается TC-02: Истекшая сессия
3 Товара нет в наличии Stock = 0 для одного из товаров 409 Conflict, заказ не создан TC-03: Нет на складе
4 Платёж с 3DS успешно 3DS пройден 201 Created, заказ PAID TC-04: 3DS успешно
5 Платёж с 3DS не пройден 3DS failed 402 Error, заказ RESERVED TC-05: 3DS не пройден
6 Ошибка платёжного шлюза Gateway timeout / 500 Backend retry + 504 клиенту TC-06: Шлюз недоступен
7 Email упал Email Service 500 Заказ PAID, уведомление не отправлено TC-07: Fallback уведомления

Правило: каждый alt и opt фрагмент добавляет минимум 1 тест-кейс. Каждый break — минимум 1 негативный тест.

7.2. Как аналитик строит тест-кейсы из Sequence Diagram

Sequence Diagram → выписать все alt/opt/break → для каждой ветки:
  1. Какое сообщение-триггер?
  2. Какое условие?
  3. Какие сообщения внутри ветки?
  4. Какой конечный результат?

Результат: таблица из 4 колонок (Триггер → Условие → Действия → Результат), которая передаётся QA-инженеру.


8. Sequence Diagram в PlantUML — полный справочник

8.1. Объявление участников с порядком

actor "Покупатель" as customer order 1
participant "Frontend" as ui order 2
participant "Backend" as svc order 3
database "DB" as db order 4
collections "Kafka" as kafka order 5

order N — гарантирует позицию слева направо.

8.2. Активации

A -> B: запрос
activate B   ' начать активацию
B --> A: ответ
deactivate B ' закончить активацию

8.3. Создание и уничтожение

create PaymentDTO
backend -> PaymentDTO: new(amount, currency)
...
destroy PaymentDTO

8.4. Automatic activation (сокращённый синтаксис)

autoactivate on
A -> B: запрос    ' activate B автоматически
B -> C: вызов     ' activate C автоматически
C --> B: ответ    ' deactivate C
B --> A: ответ    ' deactivate B
autoactivate off

Внимание: автоактивация удобна для простых диаграмм, но на сложных диаграммах лучше управлять activate/deactivate вручную — точнее контроль.

8.5. Оформление

' Разделители
=== Раздел 1: Авторизация ===

' Цвет участника
participant "Service" as svc #LightBlue

' Заметки
note right of A: текст заметки
note over A, B: заметка на несколько\nлиний жизни
note left: комментарий

' Отступ
||| Пауза |||

' Форматирование текста
note right: <b>Важно</b>\n<color:red>ошибка</color>

8.6. Несколько уточнений

  • Вложенные активации (Nested Activation) — допустимы, но их следует избегать (сложно читать)
  • Activation Bar у Actor — не ставится (актёр — внешняя сущность, мы не контролируем его время выполнения)
  • Асинхронные вызовы не имеют возврата — если возврат есть, вызов должен быть синхронным

9. Типичные ошибки и как их избежать

Ошибка 1: Sequence Diagram вместо Activity Diagram

Симптом: на диаграмме 15 участников, 40 сообщений, но нет альтернативных сценариев.

Проблема: Sequence Diagram описывает один конкретный сценарий. Если вы пытаетесь показать все ветвления бизнес-процесса на одной Sequence Diagram — используйте Activity Diagram или BPMN.

Как исправить:

  • Business logic ветвления → Activity Diagram
  • API-вызовы внутри одного сценария → Sequence Diagram

Ошибка 2: Слишком много alt-фрагментов

Симптом: диаграмма состоит на 60% из alt-фрагментов, вложенных друг в друга.

Проблема: Каждый alt — это отдельный тест-кейс. Три вложенных alt — это 8 (2³) комбинаций, которые невозможно протестировать.

Как исправить: Разбить на несколько диаграмм по одному alt на диаграмму. Или перейти на Activity Diagram.

Ошибка 3: Асинхронный вызов с возвратом

Симптом: A ->> B: запрос и B --> A: ответ.

Проблема: Если вызов асинхронный, A не ждёт ответа. Показывать возврат — противоречие.

Как исправить: Либо стрелка -> (синхронно, с ожиданием ответа), либо ->> без возврата (асинхронно, ответ не ожидается). Если асинхронному вызову всё же нужен ответ — используйте callback: второй асинхронный вызов от B к A, когда результат готов.

Ошибка 4: Нет обработки ошибок (только Happy Path)

Симптом: диаграмма показывает только успешный сценарий. Нет alt/break/opt для ошибок.

Проблема: Разработчик реализует только Happy Path, QA не проверяет ошибки, production падает.

Как исправить: Для каждого вызова добавьте alt [success] / [error]. Если ошибок много — хотя бы одну «общую» ветку error.

Ошибка 5: Lifeline не соответствует уровню абстракции

Симптом: На одной диаграмме Пользователь, HTTP-запрос, SQL-запрос, Сервер, Класс UserRepository.

Проблема: Смешение бизнес-уровня и технического уровня.

Как исправить: Выберите один уровень:

  • Бизнес-уровень: Актёр → Система A → Система B
  • Логический уровень: Frontend → API → Service → Database
  • Технический уровень: Конкретные классы, методы, SQL-запросы

Не смешивайте их на одной диаграмме.

Ошибка 6: Отсутствие Activation Bar

Симптом: Lifelines есть, стрелки есть, но Activation Bar нет.

Проблема: Непонятно, кто в какой момент времени активен. Диаграмма выглядит как «рыба» без структуры.

Как исправить: Каждый синхронный вызов → activate у получателя, deactivate после ответа. Для асинхронных — активация при получении, деактивация после обработки.

Ошибка 7: Пересекающиеся стрелки

Симптом: стрелки от разных участников пересекаются, невозможно понять, к кому идёт сообщение.

Как исправить: Следите за порядком Lifeline. Группируйте участников: слева — инициатор, справа — внешние сервисы. Используйте order N в PlantUML.


10. Чек-лист: готовая Sequence Diagram

Критерий Как проверить
1 Название сценария указано В заголовке диаграммы
2 Участники (Lifeline) определены Каждый нужный компонент присутствует
3 Первое сообщение — от внешнего актёра Инициация сценария
4 Каждое синхронное сообщение → стрелка с заполненным треугольником A -> B
5 Каждое асинхронное → стрелка с открытым наконечником A ->> B
6 У каждого синхронного вызова есть возврат B --> A
7 Activation Bar есть у всех участников activate / deactivate
8 Activation Bar не пересекается (у одного участника) Нет двух одновременных активностей
9 Альтернативы покрыты (alt) Есть ветки для ошибок
10 Опциональные шаги (opt) добавлены Например, «отправить email»
11 Циклы (loop) не бесконечные Есть условие выхода
12 Диаграмма не слишком велика < 25 сообщений, иначе — ref
13 Уровень абстракции соблюдён Не смешаны бизнес-уровень и технический
14 Все статус-коды HTTP явно указаны 201, 401, 409, 402
15 Асинхронные вызовы имеют очередь (collections) если нужно Kafka, RabbitMQ

11. Вопросы для самопроверки

  1. Почему Sequence Diagram считают «главным инструментом аналитика при интеграциях»? Назовите 3 причины.
  2. Чем синхронный вызов отличается от асинхронного? Какой тип стрелки в PlantUML у каждого?
  3. Что такое Activation Bar? Почему он не может пересекаться сам с собой?
  4. Какая разница между alt и opt? Приведите жизненный пример для каждого.
  5. Что такое break? Когда его использовать вместо alt?
  6. Как на Sequence Diagram показать, что блок должен выполниться только при определённом условии?
  7. Как на Sequence Diagram показать параллельные действия?
  8. Как на Sequence Diagram показать, что сценарий прерывается при ошибке?
  9. Сколько тест-кейсов можно выделить из Sequence Diagram оплаты (пример из урока)? Назовите хотя бы 5.
  10. Что такое паттерн «Сага»? Как его смоделировать в Sequence Diagram?
  11. Что произойдёт, если нарисовать A ->> B: запрос и потом B --> A: ответ? Почему это ошибка?
  12. Какую информацию можно извлечь из Sequence Diagram для написания OpenAPI-спецификации?

12. Практическое задание

Кейс: Процесс отмены заказа в интернет-магазине

Описание сценария:

Пользователь хочет отменить заказ, который ещё не отправлен.

  1. Пользователь открывает страницу заказа в личном кабинете
  2. Пользователь нажимает «Отменить заказ»
  3. Frontend отправляет запрос на отмену: POST /api/v1/orders/{id}/cancel
  4. Backend проверяет статус заказа в БД
  5. Если статус заказа — PAID или RESERVED:
    • Backend вызывает платёжный шлюз для возврата денег (refund)
    • Платёжный шлюз обрабатывает возврат (может завершиться успехом или ошибкой)
    • Если refund успешен: Backend меняет статус заказа на CANCELLED, возвращает товар на склад (Inventory Service)
    • Если refund не удался (например, превышен лимит на возвраты): Backend создаёт тикет для техподдержки, заказ остаётся в статусе PAID
  6. Если статус заказа — SHIPPED или DELIVERED:
    • Backend возвращает ошибку 400: «Заказ уже отправлен, отмена невозможна»
  7. Параллельно с отменой:
    • Backend отправляет уведомление пользователю (email)
    • Backend логирует событие в аналитику
  8. Frontend показывает результат пользователю (успех/ошибка/создан тикет)

Задание 1. Постройте Sequence Diagram

Создайте Sequence Diagram сценария отмены заказа.

Участники:

  • Пользователь (actor)
  • Frontend (participant)
  • Order Service / Backend (participant)
  • PostgreSQL (database)
  • Payment Gateway (participant)
  • Inventory Service (participant)
  • Support System (participant) — для создания тикета
  • Email Service (participant)
  • Analytics Service (participant)

Требования:

  • Минимум 2 фрагмента alt (проверка статуса, результат refund)
  • Минимум 1 фрагмент par (уведомление + аналитика)
  • Минимум 1 асинхронное сообщение (email)
  • Явно показаны HTTP статус-коды на возвратах
  • Транзакции в БД (BEGIN / COMMIT / ROLLBACK) — опционально, но приветствуется
  • Activation Bar для каждого участника

Задание 2. PlantUML-код

Напишите полный PlantUML-код этой диаграммы. Убедитесь:

  • @startuml / @enduml с заголовком
  • Все участники объявлены с корректными типами
  • activate / deactivate для каждого вызова
  • alt / else / else / end для ветвлений
  • par / else / end для параллельных действий
  • stop для завершения сценария
  • Код компилируется (проверьте на plantuml.com или в VS Code)

Задание 3. Карта тест-кейсов

На основе вашей Sequence Diagram составьте таблицу тест-кейсов:

TC# Сценарий Условие Входные данные Ожидаемый результат
TC-01 ... ... ... ...

Минимум 5 тест-кейсов.

Задание 4. Анализ ошибок

Даны 3 фрагмента Sequence Diagram. Найдите ошибки и объясните, почему это ошибка:

Фрагмент A:

actor User
participant Frontend
participant Backend

User -> Frontend: Нажать кнопку
Frontend -> Backend: POST /api/action
Backend -> Backend: process()
Backend --> Frontend: 200 OK
Frontend --> User: Результат

Фрагмент B:

actor User
participant ServiceA
participant ServiceB

User ->> ServiceA: Запрос
ServiceA ->> ServiceB: Асинхронный вызов
ServiceB --> ServiceA: Ответ
ServiceA --> User: Результат

Фрагмент C:

actor User
participant Frontend
participant Backend
participant DB

User -> Frontend: Действие
Frontend -> Backend: POST /api/orders
Backend -> DB: SELECT ... FOR UPDATE
DB --> Backend: rows
Frontend --> User: Результат
' Activation Bar не закрыт у Backend

Задание 5. Рефлексия

Напишите 3–5 предложений: «Почему Sequence Diagram — главный инструмент аналитика при проектировании интеграций фронтенда и бэкенда? Приведите один аргумент из своего опыта (или воображаемого опыта).»


13. Дополнительные материалы

  • Книга: Martin Fowler — «UML Distilled», 3-е издание, глава 4 «Sequence Diagrams» (75 страниц, но ёмко)
  • Спецификация: OMG UML 2.5.1, раздел 17 «Interactions» (формальное определение — 80+ страниц, для глубокого погружения)
  • PlantUML: plantuml.com/sequence-diagram — официальная документация с примерами
  • Видео: «UML Sequence Diagram Tutorial» на Lucidchart (15 минут, визуальное введение)
  • Шпаргалка: «Sequence Diagram Cheat Sheet» — поищите в Google Images для одностраничной памятки
  • Tool: draw.io (diagrams.net) — бесплатный редактор диаграмм, Sequence Diagram в разделе UML
  • Tool: PlantUML VS Code Extension — для компиляции .puml файлов внутри редактора
  • Паттерны: «Enterprise Integration Patterns» by Hohpe and Woolf — книга про интеграционные паттерны (Saga, Retry, Aggregator), которые отлично ложатся на Sequence Diagram
  • Статья: Martin Fowler — «Design Stamina Hypothesis» — почему стоит тратить время на диаграммы перед кодом (проектная документация окупается на сложных сценариях)

Следующий урок: 04.05 — Class Diagram (Диаграмма классов)

📚 Материалы модуля

🖼️ Схема и инфографика

🎬 Видео-лекция

🎬 UML Универсальные чертежи

📄 Дополнительные материалы (PDF)

📄The UML Blueprint
Скачать
Спросить ИИ