应用架构(MVC + 分层架构 + 领域驱动COLA)

应用架构

使用阿里云的应用生成器:https://start.aliyun.com/bootstrap.html 生成应用架构

MVC

MVC模式(Model–view–controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。

MVC模式最早由Trygve Reenskaug在1978年提出[1],是施乐帕罗奥多研究中心(Xerox PARC)在20世纪80年代为程序语言Smalltalk发明的一种软件架构。MVC模式的目的是实现一种动态的程序设计,使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。除此之外,此模式透过对复杂度的简化,使程序结构更加直观。软件系统透过对自身基本部分分离的同时也赋予了各个基本部分应有的功能。专业人员可以依据自身的专长分组:

  • 模型(Model) – 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。
  • 视图(View) – 界面设计人员进行图形界面设计。
  • 控制器(Controller)- 负责转发请求,对请求进行处理。
    https://zh.wikipedia.org/wiki/MVC

架构项目依赖

http://xhope.top/wp-content/uploads/2021/09/a1.png

架构代码分析

├── pom.xml
├── project-model
│   ├── pom.xml
│   └── src
│       └── main
│           ├── java
│           │   └── com
│           │       └── yodean
│           │           └── project
│           │               ├── demos
│           │               │   └── web
│           │               │       └── User.java
│           │               └── mybatis
│           │                   ├── config
│           │                   │   ├── MVCMybatisDemoConfig.java
│           │                   │   └── MybatisDemoConfig.java
│           │                   ├── entity
│           │                   │   ├── MVCMybatisDemoUser.java
│           │                   │   └── MybatisDemoUser.java
│           │                   └── mapper
│           │                       ├── MVCMybatisDemoUserMapper.java
│           │                       └── MybatisDemoUserMapper.java
│           └── resources
│               ├── data.sql
│               ├── mappers
│               │   ├── MybatisDemoUserMapper.xml
│               │   └── mybatisdemouser-mapper.xml
│               └── schema.sql
├── project-service
│   ├── pom.xml
│   └── src
│       └── main
│           └── java
│               └── com
│                   └── yodean
│                       └── project
│                           ├── GetUserInfoService.java
│                           └── impl
│                               └── GetUserInfoServiceImpl.java
├── project-web
│   ├── pom.xml
│   └── src
│       └── main
│           ├── java
│           │   └── com
│           │       └── yodean
│           │           └── project
│           │               ├── controller
│           │               │   └── GreetingController.java
│           │               ├── demos
│           │               │   ├── GreetingThymeleafController.java
│           │               │   └── web
│           │               │       ├── BasicController.java
│           │               │       └── PathVariableController.java
│           │               └── mybatis
│           │                   └── controller
│           │                       └── MybatisDemoUserController.java
│           └── resources
│               ├── static
│               │   └── index.html
│               └── templates
│                   ├── greeting.html
│                   └── greeting2.html
└── start
    ├── pom.xml
    └── src
        ├── main
        │   ├── java
        │   │   └── com
        │   │       └── yodean
        │   │           └── project
        │   │               └── ProjectApplication.java
        │   └── resources
        │       ├── application.properties
        │       ├── static
        │       └── templates
        └── test
            └── java
                └── com
                    └── yodean
                        └── project
                            └── ProjectApplicationTests.java
  • project-model: 属于Model层,包含了数据库操作(通过Mybatis简化持久层操作)和数据模型。「包mybatis」就是DAO层,有实体、配置、Mapper。User.java 就是领域对象。
  • project-service: 业务的封装,依赖项目 project-model,属于Model层和Contrller层
@Service
public class GetUserInfoServiceImpl implements GetUserInfoService{

    @Autowired
    protected MVCMybatisDemoUserMapper mVCMybatisDemoUserMapper;

    @Override
    public void getUserInfoById(String id, Model model)
    {


        //search by id, get UserInfo
        MVCMybatisDemoUser user = mVCMybatisDemoUserMapper.queryUserInfo(id);
        model.addAttribute("name", user.getId())
                .addAttribute("age", user.getAge())
                .addAttribute("height", user.getHeight())
                .addAttribute("weight", user.getWeight());
    }
}
  • project-web: 类Controller属于controller层。templates下的文件是View层,使用 thymeleaf 模版技术。
@RestController
@RequestMapping("/usermybatis")
public class MybatisDemoUserController {

    @Autowired
    private MybatisDemoUserMapper mybatisDemoUserMapper;

    @Autowired
    private GetUserInfoService getUserInfoService;

    // http://127.0.0.1:8080/usermybatis/findAll
    @RequestMapping("/findAll")
    public List<MybatisDemoUser> findAll(){
        return mybatisDemoUserMapper.findAll();
    }

    @GetMapping("/greeting")
    public String greeting(@RequestParam(name="name", required=false, defaultValue="1") String name, Model model) {
        getUserInfoService.getUserInfoById(name, model);
        //这里返回的数据类型是String,但实际上更多的数据通过本函数中Model model传给了前端。返回值String也会被SpringMVC整合为一个ModelAndView,以供前端使用。(Controller可以返回多种数值,比如void、String、ModelAndView。同学们可以自行搜索学习)
        return "greeting";
    }

}

Controller层可以直接依赖model层,操作数据。

  • start:项目启动配置

MVC架构的特点

数据与视图分离,但是业务代码复用性不高,适合前后端不分离的小项目。

分层架构

架构项目依赖

http://xhope.top/wp-content/uploads/2021/09/a2.png

  • project-dao:数据访问层,与底层 MySQL、Oracle、Hbase、OB 等进行数据交互。对应MVC中的Model层,属于基础设施层。
└── com
    └── yodean
        └── project
            ├── dao
            │   ├── dataobject
            │   │   └── UserDO.java
            │   └── mapper
            │       └── UserMapper.java
            └── mybatis
                ├── config
                │   └── MybatisDemoConfig.java
                ├── entity
                │   └── MybatisDemoUser.java
                └── mapper
                    └── MybatisDemoUserMapper.java

1.包dao:

DO: 此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象

Mapper: 仓库持久化

2.包mybatis:

entity: Mybatis配置类

entity: 领域对象,数据库可能是一个join查询

Mapper: 仓库持久化

  • project-manager:通用业务处理层,依赖DAO层。它有如下特征:
    1) 对第三方平台封装的层,预处理返回结果及转化异常信息,适配上层接口。
    2) 对 Service 层通用能力的下沉,如缓存方案、中间件通用处理。
    3) 与 DAO 层交互,对多个 DAO 的组合复用。

  • project-api:业务逻辑服务层接口层。

