Web 开发选 MVC 还是 DDD?

Python七号

共 3805字,需浏览 8分钟

 ·

2022-01-26 13:39

距离上次更新已经有 7 天了,只要停下来一天,就会有第二天,第三天,越是不写,就越不知道写什么。这就是惯性的力量,无论是勤劳还是懒惰,都会产生惯性,于是勤劳者越来越勤劳,懒惰者越来越懒惰,学霸越来越霸,学渣越来越渣。时间一长,就会觉得自己根本无法改变自己,总会回到我们习以为常的状态。所以,朋友们,一定要警惕惯性,他使我们越来越好,也会使我们越来越坏,这不,我又逼着自己更新了。

之所以停止了更新,一方面是懒惰的小人击败了勤奋,另一方面是因为时间不够用。下班后就那么点时间,用于这个就不能用于那个,而我又是一个喜欢写代码的人,一旦开始写代码,时间就飞快的流失,从晚上 8 点写到晚上 12 点,也是一眨眼的功夫,明天还要上班,我不可能再熬夜。熬夜会把第二天废掉,得不偿失。最近在学习并尝试 golang 的 Web 开发,已经入门了,从以前 Django 的 MVC 模式,也渐渐的切换到了 Golang 的 DDD 模式,感觉 DDD 更具有面向对象风格,而 MVC 更像是一种面向过程的风格。

今天展开来说,什么是 MVC,什么是 DDD,它们分别适合什么样的场景。

什么是 MVC、什么是 DDD

MVC 三层架构中的 M 表示 Model,V 表示 View,C 表示 Controller。它将整个项目分为三层:展示层、逻辑层、数据层。熟悉 Django 的朋友可以这么映射,M 就是我们编写的 models.py 表示数据层,定义数据的存储,而 V 就是 views.py,里面存放着大量的业务逻辑,C 就是 urls.py 控制着路由的访问。前端请求首先访问 Controller,然后是 View,最后是 Model,这就是面向数据访问的过程来定义的架构。

MVC 的缺点就是虽然 M 和 V 是两份文件,但是数据和业务逻辑高度耦合的,也就是说,M 只负责了数据的定义,而数据的操作都在 V,一旦修改了 M,改 V 是真是苦不堪言,这种将数据与操作分离的特点,破坏了面向对象的封装特性,是一种典型的面向过程的编程风格。

与之对应,将数据和操作定义在一起,就是 DDD,全称叫领域驱动设计(Domain Driven Design,简称 DDD),领域驱动设计这个概念并不新颖,早在 2004 年就被提出了,不过现在又被大家重视起来,还是基于微服务的兴起,微服务就是大服务拆分为小服务嘛,这样就要做好业务模块划分,自然也就加速了领域驱动设计的盛行。

DDD 开发模式实现的代码,也是按照 MVC 三层架构分层的。Controller 层还是负责暴露 API 接口,M 层还是负责数据存取,V 层负责核心业务逻辑。它跟 MVC 的主要区别还是 M 和 V 的不同。传统的 M 只定义数据数据的结构,不定义数据的操作,而 DDD 开发模式,M 不仅定义数据的结构,还定义数据的操作。

比如 Django 的 M 和 V 可能是这样的:

M

# models.py
from django.contrib.auth.models import AbstractUser
from django.db import models

# 数据模型的定义

class User(AbstractUser):
    """
  数据的定义
    """

...
    class Meta:
        db_table = 'user'
        verbose_name = '用户信息'
        verbose_name_plural = verbose_name

M

# views.py

class UserViewSet(viewsets.ModelViewSet):
    """
  数据的操作、增删改查
    """
 ...

Golang 的 M

// User.go
type User struct {
 //数据的定义
 ...
}

//数据的操作、增删改查
func (u *User) BeforeSave() error {
 ...
}

func (u *User) Prepare() {
 ...
}

