app1 - bin: Bin 目录,存放可执行文件 - run-ci CI 运行文件 - deploy: 部署目录 - env.sh 初始化环境变量 - docker: - images: 自定义镜像目录 - image1: 镜像1 Dockerfile - image2: Dockerfile - ci: CI环境 docker 目录 - dev: 开发环境 docker 目录 - .bash_profile - docker-compose.yml 开发环境的 compose 文件 - docker-compose.ci.yml CI 环境的 compose 文件 - instance: - config.py 独立配置文件,不可入库 - front-end: 前端目录 - app1: 代码目录 - common: 通用模块 - services.py 服务定位器 - config: 配置目录 - common.py Flask 通用配置 - gunicorn.py Gunicorn 配置文件 - gunicorn.ini Gunicorn 的日志配置 - dev.py 开发环境的配置 - ci.py CI 环境的配置 - online.py 生产环境的配置 - celery: Celery 异步任务入口 - config.py Celery 的配置 - blueprint.py Flask 蓝图的定义 - bootstrap.py Flask WSGI 定义 - flask.py Flask App 的定义 - cas.py Flask-CAS 的定义 - services.py Thrift WSGI 定义 - admin.py 管理后台定义 - models.py ORM 定义 - module1: 模块1 - apis.py RESTful API 实现 - views.py 页面实现 - services.py Thrift 服务实现 - tasks.py Celery 任务实现 - helpers.py 助手逻辑 - utils.py util 逻辑 - module2: 模块2 - views.py - services.py - tasks.py - tests: 测试用例目录 - templates: 模板目录 - static: 静态文件目录,一般仅用于本地调试 - thrift: 接口定义目录 - idl: thrift 定义目录
Python 项目结构、惯例及原则
v1.0
李飞于 2016年1月24日
1. 工程目录结构
-
配置
-
app1/config
为配置目录。 -
通用配置存放在
common.py
中,dev.py
、ci.py
、online.py
分别是开发、CI、生产环境的配置,通过${PROJECT_NAME}_CONFIG
指定。 -
开发配置放在
instance
目录下的config.py
目录中。
-
-
docker
目录,项目的外部依赖通过 docker 解决并可以在任何 docker 环境下跑起来。 -
项目依赖的 virtualenv 由独立的 git 库提供,由
env.sh
负责导入。 -
app1
是项目的代码顶级目录,python 的包 import 路径从此开始,如app1/app1/admin/views.py
则为import koloda.admin.views
。 -
app1/app1
目录下的flask.py
中定义了 Flask 的 app,整个项目中的 app 定义在此。 -
Flask 的 SQLAlchemy 扩展对象
db
在app1/app1
下的models.py
中,与 db 相关的操作从此引用。 -
templates
为项目的模板目录,不使用分 app 的模版存放方式,一般此目录内容由前端源文件构建编译生成。 -
static
为整个项目的静态文件目录,不拆分到各个 app 下,一般此目录内容由前端源文件构建编译生成。 -
各个功能文件文件名使用复数单词形式,如:
apis.py
、tasks.py
等。 -
常量、枚举等通过 Thrift 的 IDL 定义,通过工具生成多种语言的版本,统一管理常量和枚举。
2. 字段命名规范
-
后缀与类型:
-
主键、外键等使用 id 后缀,为整型。
-
以 status、type 为后缀的字段名明确为整型,如枚举,按位存储等。
-
后缀 time 为时间型、后缀 date 为日期型,譬如不要使用 start_date 作为一个时间型字段。
-
以 key、title、label、name、description、uri、url 等为后缀的字段名明确为字符型。
-
-
不要使用对象名作为简单类型的字段名,为其增加可识别其类型的后缀,如:uri、key 等。
-
头像不要使用
avatar
作为字段名,使用avatar_key
或者avatar_url
。 -
字段名
user
不知道是user_id
还是user_name
。
-
-
各功能文件使用复数作为文件名,如:
views.py
、apis.py
等。
3. 代码分层
层次从下到上:
-
Thrift 定义的常量、枚举、简单数据结构,如:
ttypes.User
等。 -
flask.py
的 Flask app; -
models.py
的 ORM 定义,如:User
等。 -
公共模块
common
,譬如:ServiceLocator
、BaseRequest
等。 -
module:
-
utils.py
业务无关的逻辑,如:make_key
、calculate_type
等,一般不会有。 -
helpers.py
业务相关的逻辑,如:create_user
等。 -
tasks.py
Celery 异步任务; -
services.py
服务,不允许直接 import,通过ServiceLocator
调用。 -
apis.py/views.py
API/页面的逻辑;
-
Note
|
下层代码不允许 import 上层的代码。 |
4. 模块边界
模块内可以相互 import(必须遵循分层),模块间不允许任何代码级别的 import,各个模块提供 service 接口用于模块间调用。
5. Service
服务主要用于明确模块间的调用关系,隔离模块内部实现。service 位于模块下的 services.py
文件。
服务接口的设计原则:
-
所有服务接口均使用简单数据结构或者提供专门的接口数据结构(DTO),隐藏模块内部的数据结构(PO),可参考《聊聊对象》一文。
-
创建和更新接口采用肥大的远程外观实现,更新结构可提供指定仅更新的字段列表,减少接口的数量,降低复杂度。
-
服务接口尽可能是面向数据的。
-
一个独立的服务接口是一个完整的事务。
-
写操作的接口自带 commit;
-
若使用 SQLAlchemy 的 session,纯读操作的可不进行 remove,由 middleware 统一来做;
-
6. 外部资源
外部资源依赖都采用懒加载来初始化,包括不限于 mc、redis、thrift client 等。
由配置文件提供资源的路径,通过 IoC 机制来初始化。如果可能,请尽量提供 client manager,具有线程安全、池等功能。
尽量提供 Mock 的 client,允许测试时对资源进行替换。
7. 交付
-
Views:就是页面
-
Apis: API 接口,主要是 JSON 格式,供客户端或者 WebApp 使用。
-
Tasks:Celery 异步任务/定时任务,写操作居多。
-
Services:服务,主要用于内部使用。