如何构建集成Celery、Channels、Stripe的生产级Django SaaS后端架构?
生产级类型安全Django SaaS项目结构与组件集成最佳实践
一、核心目录结构(严格遵循关注点分离)
saas_project/ ├── config/ # 集中配置层(配置、入口、核心服务初始化) │ ├── __init__.py │ ├── settings/ # 多环境配置拆分 │ │ ├── __init__.py │ │ ├── base.py # 通用基础配置 │ │ ├── development.py # 开发环境专属配置 │ │ ├── production.py # 生产环境专属配置 │ │ └── types.py # 配置类型定义(类型安全核心) │ ├── asgi.py # Channels ASGI入口 │ ├── wsgi.py # WSGI入口 │ └── celery.py # Celery核心配置 ├── apps/ # 业务应用层(按模块高内聚拆分) │ ├── __init__.py │ ├── users/ # 用户模块(认证、订阅、通知) │ │ ├── __init__.py │ │ ├── models.py # 带类型注解的模型 │ │ ├── serializers.py # DRF序列化器(类型注解) │ │ ├── views.py # DRF视图/视图集(类型注解) │ │ ├── tasks.py # 用户相关异步任务 │ │ ├── consumers.py # 用户WebSocket消费者 │ │ ├── stripe/ # Stripe集成子模块 │ │ │ ├── __init__.py │ │ │ ├── webhooks.py # Stripe Webhook处理 │ │ │ └── services.py # Stripe业务逻辑封装 │ │ └── urls.py │ └── core/ # 通用核心模块(工具、中间件) │ ├── __init__.py │ ├── middleware.py │ └── utils.py ├── tasks/ # 全局跨模块异步任务 │ ├── __init__.py │ ├── background_tasks.py │ └── types.py # 任务类型定义 ├── consumers/ # 全局跨模块WebSocket消费者 │ ├── __init__.py │ ├── realtime.py │ └── types.py # 消费者消息类型定义 ├── static/ # 静态资源 ├── media/ # 媒体资源 ├── docker-compose.yml # Docker Compose服务编排 ├── Dockerfile # Django服务镜像构建 ├── requirements/ # 分层依赖管理 │ ├── base.txt │ ├── dev.txt │ ├── prod.txt │ └── types.txt # 类型检查专属依赖 ├── mypy.ini # Mypy类型检查配置 └── manage.py
目录职责说明
config/:统一管理所有配置、服务入口,避免配置分散;单独维护类型定义确保配置安全apps/:按业务模块拆分,每个模块内包含自身的模型、视图、任务、消费者,实现高内聚低耦合tasks//consumers/:存放跨模块的全局任务与消费者,避免业务模块间的耦合requirements/:分层管理依赖,将类型检查依赖单独拆分,便于环境切换
二、类型安全的配置体系
1. 用Pydantic Settings替代原生Django配置
在config/settings/types.py中定义类型化配置类,自动验证环境变量类型:
from pydantic_settings import BaseSettings, SettingsConfigDict from typing import Optional, List class Settings(BaseSettings): model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8") # 基础配置 DEBUG: bool = False SECRET_KEY: str ALLOWED_HOSTS: List[str] = [] # PostgreSQL配置 DB_NAME: str DB_USER: str DB_PASSWORD: str DB_HOST: str = "postgres" DB_PORT: int = 5432 # Redis配置(Celery + Channels) REDIS_URL: str = "redis://redis:6379/0" # Celery配置 CELERY_BROKER_URL: str = "redis://redis:6379/0" CELERY_RESULT_BACKEND: str = "redis://redis:6379/0" # Stripe配置 STRIPE_SECRET_KEY: str STRIPE_PUBLISHABLE_KEY: str STRIPE_WEBHOOK_SECRET: str settings = Settings()
2. 多环境配置映射
在config/settings/base.py中将Pydantic配置映射到Django原生配置:
from config.settings.types import settings # 基础Django配置 DEBUG = settings.DEBUG SECRET_KEY = settings.SECRET_KEY ALLOWED_HOSTS = settings.ALLOWED_HOSTS # 数据库配置 DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", "NAME": settings.DB_NAME, "USER": settings.DB_USER, "PASSWORD": settings.DB_PASSWORD, "HOST": settings.DB_HOST, "PORT": settings.DB_PORT, } } # Celery配置 CELERY_BROKER_URL = settings.CELERY_BROKER_URL CELERY_RESULT_BACKEND = settings.CELERY_RESULT_BACKEND # Channels配置 CHANNEL_LAYERS = { "default": { "BACKEND": "channels_redis.core.RedisChannelLayer", "CONFIG": { "hosts": [settings.REDIS_URL], }, }, } # Stripe配置 STRIPE_SECRET_KEY = settings.STRIPE_SECRET_KEY STRIPE_PUBLISHABLE_KEY = settings.STRIPE_PUBLISHABLE_KEY STRIPE_WEBHOOK_SECRET = settings.STRIPE_WEBHOOK_SECRET
development.py和production.py继承base.py,仅覆盖环境差异配置(如DEBUG、ALLOWED_HOSTS)。
三、各组件集成实现
1. PostgreSQL集成
在Docker Compose中定义Postgres服务:
services: postgres: image: postgres:15-alpine environment: POSTGRES_DB: ${DB_NAME} POSTGRES_USER: ${DB_USER} POSTGRES_PASSWORD: ${DB_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data/ ports: - "5432:5432"
2. Celery + Redis集成
- 核心配置
config/celery.py:
from celery import Celery from django.conf import settings app = Celery("saas_project") app.config_from_object("django.conf:settings", namespace="CELERY") app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
- Docker Compose添加Celery Worker和Beat服务:
celery_worker: build: . command: celery -A config worker --loglevel=info volumes: - .:/app depends_on: - postgres - redis celery_beat: build: . command: celery -A config beat --loglevel=info volumes: - .:/app depends_on: - postgres - redis
- 任务添加类型注解:
# apps/users/tasks.py from celery import shared_task from typing import int from apps.users.models import User @shared_task def send_welcome_email(user_id: int) -> None: user = User.objects.get(id=user_id) # 发送邮件逻辑 pass
3. Django Channels集成
- ASGI入口
config/asgi.py:
import os from django.core.asgi import get_asgi_application from channels.routing import ProtocolTypeRouter, URLRouter from channels.auth import AuthMiddlewareStack from django.urls import path, include os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.development") application = ProtocolTypeRouter({ "http": get_asgi_application(), "websocket": AuthMiddlewareStack( URLRouter([ path("ws/users/", include("apps.users.consumers.urls")), path("ws/realtime/", include("consumers.urls")), ]) ), })
- 消费者添加类型注解:
# apps/users/consumers.py from channels.generic.websocket import AsyncWebsocketConsumer import json from typing import Dict, Any class UserNotificationConsumer(AsyncWebsocketConsumer): async def connect(self) -> None: await self.accept() async def receive(self, text_data: str) -> None: data: Dict[str, Any] = json.loads(text_data) # 消息处理逻辑 await self.send(text_data=json.dumps({"message": "Received"}))
4. Stripe集成
- 封装Stripe服务逻辑(带类型注解):
# apps/users/stripe/services.py import stripe from django.conf import settings from typing import Any from apps.users.models import User stripe.api_key = settings.STRIPE_SECRET_KEY def create_subscription(user: User, plan_id: str) -> stripe.Subscription: customer = stripe.Customer.create(email=user.email) subscription = stripe.Subscription.create( customer=customer.id, items=[{"price": plan_id}], payment_behavior="default_incomplete", expand=["latest_invoice.payment_intent"], ) return subscription def handle_stripe_event(event: stripe.Event) -> None: event_type = event.type if event_type == "customer.subscription.created": # 处理订阅创建逻辑 pass
- Webhook处理视图:
# apps/users/stripe/webhooks.py from django.http import HttpResponse from django.views.decorators.http import require_POST from django.views.decorators.csrf import csrf_exempt import stripe from django.conf import settings from apps.users.stripe.services import handle_stripe_event @csrf_exempt @require_POST def stripe_webhook(request) -> HttpResponse: payload = request.body sig_header = request.META.get("HTTP_STRIPE_SIGNATURE") event: stripe.Event | None = None try: event = stripe.Webhook.construct_event( payload, sig_header, settings.STRIPE_WEBHOOK_SECRET ) except (ValueError, stripe.error.SignatureVerificationError): return HttpResponse(status=400) if event: handle_stripe_event(event) return HttpResponse(status=200)
5. Docker & Docker Compose集成
- Dockerfile示例:
FROM python:3.11-slim WORKDIR /app COPY requirements/base.txt requirements/dev.txt requirements/types.txt ./ RUN pip install --no-cache-dir -r base.txt -r dev.txt -r types.txt COPY . . EXPOSE 8000 CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
- 完整Docker Compose包含所有服务:django、postgres、redis、celery_worker、celery_beat、flower(可选监控)。
四、类型安全保障措施
- 全量类型注解:所有模型、视图、任务、消费者、服务函数都添加类型注解,包括参数、返回值、内部变量
- Mypy配置:
mypy.ini中启用Django、DRF、Celery、Channels的类型插件:
[mypy] plugins = mypy_django_plugin.main, mypy_drf_plugin.main, celery.contrib.mypy django_settings_module = config.settings.base strict = True
- 依赖管理:在
requirements/types.txt中添加类型检查依赖:mypy,django-stubs,djangorestframework-stubs,celery-stubs,pydantic-settings - 配置验证:利用Pydantic Settings自动验证环境变量类型,避免配置错误
内容的提问来源于stack exchange,提问作者aburayhanalif
相关产品推荐
相关产品推荐

