Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
# Project environment
# Project environment: dev | prod
ENVIRONMENT=dev

# Django settings
DJANGO_SECRET_KEY=your_secret_key_here
DJANGO_DEBUG=True
DJANGO_ALLOWED_HOSTS=localhost,
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1
# DJANGO_CSRF_TRUSTED_ORIGINS=https://example.com
# DJANGO_LOG_LEVEL=INFO

# Database settings
DATABASE_URL=postgres://user:password@localhost:5432/dbname
# Database settings (по умолчанию — sqlite, для PostgreSQL раскомментируйте)
# DATABASE_URL=postgres://app:app@localhost:5432/app

# Postgres (используется docker-compose)
POSTGRES_DB=app
POSTGRES_USER=app
POSTGRES_PASSWORD=app

# Redis settings
REDIS_URL=redis://localhost:6379/0
REDIS_URL=redis://localhost:6379/1
REDIS_HOST=localhost
REDIS_PORT=6379

# Production security (используются при ENVIRONMENT=prod)
# DJANGO_SECURE_SSL_REDIRECT=True
# DJANGO_SECURE_HSTS_SECONDS=2592000
# GUNICORN_WORKERS=4
45 changes: 45 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true

- name: Install dependencies
run: uv sync --group dev

- name: Ruff check
run: uv run ruff check .

- name: Ruff format check
run: uv run ruff format --check .

- name: Bandit
run: uv run bandit -c pyproject.toml -r .

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true

- name: Install dependencies
run: uv sync --group dev

- name: Run tests
run: uv run pytest -v
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
ARG UID=1000
ARG PYTHON_VERSION=3.13.7
ARG PYTHON_VERSION=3.13

FROM python:${PYTHON_VERSION}-slim-trixie AS builder

