卷柏小站

← 返回文章列表

关于Flask的MSV描述

发布时间:2026-05-14 阅读 · 142

Flask MSV 架构开发规范

Flask MSV 架构(MVC 在 Flask 中的具体实现,内部简称 MSV)— 分层:Model / Service / View

一、工程目录

flask-tutorial/                # 项目根目录
├── .venv/                     # 虚拟环境目录
├── app/                       # 业务应用主包目录
│   ├── __init__.py            # 应用工厂入口文件
│   ├── extensions.py          # 第三方扩展统一实例化:db、jwt、limiter、cors等
│   ├── errors.py              # 全局自定义异常基类、业务错误码枚举定义
│   ├── config/                # 应用核心配置以及安全配置目录
│   │   ├── config.py          # 应用核心配置、全局常量、版本、业务开关
│   │   └── security.py        # 全局安全配置:CORS、CSRF、XSS、CSP、密钥、加密策略等
│   ├── models/                # 模型层(M):定义ORM表结构、封装基础CRUD
│   ├── services/              # 服务层(S):业务逻辑、事务控制、多模型组合调用
│   ├── views/                 # 视图控制层(V):蓝图路由、请求接收、页面跳转
│   ├── templates/             # 模板展示层:Jinja2页面渲染
│   ├── static/                # 静态资源:CSS/JS/图片等
│   └── utils/                 # 通用工具目录:无业务逻辑、无硬编码App实例依赖
│       ├── pure/              # 纯工具:无任何Flask框架依赖
│       └── flask_tool/        # 依赖App上下文的工具函数
├── scripts/                   # 离线CLI脚本、数据初始化、批量任务
├── migrations/                # Flask-Migrate(Alembic)数据库迁移版本文件,纳入Git版本管理
├── tests/                     # 单元/业务测试代码目录
├── logs/                      # 日志持久化目录
├── data/                      # 运行时临时目录,可完整清理:缓存、临时文件,不打包、容器挂载
├── storage/                   # 持久化业务资源:用户上传文件、业务导出文件;Nginx隔离脚本执行权限
├── deploy/                    # 部署运维配置(Docker/Nginx/环境变量)
│   ├── nginx/
│   │   ├── nginx.conf         # Nginx主配置
│   │   └── ssl/               # SSL证书存放目录
│   ├── docker/
│   │   ├── gunicorn.py        # Gunicorn进程配置
│   │   ├── entrypoint.sh      # Docker容器启动脚本
│   │   ├── Dockerfile         # 镜像构建文件
│   │   └── docker-compose.yml # 容器编排配置
│   └── envs/                  # 多环境变量配置
│       ├── .env.dev           # 开发环境变量
│       └── .env.prod          # 生产环境变量
├── .gitignore                 # Git忽略清单
├── run.py                     # 本地开发启动入口
├── wsgi.py                    # 生产Gunicorn挂载入口
├── README.md                  # 项目说明文档
├── MANIFEST.in                # Python打包资源清单
└── pyproject.toml             # 项目依赖、打包配置

二、运行时业务调用链路(分层请求流转)

请求 → 视图(V) → 服务(S) → 模型(M) → 数据库(DB)
响应 ← 视图(V) ← 服务(S) ← 模型(M) ← 数据库(DB)

三、模块导入依赖关系(箭头代表「左侧导入右侧」)

app/__init__.py       → 导入 →  config, extensions, views(注册蓝图)
config                → 无外部导入,仅读取环境变量
extensions            → 无外部导入,仅实例化扩展空对象
models                → 导入 →  extensions.db
services              → 导入 →  models, utils.pure
views                 → 导入 →  services, utils.flask_tool
utils.pure            → 无框架/业务依赖,仅标准库、通用第三方包
utils.flask_tool      → 仅引入 current_app 上下文代理,不导入 app 实例

初始化顺序(create_app 内):
1. 加载 config
2. 初始化 extensions(app)
3. 注册 views(蓝图)

四、统一开发规范

一、通用基础规则

1、自定义中间件、请求钩子、信号、全局异常处理器,统一写在 create_app 内部。
2、app/views/__init__.py 统一聚合导出全部业务蓝图;app/models/__init__.py 统一导出全部 ORM 模型。
3、蓝图、模型优先从包顶层导入;模型数量 ≥15 时允许按需 from app.models.xxx import Xxx
4、视图按业务模块拆分子蓝图文件;蓝图间页面跳转仅使用 url_for,禁止互相导入蓝图对象。
5、离线脚本统一放在 scripts/,仅调用 create_app(),使用 with app.app_context(),禁止顶层导入 app 实例。
6、所有 .env 环境变量文件加入 .gitignore,密钥、敏感配置禁止提交代码仓库。
7、tests 仅可导入 models/services/utils,禁止反向导入业务上层模块污染主流程。

二、静态导入约束(仅允许从左向右导入,禁止反向回流)

1、app/__init__.py:导入 config、extensions、views
2、config:仅读取环境变量,不导入 extensions/models/services/views/utils
3、extensions:仅实例化扩展,不导入 config、models、services、views
4、models:仅导入 extensions.db,不导入 services/views/utils/app
5、services:导入 models、utils.pure,禁止导入 views、app
6、views:导入 services、utils.flask_tool,不导入其他蓝图、不导入 app 实例
7、utils:pure 无上层依赖,全场景可调用;flask_tool 仅函数内部使用 current_app,不直接导入 app

