从单体架构升级到微服务,在代码层面应注意的一些问题

因为近年来的移动端的发展和 2C模式 的红利,一些在风口的企业的业务获得爆发式增加。从架构层面来讲,业务驱动技术的变革,因此微服务架构的概念获得不少企业的青睐,由于能够解决服务的大流量和高并发以及稳定性的要求。前端

可是任何架构设计不是一蹴而就的,不能从起步就开始使用微服务,通常都是先经过单体架构来快速实现需求和抢占市场,而后再迭代式扩展。不能一口气吃个胖子。java

这几年本身有经历从单体到微服务的架构演变,也有直接参与到已经落地的微服务架构的项目中。见过好的架构设计,也见过一些孬的设计。好的架构设计,代码结构优雅,分层清晰,业务边界划分明朗,业务开发人员职责清晰。很差的设计就会致使代码混乱难以维护,对新需求没法快速应变,开发人员容易在补丁上打补丁,最后形成积重难返不得不重构。程序员

架构师须要从业务层面和将来业务发展有个全面的规划,让架构高可用,易扩展,灵活易使用,隐藏其复杂性。好的架构会让下面的业务开发人员按照既定的模式“傻瓜式”编程。web

既然第一步是单体架构,那么好的单体架构设计,为咱们后期的微服务拆分会有事半功倍的效果。避免重复劳动和过多的重写。咱们能够从这些方面进行一些有效的设计。数据库

划清业务边界

若是对将来的架构有微服务的考虑,那么在单体架构的时候就须要理清业务边界的问题,常见的简单划分就是以业务区分,例如:用户,商品,订单,支付,权限等等,具体的拆分程度可根据自身业务量和须要作划分。编程

当前流行的 DDD(领域驱动设计)能够做为一个指导原则,可是 DDD 比较偏向于理论,须要执行人员有良好的专业能力才能实施的比较好。服务器

代码层次结构

业务区分好以后,就是项目代码模块的设计。在代码层咱们须要根据MVC的模式,建议的代码设计层次以下:mybatis

├─demo-common
│  │  demo-common.iml
│  │  pom.xml
│  │  
│  └─src
│      ├─main
│      │  ├─java
│      │  └─resources
│      └─test
│          └─java
├─demo-dao
│  │  demo-dao.iml
│  │  pom.xml
│  │  
│  └─src
│      ├─main
│      │  ├─java
│      │  └─resources
│      └─test
│          └─java
├─demo-service
│  │  demo-service.iml
│  │  pom.xml
│  │  
│  └─src
│      ├─main
│      │  ├─java
│      │  └─resources
│      └─test
│          └─java
└─demo-web
    │  demo-web.iml
    │  pom.xml
    │  
    └─src
        ├─main
        │  ├─java
        │  └─resources
        └─test
            └─java

主要包含4个 module 模块架构

  • demo-common:基础模块,枚举,常亮类,工具类,配置类。
  • demo-dao:Dao层,mapper接口,mapper.xml。
  • demo-service:服务接口提供层,业务service接口。
  • demo-web:web层,Controller类,服务接口,与外部进行交互。

各模块之间的依赖关系为:并发

项目 Module 模块设计完成以后,每一个模块的内部 package 包如何设计呢?一般有两种划分模式:根据业务模块而后内部按MVC划分根据MVC模式而后内部按业务划分

一、根据业务模块划分,就是将每一个业务模块做为一个 package,而后每一个package里面有本身的 MVC,这样就作到业务模块的隔离。

二、根据 MVC 模式划分,先根据 MVC模式划分不一样的包,service,serviceImpl,dto等,而后再是各个业务本身的模型和服务接口。

针对上述的两个划分模式,我的的选择是根据业务模式划分,这样的包设计与后期微服务拆分有良好的匹配度,拆分的时候只须要将每一个业务包下的代码 Copy 到新的微服务中就好了,易迁移变更小。每一个模块中对不一样的业务经过 package 包名进行划分,例如:com.example.jajian.service.order、com.example.jajian.service.user等。

└─src
    ├─main
    │  ├─java
    │  │  └─com
    │  │      └─example
    │  │          └─jajian
    │  │              ├─common
    │  │              │      BaserService.java
    │  │              │      
    │  │              └─service
    │  │                  ├─order
    │  │                  │  ├─dto
    │  │                  │  │      OrderDto.java
    │  │                  │  │      
    │  │                  │  └─service
    │  │                  │      │  OrderService.java
    │  │                  │      │  
    │  │                  │      └─impl
    │  │                  │              OrderServiceImpl.java
    │  │                  │              
    │  │                  ├─pay
    │  │                  │  ├─dto
    │  │                  │  │      PayDto.java
    │  │                  │  │      
    │  │                  │  └─service
    │  │                  │      │  PayService.java
    │  │                  │      │  
    │  │                  │      └─impl
    │  │                  │              PayServiceImpl.java
    │  │                  │              
    │  │                  └─user
    │  │                      ├─dto
    │  │                      │      UserDto.java
    │  │                      │      
    │  │                      └─service
    │  │                          │  UserService.java
    │  │                          │  
    │  │                          └─impl
    │  │                                  UserServiceImpl.java
    │  │                                  
    │  └─resources
    └─test
        └─java

