<- записи

Шаблон, а не фреймворк: мой стартовый бэкенд на Python

поддерживается

Каждый новый сервис у меня начинался с одного и того же ритуала: поднять слои, завести конфиги с env-префиксами, прикрутить DI, настроить структурные логи, вспомнить outbox и снова собрать документацию так, чтобы она не развалилась к третьей неделе. В какой-то момент я понял, что таскаю из проекта в проект одни и те же решения, и собрал их в публичный шаблон: s1t-python-backend-templates.

Я подготовил шаблон с максимально плотным соотношением функциональности к объёму кода. В нём уже есть то, что мне обычно требуется на старте, но его можно форкнуть, переименовать и спокойно вырезать лишнее.

Два самостоятельных сервиса

Внутри монорепо два полноценных сервиса. Общая у них только инфраструктура: Postgres и Valkey. В коде они друг от друга не зависят. Первый - плотный бэкенд с API, сложной логикой и работой с событиями; второй - небольшой event-driven микросервис, который забирает события и делает тяжёлую работу в фоне.

код
litestar_backend  --- video_uploaded -->  event_microservice
                  <-- video_status ----

litestar_backend: HTTP API на Litestar с Postgres, транзакционным outbox, композитной auth-цепочкой (пользователи с argon2id, JWT, API-ключи, админ-токен), админкой с лайв-просмотром логов, SSE-фидом и метриками Prometheus. event_microservice: FastStream-консьюмер и SAQ-воркер, который раскладывает тяжёлую работу на джобы и публикует статусы обратно. Каждый сервис - самостоятельный uv-проект со своим lock-файлом и Dockerfile: можно унести в отдельный репозиторий, не поменяв ни строчки.

Видеопайплайн внутри - не продукт, а сквозной пример, на котором видно, как всё это работает вместе.

Структура и проверки

В обоих сервисах жёстко разделены слои и bounded context'ы.

Схема: слои одного bounded context
Схема: слои одного bounded context

За этим следят полноценный pre-commit, ruff, mypy, import-linter и тесты. Те же проверки повторяются в Docker-гейте и CI, поэтому очередной быстрый импорт не может тихо сломать границы между слоями и контекстами.

Асинхронность, потоки и процессы на своих местах

Мне было важно, чтобы шаблон показывал все три способа выполнять работу в Python там, где каждый уместен. API и консьюмер живут на asyncio. В воркере CPU-задачи уходят в process pool, блокирующий I/O в потоки, лёгкие задачи остаются асинхронными. Завершение джобов джойнится в Valkey, и статус улетает обратно одним событием. Не надо изобретать это заново в каждом проекте: рядом лежат три типа джобов, скопируй нужный.

Паттерны, каждый end-to-end

Transactional outbox, inbox dedup (at-least-once доставка плюс дедупликация по event_id дают exactly-once эффект), композитная auth-цепочка, keyset-пагинация с opaque-курсорами, конверт интеграционных событий с event_id/version/occurred_at, graceful drain фоновых тасок. Это не список паттернов для README: каждый качественно реализован в контексте приложения, объяснён в документации и покрыт тестами. Всего ~364 теста на четырёх уровнях, от unit по домену без моков до e2e на полном приложении с testcontainers.

Доки как часть продукта

Документацией я особенно горжусь. Она строго и понятно разложена: architecture.md описывает монорепо целиком, contract/-страницы держат wire-факты, ADR лежат в деревьях проекта, сервисов и контекстов. За счёт этого в ней легко ориентироваться и не приходится искать один и тот же ответ по всему репозиторию. Расхождение доки с кодом считается багом. Для ежедневной работы рядом лежат Taskfile, pre-commit с gitleaks, docker-compose с bind-mounts и пара .env.example / .env.full.example.

Если вам такое нужно

Лицензия MIT, на репозитории есть кнопка Use this template, а на лендинге - интерактивная карта слоёв: sense1tapo4ek.github.io/s1t-python-backend-templates. Если начинаете сервис на Python, берите шаблон и выбрасывайте то, что не понадобится. Если найдёте дыру, issue сделает его лучше.

просмотров · 12

Комментарии

пока тихо

· · ·Будьте первым — анонимно или под ником.
-> отобразится как аноним#…
или войти, чтобы писать под своим #id