全局禁止行为:
1、views /services/models 反向导入 app、config、extensions
2、任意文件顶层编写 from app import app
3、utils 顶层直接引用 App 实例、上下文对象

三、运行时分层职责约束

1、视图 (V):仅参数校验、调用服务、组装返回响应
- 校验工具:webargs + marshmallow,Schema 文件放在 views/schemas/ 下
- ❌ 禁止直接操作模型 / 数据库、编写复杂业务逻辑
2、服务 (S):处理业务流程、事务、多模型联动
- ❌ 禁止操作 request/url_for、直接返回 HTTP 响应
3、模型 (M):仅数据表定义、基础单表 CRUD 封装
- ❌ 禁止复杂业务、事务、跨模型业务调用
4、业务异常必须向上抛出,由全局异常处理器统一拦截响应。
- ❌ 禁止在 models、services 内部捕获并吞掉 BusinessError
数据仅自下而上逐层透传,下层禁止反向调用上层。

四、异常与错误码规范

1、app/errors.py 定义异常体系:
- BusinessError(业务异常基类,含 code + message)
- ValidationError(参数校验失败,code=400)
- NotFoundError(资源不存在,code=404)
- ForbiddenError(权限不足,code=403)
2、错误码枚举 app/errors.py 中统一管理:

class ErrorCode(Enum):
    SUCCESS = 0
    PARAM_INVALID = 40001
    USER_NOT_FOUND = 40401
    PERMISSION_DENIED = 40301
    INTERNAL_ERROR = 50001

3、Service 层:遇到业务异常直接 raise BusinessError
4、View 层不写 try/except BusinessError,统一由 create_app 内注册的全局异常处理器拦截:
- register_error_handler(BusinessError) → 返回 JSON 或渲染错误页
- register_error_handler(404) → 自定义 404 页面
- register_error_handler(500) → 自定义 500 页面
5、API 统一响应格式:
json {"code": 0, "data": {...}, "message": "ok"}
- HTTP 状态码:始终 200(业务成功)/ 400 / 403 / 404 / 500(业务异常)
- body.code:业务错误码(ErrorCode 枚举值),成功时为 0
- body.message:人类可读的错误描述
6、Jinja2 渲染页面:全局异常处理器中判断 request.accept_mimetypes,优先返回 HTML 错误页(render_template),否则返回 JSON

五、日志规范

1、统一使用 Python logging 模块:
- View 层 / 应用工厂内:使用 current_app.logger
- Service 层 / Models 层:使用 logging.getLogger(__name__)
- create_app 内统一配置 logging.basicConfig 或 dictConfig
2、日志级别:开发环境 DEBUG,生产环境 INFO
3、日志统一格式:时间 | 级别 | 模块 | 请求ID | 消息
- 请求ID 由 create_app 内的 before_request 钩子生成(uuid4),
存入 flask.g.request_id,通过日志 Filter 自动注入每条日志
4、Service 层:记录业务关键节点(DEBUG)、业务/系统异常(ERROR)
5、View 层:记录请求入口/出口(INFO)、请求异常(ERROR)
6、Models 层禁止主动打印业务日志,数据库异常直接向上抛出;
SQL 执行异常统一由上层 Service/View 捕获记录 ERROR 日志
7、logs/ 目录按天自动轮转,日志文件保留 30 天
8、日志文件按业务类型分文件写入,各日志写入位置固定如下:
| 文件 | 内容 | 写入位置 | 补充说明 |
|------|------|---------|---------|
| logs/access.log | 请求方法、路径、客户端 IP、响应状态码、请求耗时 | before_request / after_request 钩子 | 全局切面统一采集,禁止业务层手动记录访问信息 |
| logs/error.log | 未捕获异常完整堆栈、500 服务错误 | 全局异常处理器 logger.exception() | 包含完整堆栈,用于线上故障排查 |
| logs/slow_query.log | 执行耗时超过 500ms 的 SQL 查询 | SQLALCHEMY_RECORD_QUERIES + after_request 判断 | 仅测试/预发常开,生产按需临时开启 |
| logs/security.log | 登录失败、JWT 校验失败、限流触发、可疑访问 IP | 安全中间件/认证信号内 logger.warning() | 用于安全审计、风险拦截溯源 |
| logs/cron.log | 离线脚本执行起止、脚本运行异常 | scripts 内部 logging.getLogger(__name__) | 所有批量任务、初始化脚本统一输出到此文件 |
| logs/api.log | 第三方外部接口完整请求/响应摘要 | Service 业务层 | 第三方调用对账、接口超时排查使用 |
9、业务审计日志(需持久化数据库,与文件日志区分):
- 用户操作/权限审计日志:models/ 定义独立审计 ORM 模型,services/ 封装入库逻辑,视图仅调用服务,不直接操作模型
- 前端行为埋点日志:前端自主采集,通过专用上报接口接收参数;视图仅做参数校验,交由 Service 统一入库存储
- 第三方接口关键流水信息(订单号、交易号、业务状态):Service 层持久化至数据库,用于长期对账、业务追溯
10、异常捕获约束:
禁止在 models、services 捕获后吞掉 BusinessError 与未知系统异常;
若业务需要临时捕获以补充上下文信息,仅允许修改异常 message 后重新 raise,
禁止在捕获处重复打印日志,由全局异常处理器统一记录。


附件

DEVELOPMENT_STANDARDS.md