func (u *User) Save(db *gorm.DB) (*User, error) {
 ...
}

func (u *User) UpdateAUser(db *gorm.DB, uid uint32) (*User, error) {
 ...
}

func (u *User) DeleteAUser(db *gorm.DB, uid uint32) (int64, error) {
 ...
}

Golang 的 V

func (server *Server) DeleteUser(w http.ResponseWriter, r *http.Request) {

 vars := mux.Vars(r)

 user := models.User{}

 uid, err := strconv.ParseUint(vars["id"], 1032)
 if err != nil {
  responses.ERROR(w, http.StatusBadRequest, err)
  return
 }
 tokenID, err := auth.ExtractTokenID(r)
 if err != nil {
  responses.ERROR(w, http.StatusUnauthorized, errors.New("Unauthorized"))
  return
 }
 if tokenID != 0 && tokenID != uint32(uid) {
  responses.ERROR(w, http.StatusUnauthorized, errors.New(http.StatusText(http.StatusUnauthorized)))
  return
 }
 _, err = user.DeleteAUser(server.DB, uint32(uid))
 if err != nil {
  responses.ERROR(w, http.StatusInternalServerError, err)
  return
 }
 w.Header().Set("Entity", fmt.Sprintf("%d", uid))
 responses.JSON(w, http.StatusNoContent, "")
}

里面调用了 M 中的 DeleteAUser,以后修改 Model 时,只需要修改函数 DeleteAUser,而不需要修改 V

注意,MVC 和 DDD 与编程语言和框架都没有关系,因为正好手边有对应的代码,就拿来用了。

MVC 和 DDD 分别适合什么样的场景?

MVC 适合简单的业务,DDD 适合复杂的业务,为什么这么说呢?

如果系统业务比较简单,简单到就是基于 SQL 的 CRUD 操作,那么根本不需要动脑子精心设计 DDD 模型,MVC 模型就足以应付这种简单业务的开发工作。因为业务比较简单,即便我们使用 DDD,那模型本身包含的业务逻辑也并不会很多,设计出来的领域模型也会比较单薄,跟 MVC 差不多,没有太大意义。

你可能会问,DDD 不就是把部分数据的操作放在了模型里面吗,为什么就适合复杂的业务呢?

不夸张地讲,MVC 模式的开发,大部分都是 SQL 驱动(SQL-Driven)的开发模式。我们接到一个后端接口的开发需求的时候,就去看接口需要的数据对应到数据库中,需要哪张表或者哪几张表,然后思考如何编写 SQL 语句来获取数据。之后就是定义 models.py 编写 views.py 中的视图函数,你可以这么理解,views.py 中就是各种 SQL 语句。而 SQL 语句是不能复用的,新接口开发即使有部分相同的逻辑,也只能重新编写视图函数。

而 DDD 开发模式下,我们需要事先理清楚所有的业务,定义领域模型所包含的属性和方法。领域模型相当于可复用的业务中间层。新功能需求的开发,都基于之前定义好的这些领域模型来完成。越复杂的系统,对代码的复用性、易维护性要求就越高,我们就越应该花更多的时间和精力在前期设计上。DDD 开发模式,正好需要我们前期做大量的业务调研、领域模型设计,所以它更加适合这种复杂系统的开发。

最后的话

平时做 Web 开发,基本上,都是使用 MVC 架构,就连 Spring 的官方 Demo 也是 MVC 模式,也就是说 MVC 仍然是主流,因为项目之前就是 MVC 架构,保持不变的成本最小。

但 MVC 是典型的面向过程风格的设计,不适合复杂的系统,比如金融类系统、账务核算系统。DDD 架构把数据和操作封装在一起,对数据的操作可以复用,是面向对象风格的设计,比较适合复杂的业务系统。

一句话,简单的系统,就用 MVC,复杂的系统就用 DDD。

如果有帮助,还请点赞、转发、在看,感谢阅读。

浏览 32
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报