Expand Down Expand Up @@ -35,6 +35,7 @@ RUN apt-get update && apt-get install -y \
netcat-openbsd \
&& rm -rf /var/lib/apt/lists/*

ARG UID
RUN useradd --create-home --shell /bin/bash --uid ${UID:-1000} django
USER django
WORKDIR /home/django/app
Expand Down
40 changes: 38 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,44 @@
.PHONY: env
.PHONY: env install run migrate makemigrations superuser test lint format up down logs

env:
@if [ -f .env ]; then \
echo ".env уже существует, пропускаю."; \
else \
cp .env.example .env && echo "Создано .env из .env.example."; \
fi
fi

install:
uv sync --group dev

run:
uv run python manage.py runserver

migrate:
uv run python manage.py migrate

makemigrations:
uv run python manage.py makemigrations

superuser:
uv run python manage.py createsuperuser

test:
uv run pytest

lint:
uv run ruff check .
uv run ruff format --check .
uv run bandit -c pyproject.toml -r .

format:
uv run ruff check --fix .
uv run ruff format .

up:
docker compose up -d --build

down:
docker compose down

logs:
docker compose logs -f django
255 changes: 214 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,214 @@
### Общая архитектура

Проект построен как **монолитное API-ориентированное приложение** (API-first Monolith). Архитектура спроектирована для быстрого старта разработки, но с запасом прочности для масштабирования до HighLoad-системы. В основе лежит принцип **модульности**: весь бизнес-функционал инкапсулирован в изолированные приложения внутри директории `apps/`.

Система следует стандартам **Twelve-Factor App**, что обеспечивает её готовность к развертыванию в облачных средах (Kubernetes/Docker) и легкость в горизонтальном масштабировании.

### Уровни системы

Архитектура разделена на логические слои, чтобы снизить связность кода (Loose Coupling):

1. **Инфраструктурный слой (`config/`, `pyproject.toml`)**:
* Централизованная конфигурация, отделенная от кода бизнес-логики.
* Управление зависимостями через современный стандарт PEP 735 (dependency groups), что упрощает CI/CD пайплайны.
* Сюда входят настройки безопасности, логирования и подключений к БД.
2. **Слой API (Interface Layer)**:
* Отвечает за принятие запросов и валидацию входных данных.
* Реализован на **Django REST Framework**.
* Строгое разделение: ViewSet'ы занимаются только HTTP-логикой, не содержат бизнес-правил.
3. **Слой бизнес-логики (Domain Layer)**:
* Расположен внутри приложений в `apps/`.
* Инкапсулирует правила работы системы. Здесь живут сервисы (Services) и селекторы (Selectors), которые отделены от моделей и view.
* Модели данных ("толстые модели" используются умеренно) отвечают только за целостность данных, но не за сложные процессы.
4. **Слой данных (Persistence Layer)**:
* PostgreSQL как основное хранилище.
* Использование миграций Django для версионирования схемы БД.

### Ключевые архитектурные решения

* **Изоляция приложений**:
Каждый модуль в папке `apps/` (например, `users`, `billing`) проектируется как независимый компонент с собственным пространством имен (`apps.users`). Это предотвращает "спагетти-импорты" и облегчает будущий вынос модулей в микросервисы при необходимости.
* **Безопасность по умолчанию**:
В архитектуру встроены инструменты статического анализа (`bandit`, `ruff`), которые на этапе разработки блокируют уязвимый код. Конфигурация Django (`settings.py`) ужесточена для production-среды.
* **Строгая типизация и качество кода**:
Проект не допускает неявных типов. Использование `mypy` и `ruff` гарантирует, что кодовая база остается чистой, читаемой и предсказуемой, что критично для командной разработки.
* **Готовность к Observability**:
Архитектура подразумевает, что логирование и метрики (Tracing/Metrics) внедряются через middleware и не загрязняют бизнес-код, что упрощает подключение систем мониторинга (Prometheus/Grafana/Sentry).


---

Это **Modern Python Backend** архитектура: она отказывается от устаревших практик (как requirements.txt или запутанных settings) в пользу строгих стандартов, типизации и модульности. Она идеально подходит для команд, где важны скорость разработки (Time-to-Market) и надежность (Reliability).
<div align="center">

![Header](https://capsule-render.vercel.app/api?type=waving&color=0:092E20,100:44B78B&height=200&section=header&text=Django%20Boilerplate&fontSize=60&fontColor=ffffff&animation=fadeIn&fontAlignY=35&desc=Production-ready%20шаблон%20для%20API-приложений&descSize=18&descAlignY=55)

[![Typing SVG](https://readme-typing-svg.demolab.com?font=Fira+Code&weight=500&size=20&pause=1000&color=44B78B&center=true&vCenter=true&width=600&lines=API-first+монолит+на+Django+6;Версионирование+API+через+заголовок+Accept;OpenAPI+%2B+Swagger+UI+из+коробки;Docker+%C2%B7+PostgreSQL+%C2%B7+Redis+%C2%B7+uv)](https://git.io/typing-svg)

[![CI](https://github.com/ak4code/django_boilerplate/actions/workflows/ci.yml/badge.svg)](https://github.com/ak4code/django_boilerplate/actions/workflows/ci.yml)
[![Python](https://img.shields.io/badge/Python-3.13-3776AB?logo=python&logoColor=white)](https://www.python.org/)
[![Django](https://img.shields.io/badge/Django-6.0-092E20?logo=django&logoColor=white)](https://www.djangoproject.com/)
[![DRF](https://img.shields.io/badge/DRF-3.17-A30000?logo=django&logoColor=white)](https://www.django-rest-framework.org/)
[![PostgreSQL](https://img.shields.io/badge/PostgreSQL-18-4169E1?logo=postgresql&logoColor=white)](https://www.postgresql.org/)
[![Redis](https://img.shields.io/badge/Redis-8-FF4438?logo=redis&logoColor=white)](https://redis.io/)
[![Docker](https://img.shields.io/badge/Docker-ready-2496ED?logo=docker&logoColor=white)](https://www.docker.com/)
[![uv](https://img.shields.io/badge/uv-package%20manager-DE5FE9?logo=uv&logoColor=white)](https://docs.astral.sh/uv/)
[![Ruff](https://img.shields.io/badge/code%20style-ruff-D7FF64?logo=ruff&logoColor=black)](https://docs.astral.sh/ruff/)

</div>

---

## ✨ Возможности

| | |
| --- | --- |
| 🏗️ **API-first монолит** | Слои: views → services / selectors → models |
| 🔖 **Версионирование через заголовок** | `AcceptHeaderVersioning` — чистые URL без `/v1/` |
| 📖 **OpenAPI из коробки** | Swagger UI и ReDoc (drf-spectacular) |
| ⚙️ **12-factor конфигурация** | Всё через переменные окружения (django-environ) |
| 🔒 **Безопасный production** | HSTS, secure cookies, SSL redirect, whitenoise, gunicorn |
| 👤 **Кастомный пользователь** | Email-логин + `external_id` (UUID) для интеграций |
| 💓 **Health-check** | `/health/` с проверкой БД и Redis — готов для k8s-проб |
| 🧹 **Качество кода** | ruff · bandit · ty · pre-commit · pytest · GitHub Actions |
| 🐳 **Docker** | Multi-stage образ (non-root) + compose с PostgreSQL и Redis |

## 🚀 Быстрый старт

### 🐳 Docker (рекомендуется)

```bash
make env # создать .env из .env.example
make up # собрать и запустить postgres + redis + django
make logs # смотреть логи
```

➜ Приложение: http://localhost:8000 · Swagger UI: http://localhost:8000/api/docs/

### 💻 Локально

```bash
make env
make install # uv sync --group dev
make migrate
make run
```

> 💡 По умолчанию локально используется SQLite; для PostgreSQL задайте `DATABASE_URL` в `.env`.

## 🔖 Версионирование API

Используется `AcceptHeaderVersioning`: клиент передаёт версию в заголовке `Accept`,
URL остаются стабильными (`/api/users/...` без `/v1/`).

```bash
# ✅ Явно указанная версия
curl http://localhost:8000/api/users/me/ \
-H 'Accept: application/json; version=v1'

# ✅ Без версии — используется DEFAULT_VERSION (v1)
curl http://localhost:8000/api/users/me/

# ❌ Неподдерживаемая версия → 406 Not Acceptable
curl http://localhost:8000/api/users/me/ \
-H 'Accept: application/json; version=v999'
```

Версия доступна во view через `request.version`. Список поддерживаемых версий —
`ALLOWED_VERSIONS` в `config/settings/base.py`. Чтобы выпустить `v2`, добавьте версию
в `ALLOWED_VERSIONS` и ветвите логику по `request.version` (или подключайте другой
сериализатор), не меняя URL.

## 🌐 Эндпоинты

| Метод | URL | Описание |
| :---: | --- | --- |
| `POST` | `/api/users/register/` | 📝 Регистрация пользователя |
| `GET` | `/api/users/me/` | 👤 Профиль текущего пользователя |
| `GET` | `/api/schema/` | 📄 OpenAPI-схема |
| `GET` | `/api/docs/` | 📖 Swagger UI |
| `GET` | `/api/redoc/` | 📕 ReDoc |
| `GET` | `/health/` | 💓 Health-check (БД + Redis), 503 при сбое |
| — | `/admin/` | 🛠️ Django admin |

## 🏛️ Архитектура

Монолитное API-ориентированное приложение по стандартам **Twelve-Factor App**.
Бизнес-функционал инкапсулирован в изолированные приложения внутри `apps/` —
каждый модуль проектируется как независимый компонент, что облегчает будущий
вынос в микросервисы.

```mermaid
flowchart LR
Client([🌍 Клиент]) -->|"Accept: application/json; version=v1"| API

subgraph Django["🐍 Django Boilerplate"]
direction LR
API["🔌 API Layer<br/>(serializers, views)"] --> Domain["🧠 Domain Layer<br/>(services, selectors)"]
Domain --> Models["🗃️ Models"]
end

Models --> PG[("🐘 PostgreSQL")]
Domain --> RD[("⚡ Redis")]
```

## 📁 Структура проекта

Структура следует лучшим практикам ([HackSoft Django Styleguide](https://github.com/HackSoftware/Django-Styleguide),
[cookiecutter-django](https://github.com/cookiecutter/cookiecutter-django)) — три корневых пакета
с чёткими зонами ответственности:

```
├── 📂 apps/ # Бизнес-приложения (изолированные модули)
│ └── users/
│ ├── api/ # Слой API: serializers, views, urls
│ ├── services.py # Бизнес-логика (запись)
│ ├── selectors.py # Бизнес-логика (чтение)
│ ├── models.py
│ └── tests/
├── ⚙️ config/ # Только конфигурация проекта
│ ├── settings/ # base.py + dev.py / prod.py (split-settings)
│ ├── urls.py
│ ├── asgi.py
│ └── wsgi.py
├── 🧰 core/ # Переиспользуемый инфраструктурный код
│ ├── health.py # Health-check view
│ ├── integrations/ # Клиенты внешних сервисов
│ ├── mixins/ # Примеси для моделей (timestamps и т.п.)
│ └── redis/ # Redis-клиент
├── 🐳 Dockerfile # Multi-stage, non-root
├── 🐳 docker-compose.yml # postgres + redis + django
└── 📦 pyproject.toml # Зависимости (uv), ruff, bandit, pytest
```

| Пакет | Ответственность | Правило |
| --- | --- | --- |
| `apps/` | Бизнес-логика | Может импортировать `core/`, но не `config/` |
| `core/` | Инфраструктура, переиспользуемый код | Не знает о бизнес-логике |
| `config/` | Настройки, маршрутизация, WSGI/ASGI | Только конфигурация, без логики |

## 🛠️ Команды

```bash
make install # 📦 установить зависимости (с dev-группой)
make run # 🚀 запустить dev-сервер
make migrate # 🗃️ применить миграции
make makemigrations # 📝 создать миграции
make superuser # 👑 создать суперпользователя
make test # 🧪 запустить тесты
make lint # 🔍 ruff check + format check + bandit
make format # ✨ автоформатирование
make up / down # 🐳 docker compose up/down
```

## ⚙️ Конфигурация

Все настройки — через переменные окружения (см. [`.env.example`](.env.example)).

<details>
<summary><b>📋 Ключевые переменные окружения</b></summary>

<br/>

| Переменная | Назначение | По умолчанию |
| --- | --- | --- |
| `ENVIRONMENT` | `dev` или `prod` (выбор настроек) | `dev` |
| `DJANGO_SECRET_KEY` | Секретный ключ (обязателен) | — |
| `DJANGO_DEBUG` | Режим отладки | `False` (prod), `True` (dev) |
| `DJANGO_ALLOWED_HOSTS` | Разрешённые хосты через запятую | — |
| `DATABASE_URL` | URL базы данных | SQLite |
| `REDIS_URL` | URL Redis для кэша | `redis://redis:6379/1` |
| `DJANGO_LOG_LEVEL` | Уровень логирования | `INFO` |
| `GUNICORN_WORKERS` | Количество воркеров gunicorn | `4` |

</details>

<details>
<summary><b>🔒 Production-развёртывание</b></summary>

<br/>

`ENVIRONMENT=prod` включает: `DEBUG=False`, SSL redirect, HSTS, secure cookies,
JSON-only рендеринг DRF, манифестную статику (whitenoise) и запуск через gunicorn
(`MODE=prod` в docker-compose).

```bash
ENVIRONMENT=prod MODE=prod INSTALL_DEV=false docker compose up -d --build
```

</details>

## 🧹 Качество кода

```bash
uv run pre-commit install # включить pre-commit хуки
make lint # проверка
make test # тесты
```

CI (GitHub Actions) запускает линтеры и тесты на каждый push в `main` и pull request.

<div align="center">

![Footer](https://capsule-render.vercel.app/api?type=waving&color=0:44B78B,100:092E20&height=120&section=footer)

</div>
Loading
Loading