这样划分有什么好处?咱们单体架构的时候这样开发,当须要拆分红微服务的时候就能够直接将业务包拆分出去,由于每一个业务包里面就已经包含了全部的当前业务的关联业务类。

避免多边界业务的关联查询

单表关联因为业务须要并且简单方便易使用,因此多表关联查询在单体服务中是广泛存在的,若是咱们后期不须要作服务拆分则能够不须要考虑这方面的限制。

可是若是后期有微服务的规划,那么单体服务的时候若是没有作这个方面的限制,mybatis 的 mapper.xml中有过多的多表关联查询,这些关联查询会严重影响服务拆分的进度和复杂度。

若是同属于一个业务领域则可使用关联查询,而那些微服务拆分后属于不一样领域的业务则应避免使用多表关联查询,由于不一样的业务领域后期会被隔离拆分到不一样的服务当中,即数据库表都是分布在不一样的服务器上,全部服务之间都是经过RPC方式进行通讯,关联查询这时是没法处理的。

Controller层尽可能不作业务逻辑处理

常看到不少 coder 会在Controller 层作一些业务处理,我的认为这是很不规范的。Controller层是控制层,是和前端进行数据转换的,这里咱们应该只作请求的接受和返回,也无需作一些异常的try...catch...的捕获,异常能够经过全局通用拦截器统一进行拦截而后返回给前端异常提示语,提高代码的简洁性。

全部的参数校验也放到 service层,由于若是服务内部调用也可使用提升代码的共用度。固然分层领域模型最好也能区分开,

  • DO(Data Object):此对象与数据库表结构--对应,经过DAO层向上传输数据源对象。
  • DTO(Date Transfer Object):数据传输对象,service或Manager向外传输的对象。
  • VO(View Object):显示层的对象,一般是Web向模板渲染引擎层传输的对象。

这样区分开的好处是,当你须要对展现层数据进行特殊定制化的时候能够灵活变通,例如针对用户隐私信息身份证号,手机号码脱敏处理,或者用户ID加密显示等。

最后就是统一通用返回类了,经过这种格式的封装咱们将数据格式进行全局格式化,这里的状态码能够本身设计的更详细一点。

public class CommonResult<T> {

    public static final String CODE_SUCCESS = "0";
    public static final String CODE_FAILED = "9999";

    private String code;
    private T data;

    private String msg;

    private CommonResult(String code, T data, String msg) {
        this.code = code;
        this.data = data;
        this.msg = msg;
    }

    public boolean isSuccess() {
        return CODE_SUCCESS.equals(code);
    }

    public static <T> CommonResult<T> success() {
        return new CommonResult<>(CODE_SUCCESS, null, null);
    }

    public static <T> CommonResult<T> success(T data) {
        return new CommonResult<>(CODE_SUCCESS, data, null);
    }

    public static <T> CommonResult<T> success(T data, String msg) {
        return new CommonResult<>(CODE_SUCCESS, data, msg);
    }

    public static <T> CommonResult<T> failed() {
        return new CommonResult<>(CODE_FAILED, null, null);
    }

    public static <T> CommonResult<T> failed(String errorCode, String msg) {
        return new CommonResult<>(errorCode, null, msg);
    }

    public static <T> CommonResult<T> failed(String msg) {
        return new CommonResult<>(CODE_FAILED, null, msg);
    }

    public static <T> CommonResult<T> failed(T data, String msg) {
        return new CommonResult<>(CODE_FAILED, data, msg);
    }

    public static <T> CommonResult<T> failed(String errorCode, T data, String msg) {
        return new CommonResult<>(errorCode, data, msg);
    }
   
   // 省略 setter、getter
}

后记

以上只是列举了单体服务将来规划作微服务时须要注意的一部分简单内容,每一个人在作单体架构拆分红微服务的时候都会踩到各类各样的坑,这些坑成了咱们的开发经验,有了这些坑就会造成注意点,在咱们下次开发时就会具备指导意义。也许咱们程序员就是在踩坑和填坑的过程当中成长壮大起来的。

相关文章
相关标签/搜索