Django项目中业务逻辑代码应该写在哪里

什么是业务逻辑?#

业务逻辑就是程序中那些真正用于赚钱或省钱的业务逻辑与过程。 – 《Clean Architecture》

例如:

账户的余额不能为负数

用户选择激活后向用户发送一封激活邮件

业务逻辑应该写在哪里?#

Django框架只提供了View和Model两种对象,没有专门的业务逻辑层(类似Spring的Service层)。

The view layer#

Django has the concept of “views” to encapsulate the logic responsible for processing a user’s request and for returning the response.

View是用来处理用户的请求并返回结果的,业务逻辑代码写在View中是不合适的。

因为这样业务逻辑就与请求返回的协议耦合在一起,同时也不方便业务逻辑之间相互调用(只能通过HTTP请求来调用了)。

The model layer#

Django provides an abstraction layer (the “models”) for structuring and manipulating the data of your web application.

Django中Model采用的是 Active Record 模式(我非常喜欢Active Record)。

如果业务逻辑只影响某一个业务对象,可以考虑将业务逻辑放到Model中,像上面提到的:账户的余额不能为负,是可以考虑放在Model类中进行检查的。

值得一提的是网上有不少建议不要把业务逻辑检查的代码放在model.save()函数中,Django框架为model的自定义校验提供了专门的函数:https://docs.djangoproject.com/en/4.1/ref/models/instances/#validating-objects

Model的业务逻辑校验代码放在model.clean()函数中是比较合适的,需要注意的是model.clean()函数不会在model.save()中自动被调用,需要自己写代码调用。

如果在创建业务对象时需要使用的业务逻辑,可以放在Manager对象中。

一些业务逻辑中的查询(例如:获取全部未激活的用户),可以在Manager中增加对应的查询函数,或者使用QuerySet method。

在这篇文章中,作者强烈推荐在Model中编写业务逻辑代码,反对使用Service:https://www.b-list.org/weblog/2020/mar/16/no-service/

但我认为,那些跨多个业务实体或系统的逻辑,写在Model中显然不合适。

Service layer#

跨多个业务实体或系统的逻辑应该放在Service层,例如上面用户激活的例子:

services.py

def activate_user(user_id):
    user = User.objects.get(pk=user_id)

    # set active flag
    user.active = True
    user.save()

    # mail user
    send_mail(...)

    # etc etc

上面的代码来自于这个问题的回答:https://stackoverflow.com/questions/12578908/separation-of-business-logic-and-data-access-in-django

代码展示了用Service层实现业务逻辑的例子。由于Django中是没有Service对象的,所以只能自己来写。

好在自己写也不复杂,只需要在django的app中用一个services.py文件来实现service的业务逻辑即可。

实现风格#

网上有些建议在services.py文件中直接使用函数来组织业务逻辑代码,我认为这种面向过程风格的代码在需要进行依赖反转时会遇到问题。

在进行依赖反转时,需要在调用的模块定义接口,在另外的模块实现接口,使用面向对象风格的代码在进行依赖反转改造时会更简单。

在Spring的Service层中,通常是用一个类来实现一个模块中的所有业务处理,不过《Clean Architecture》的风格更偏向于每个业务处理用一个类实现。

依赖注入#

提到依赖反转,就会想到Spring中方便的依赖注入,确实能够很大程度上简化系统构造的代码。

Python也有一些依赖注入的库,例如:python-injectinjector

边界隔离#

引入了Service层后,还有一个问题需要考虑:各个模块(或子系统)之间隔离到什么程度。

参考资料:#

https://stackoverflow.com/questions/12578908/separation-of-business-logic-and-data-access-in-django

https://breadcrumbscollector.tech/the-clean-architecture-in-python-how-to-write-testable-and-flexible-code/

https://www.b-list.org/weblog/2020/mar/16/no-service/

https://github.com/HackSoftware/Django-Styleguide

comments powered by Disqus