└── com
    └── yodean
        └── project
            └── api
                ├── UserService.java
                └── model
                    └── UserModel.java

UserService.java

public interface UserService {
    String getUserName(Long id);
    UserModel addUser(UserModel user);
}

UserModel: 属于DTO对象

  • project-service:相对具体的业务逻辑服务实现层。依赖dao层、manager层、api层。
└── com
    └── yodean
        └── project
            └── service
                └── UserServiceImpl.java

UserServiceImpl.java

@Component
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;
    private static final BeanCopier copier = BeanCopier.create(UserModel.class, UserDO.class, false);

    public String getUserName(Long id) {
        UserDO userDO = userMapper.getById(id);
        return userDO != null ? userDO.getName() : null;
    }

    public UserModel addUser(UserModel user) {
        UserDO userDO = new UserDO();
        copier.copy(user, userDO, null);
        Long id = userMapper.insert(userDO);
        user.setId(id);
        return user;
    }
}

实现了模块api的接口 UserService ,并依赖模块dao做持久化操作 UserMapper

  • project-web:网关适配层,依赖dao层、api层。
└── com
    └── yodean
        └── project
            ├── mybatis
            │   └── controller
            │       └── MybatisDemoUserController.java
            └── web
                └── UserController.java

UserController.java

@Component
@RestController
public class UserController {
    @Autowired
    private UserService userService;
    @RequestMapping("/username")
    public String getUserName(@RequestParam("id") Long id) {
        return userService.getUserName(id);
    }
    @RequestMapping("/add")
    @ResponseBody
    public UserModel addUser(@RequestParam("name") String name, @RequestParam("age") Integer age) {
        UserModel user = new UserModel();
        user.setName(name);
        user.setAge(age);
        return userService.addUser(user);
    }
}

Web层可以直接依赖dao层,操作数据。

  • start: 项目启动配置,项目装配。

分层架构的特点

数据与视图分离,业务代码复用性高,适合前后端分离的项目。

阿里巴巴Java开发推荐分层结构如图所示

https://raw.githubusercontent.com/alibaba/p3c/p3c-pmd-2.1.0/p3c-gitbook/images/alibabaLevel.png

领域驱动COLA架构

DDD以业务为核心,解耦外部依赖,分离业务复杂度和技术复杂度。
DDD的架构模式有六边形架构、洋葱圈架构、整洁架构、COLA架构等。
COLA项目地址:https://github.com/alibaba/COLA
COLA参考资料:COLA 4.0:应用架构的最佳实践

COLA架构图

http://xhope.top/wp-content/uploads/2021/09/a3.png
由图所示,COLA架构采用了4层架构。Adapter层(用户接口层),App 层(应用层),Domain层(领域层),Infrastructure层(基础设施层)。

架构项目依赖

http://xhope.top/wp-content/uploads/2021/09/a4.png

  • project-controller:适配层(Adapter Layer),负责对前端展示(web,wireless,wap)的路由和适配,对于传统B/S系统而言,adapter就相当于MVC中的controller
  • project-app:应用层(Application Layer),主要负责获取输入,组装上下文,参数校验,调用领域层做业务处理,如果需要的话,发送消息通知等。层次是开放的,应用层也可以绕过领域层,直接访问基础实施层;
  • project-client:Client SDK,服务对外透出的API;
  • project-domain:领域层(Domain Layer),主要是封装了核心业务逻辑,并通过领域服务(Domain Service)和领域对象(Domain Entity)的方法对App层提供业务实体和业务逻辑计算。领域是应用的核心,不依赖任何其他层次;
  • project-infrastructure:基础实施层(Infrastructure Layer),主要负责技术细节问题的处理,比如数据库的CRUD、搜索引擎、文件系统、分布式服务的RPC等。此外,领域防腐的重任也落在这里,外部依赖需要通过gateway的转义处理,才能被上面的App层和Domain层使用。

DDD-经典四层架构应用
通过现实例子显示领域驱动设计的威力

源代码下载

architecture.zip

PO BO DTO VO DO的区别

  • DO(Data Object):此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。
  • DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。
  • BO(Business Object):业务对象,可以由 Service 层输出的封装业务逻辑的对象。
  • Query:数据查询对象,各层接收上层的查询请求。注意超过 2 个参数的查询封装,禁止使用 Map 类 来传输。
  • VO(View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。

更多参考: