§ 01О чём проектWhat this is
За несколько коммерческих проектов я заметил, что объясняю одни и те же вещи: «почему Repository, а не Active Record», «зачем outbox, если есть транзакция», «как сделать идемпотентный POST». Объяснять на продакшен-коде нельзя - NDA. На примерах из книги - тоже плохо, они слишком стерильны. After several commercial projects, I noticed I keep explaining the same things: "why Repository and not Active Record," "why outbox when there's a transaction," "how to make an idempotent POST." Explaining with production code is off-limits - NDA. Book examples don't work either - too sterile.
ServiceFlow - это helpdesk-бэкенд, написанный достаточно подробно, чтобы на нём можно было показать всё, что я считаю важным. Достаточно простой, чтобы домен не закрывал паттерны. ServiceFlow is a helpdesk backend written in enough detail to demonstrate everything I consider important. Simple enough that the domain doesn't obscure the patterns.
§ 02Как устроеноHow it's built
- Repository + UoW - чистый домен, тестируемый без БД.Repository + UoW - clean domain, testable without a database.
- Доменный FSM - переходы статусов тикета как явная машина, не набор if'ов.Domain FSM - ticket status transitions as an explicit state machine, not a bunch of ifs.
- Idempotency - заголовок
Idempotency-Key, повтор запроса возвращает тот же ответ без побочных эффектов.Idempotency -Idempotency-Keyheader, retrying a request returns the same response with no side effects. - Outbox pattern - события в ту же транзакцию, отправка асинхронно.Outbox pattern - events in the same transaction, delivery is asynchronous.
- ETag / If-Match - оптимистичный concurrency control на HTTP-уровне.ETag / If-Match - optimistic concurrency control at the HTTP level.
- RFC 7807 ошибки - единый формат problem-details.RFC 7807 errors - unified problem-details format.
- OpenTelemetry - трейсы, метрики, логи с первого коммита.OpenTelemetry - traces, metrics, logs from the first commit.
- API keys на argon2id - проверка занимает 100 мс намеренно.API keys on argon2id - verification takes 100 ms intentionally.
§ 03СтруктураStructure
serviceflow/ ├── domain/ # entities · value objects · FSM · invariants │ ├── ticket.py │ ├── status.py # FSM transitions, no IO │ └── policies/ ├── app/ # use-cases, ports │ ├── tickets/ │ ├── idempotency.py │ └── outbox.py ├── infra/ # adapters: Postgres, Redis, ARQ workers │ ├── repo/ │ ├── outbox_worker.py │ └── otel.py └── api/ # FastAPI routers, pydantic IO models ├── v1/ └── errors.py # RFC 7807
§ 04Где посмотретьWhere to look
Репозиторий открытый. README ведёт за руку: 10 минут - и всё крутится локально. ADR-папка объясняет каждое нетривиальное решение - почему UoW поверх SQLAlchemy 2.x, почему outbox, а не CDC, почему ARQ, а не Celery для этого размера. The repository is public. The README walks you through setup: 10 minutes and everything runs locally. The ADR folder explains every non-trivial decision - why UoW on top of SQLAlchemy 2.x, why outbox and not CDC, why ARQ and not Celery for this scale.