详细摘要 摘要
生成:2025-06-21 19:40摘要详情
- 音频文件
- 2018-08-31 | Radoslav Georgiev | Django structure for scale and longevity
- 摘要类型
- 详细摘要
- LLM 提供商
- openai
- LLM 模型
- gemini-2.5-pro
- 温度
- 0.3
- 已创建
- 2025-06-21 19:40:11
摘要内容
概览/核心摘要 (Executive Summary)
本次演讲由 Radoslav Georgiev 主讲,核心议题是如何构建可扩展、易于长期维护的 Django 项目。演讲者指出,随着项目功能增多和团队规模扩大,若将业务逻辑不加区分地放置在 Django 的默认组件(如模型的 save 方法、序列化器的 create 方法)中,会导致代码混乱、难以维护和扩展。
演讲者提出的核心解决方案是引入一个明确的服务层(Service Layer),作为业务逻辑的专属容器,从而将应用的核心逻辑与框架的接口(如 API、Views)分离开来。该架构模式主要包含两个新概念:
1. 服务(Services):用于处理所有“写”操作(如创建、更新、删除对象)。它们是独立的、使用关键字参数和类型注解的函数,封装了所有与数据库写入相关的复杂逻辑、事务和副作用(如调用任务、发送邮件)。
2. 选择器(Selectors):用于处理所有“读”操作(如列表查询、获取详情)。它们封装了复杂的查询、过滤和权限检查逻辑,解决了在模型属性中执行查询可能导致的性能问题。
通过这种模式,API 和视图(Views)变得极其“薄”和标准化,仅作为调用服务和选择器的入口,不包含任何业务逻辑。这种清晰的边界划分和可重复的模式,极大地提高了代码的可读性、可测试性和团队协作效率,是构建大型、长期 Django 项目的有效实践。
核心问题:业务逻辑应该放在哪里?
演讲者首先定义了业务逻辑:“所有与软件领域相关、非框架或工具类的代码。” 他提供了一个识别业务逻辑的经验法则:> “每一次你看到一个 if 判断,并且这个 if 不在检查字符串之类的工具方法里,那么它很可能就是一条需要被应用在软件中的业务规则。” 随着项目复杂化,开发者面临的核心挑战是如何组织这部分占代码库约80%的业务逻辑。
对常见错误模式的批判
演讲者分析了将业务逻辑放置在传统 Django 组件中的弊端:
-
模型(Models)
- 可接受的实践:
- 定义简单的、不产生额外数据库查询的属性(如
has_started)。 - 在
clean方法中实现额外的模型验证逻辑。
- 定义简单的、不产生额外数据库查询的属性(如
- 不推荐的实践:
- 在
save方法中堆砌大量业务逻辑(如创建关联对象、触发异步任务)。这会导致 “胖模型(Fat Models)”,违反了单一职责原则,难以测试和扩展。“不要在你的
save方法中添加大量代码……模型应该只关心数据模型,而不是业务逻辑。”
- 在
- 可接受的实践:
-
视图与API(Views & APIs)
- 许多教程会将业务逻辑直接写在视图的
post方法中。 - 使用 Django Rest Framework (DRF) 的
ModelViewSet虽然能用几行代码快速生成 CRUD API,但其创建对象的逻辑被隐藏在框架深层的抽象(如序列化器的create方法)中。 - 问题: 当同一业务逻辑需要在不同地方(如异步任务、管理命令、其他视图)复用时,会导致代码重复或产生不合理的调用(如为了复用逻辑而去实例化一个 API 类)。
- 许多教程会将业务逻辑直接写在视图的
-
序列化器(Serializers)
- 核心职责:
- 将 Python 对象或 ORM 对象转换为 JSON。
- 将传入的 JSON 数据转换为 Python 数据结构。
- 错误用法:
- 重写
create或update方法来实现复杂的对象创建逻辑。演讲者强调:> “创建对象不是序列化器的工作,而是你的工作。” 这同样违反了关注点分离原则。
- 重写
- 核心职责:
-
工具方法(Utility Methods)
- 工具方法应保持纯粹,作为处理通用数据结构的纯函数。它们不应包含特定领域的业务逻辑。如果一个函数处理的是业务领域问题,它就不应被归为“工具”。
解决方案:引入服务层(Service Layer)
为了解决上述问题,演讲者提倡引入新的“盒子”来专门存放业务逻辑,即服务层。
服务(Services):处理“写”操作
- 定义: 在每个 app 中创建一个
services.py模块,用于存放所有执行数据库写入(创建、更新)的逻辑。 - 规范与特点:
- 函数签名: 使用 仅关键字参数(keyword-only arguments) 和 类型注解(type notations),这使得函数调用更明确,也为代码提供了自文档化的能力。
- 职责: 封装所有与创建或修改对象相关的重度逻辑,包括调用其他服务、触发 Celery 任务、处理事务等。
- 原则: > “每一个接触数据库的非平凡操作都应该在服务中完成。”
- 异常与事务处理:
- 事务: 对于需要原子性操作的服务函数,直接使用
@transaction.atomic装饰器进行包裹。 - 异常: 在服务层中抛出自定义的业务异常,然后在 API 层通过一个共享的
mixin来捕获这些异常,并将它们转换为标准的 HTTP 响应(如 400 或 403 错误),避免返回 500 错误。
- 事务: 对于需要原子性操作的服务函数,直接使用
选择器(Selectors):处理“读”操作
- 定义: 对应地,在
selectors.py模块中存放所有从数据库读取数据的逻辑。 - 职责:
- 封装复杂的查询逻辑,特别是包含业务规则的过滤(如基于用户权限的过滤)。
- 处理权限检查和数据筛选。
- 解决的问题:
- 避免在模型属性中执行数据库查询,这种做法很容易在列表 API 中引发 N+1 查询问题(即在循环中为每个对象触发一次独立的数据库查询,导致性能低下)。
- 经验法则: > “如果你的模型属性会产生数据库查询,并且不能简单地通过
select_related优化,那么它应该被移到一个选择器中。”
架构影响与最佳实践
采用服务层架构后,项目的其他部分也相应地发生了变化。
API 变得“薄”且可重复
- API 视图不再包含任何业务逻辑,它们只负责:
- 接收请求和验证输入(通常通过一个输入序列化器)。
- 调用相应的服务或选择器。
- 使用输出序列化器格式化返回结果。
- 这种模式使得所有 API 的结构都高度一致(约5-7行代码),易于理解和维护,实现了 “可重复的模式”。
对序列化器的严格管理
- 防止复用: 为了避免因修改一个共享的序列化器而意外破坏多个 API,演讲者建议 将序列化器内联定义在 API 视图类内部。
- 输入与输出分离:
- 输出序列化器: 可以使用
ModelSerializer以节省代码。 - 输入序列化器: 严禁使用
ModelSerializer,应使用普通的serializers.Serializer。这可以防止ModelSerializer隐式地执行数据库创建操作,确保所有写操作都通过服务进行。
- 输出序列化器: 可以使用
清晰的测试策略
这种架构划分也带来了清晰的测试分层:
- 模型: 只需测试简单的属性和
clean方法中的验证逻辑,测试速度快。 - 服务与选择器: 这是测试的 核心和重点。测试需要与数据库交互,并大量使用
mock来隔离外部依赖(如其他服务、邮件发送等)。 - API: 由于 API 本身非常简单,通常 不需要进行单元测试,其功能由更高层级的 集成测试 覆盖。
结论与核心观点
演讲的核心思想是 通过明确的边界划分来管理复杂性。演讲者主张将 Django 应用划分为两个部分:
1. 核心(The Core): 由服务和选择器组成的业务逻辑层,它独立于框架,定义了应用的行为。
2. 外壳(The Shell): 由 Django 的模型、视图、API 等组件构成,负责与外部世界(如 HTTP 请求)交互,并将请求委托给核心业务逻辑层处理。
通过将业务逻辑严格限制在服务和选择器中,可以构建出扩展性强、易于维护、团队协作顺畅的 Django 应用。这是一种从经验中提炼出的、行之有效的架构模式。
评审反馈
总体评价
总结内容整体质量较高,准确提炼了演讲核心观点,结构清晰,语言专业。但仍存在少量事实性偏差和可优化空间。
具体问题及建议
- 事实准确性
- 问题:总结中"演讲者指出...将业务逻辑随意放置在 Django 默认提供的'盒子'..."的表述过于绝对化,原文实际区分了模型属性/clean方法(可接受)与save方法/序列化器(不推荐)的不同情况。
-
修改建议:调整表述为"演讲者分析了将业务逻辑不加区分地放置在Django默认组件(如模型的save方法、序列化器的create方法等)中的弊端"。
-
完整性
- 问题:遗漏了演讲者关于"utility方法"的讨论(应保持纯函数特性,避免包含业务逻辑)。
-
修改建议:在"对常见错误模式的批判"部分补充该要点。
-
格式规范
- 问题:执行摘要部分存在术语不一致,如"Services (服务)"中英文混排格式不统一。
-
修改建议:统一为"服务(Services)"或"Services(服务层)"。
-
内容组织
- 问题:"架构影响与最佳实践"部分将测试策略放在最后,与演讲实际流程(测试讨论出现在问答环节前)存在时序错位。
- 修改建议:将测试相关内容调整至"解决方案"部分末尾,或单独设立"测试策略"小节。
优化方向
- 增加演讲者关键引述的直接引用(如关于模型属性的"if判断规则"),增强说服力。
- 补充问答环节中关于异常处理和事务管理的实践要点。
- 优化技术术语的渐进式解释(如首次出现N+1问题时增加简短说明)。