将事件相关参数作为变量附加到对象模型是否为良好开发实践?
API对象保存场景校验逻辑最佳实践
这个问题本质是后端分层设计中,业务校验逻辑的边界划分问题,两种原生方案没有绝对的优劣,需要结合团队的项目特性、人员情况选择,业界主流实践会优先选择折中改良方案,同时规避两种方案的缺陷。
两种原生方案的优劣势对比
- 方案1(Controller/Service层执行校验)
符合单一职责设计原则,Model层仅保留和数据持久化强绑定的基础校验规则,和请求来源、业务场景相关的逻辑放在上层处理,不会出现Model层被无关属性、逻辑污染导致的模型膨胀问题。缺点是当新增对象创建入口时,容易出现开发者遗漏补充对应校验逻辑的问题,适合业务场景稳定、入口数量少、团队规范执行度高的项目。 - 方案2(Model层新增变量触发校验)
校验逻辑全量收敛在Model层,不管上层有多少个操作入口,只要调用对象的save方法就会触发对应校验,不会出现漏校验的问题,适合迭代速度快、入口数量多、人员流动率高的项目。缺点是会导致Model层积累大量仅和特定场景相关的临时属性,长期迭代后Model会变成逻辑大杂烩,维护成本大幅上升。
业界通用改良最佳实践
优先将校验逻辑按类型拆分,分别放在合适的层级,同时兼顾可维护性和可靠性:
- 拆分校验类型
- 基础持久化校验:所有场景下,只要数据需要入库就必须满足的规则,比如字段非空、长度限制、格式校验(手机号、邮箱等),这类规则统一放在Model层的save触发逻辑中,不需要额外参数控制。
- 场景化校验:和请求来源、特定业务场景强绑定的校验规则,比如本次场景中仅浏览器端请求需要执行的额外校验,这类规则不要散落在各个Controller中,也不要硬塞在Model的属性中,统一抽离为独立的公共校验模块。
- 优化参数传递方式
绝大多数ORM框架的save方法都支持传递额外的上下文/配置参数,不需要在Model实体上新增临时属性。可以将场景标识作为参数传入save方法,Model层的校验逻辑读取该参数判断是否需要执行对应场景的额外校验,既不会污染Model的字段定义,也能保证所有save操作都能触发对应校验,不会出现遗漏。
代码示例(Python Django 场景)
# 错误的方案2实现:Model新增无关临时属性 class Order(models.Model): amount = models.IntegerField() # 仅用于校验的临时属性,不会持久化到库,污染模型定义 is_browser_request = models.BooleanField(default=False, editable=False) def save(self, *args, **kwargs): if self.is_browser_request: # 执行浏览器端额外校验 if self.amount > 10000: raise ValidationError("浏览器端下单单笔金额不能超过10000") super().save(*args, **kwargs)
# 改良实现:通过save参数传递场景标识 class Order(models.Model): amount = models.IntegerField() def save(self, *args, **kwargs): # 基础持久化校验,所有场景都必须执行 if self.amount <= 0: raise ValidationError("订单金额不能小于等于0") # 读取传入的场景参数 scene = kwargs.pop("scene", None) # 仅对应场景执行额外校验 if scene == "browser_request": if self.amount > 10000: raise ValidationError("浏览器端下单单笔金额不能超过10000") super().save(*args, **kwargs) # Controller层调用示例 # 浏览器端请求入口,传递场景参数 def browser_create_order(request): order = Order(amount=request.data["amount"]) order.save(scene="browser_request") # 第三方OpenAPI请求入口,不需要对应校验,不传参数即可 def openapi_create_order(request): order = Order(amount=request.data["amount"]) order.save()
内容的提问来源于stack exchange,提问作者Random Dude
相关产品推荐
相关产品推荐

