WEB工程模块拆分设想

原创
03/04 12:51
阅读数 1.5W

WEB工程模块拆分设想

author: HuiFer

​ 首先来看目前使用到的一些开发模块,通常有dao、api、service、task、base、web五种,上述五种内容含义如下:

  • dao:存储数据库实体和mapper相关内容。
  • api:存储open feign相关接口,为SpringCloud提供服务。
  • service:存储当前项目中关于业务处理的代码。
  • task:存储关于定时任务相关代码。
  • base:存储请求参数,返回参数,枚举等JavaBean对象,不具备高级功能。
  • web:存储controller、拦截器、过滤器等于web有关代码。

上述这种模块分包是一个比较常用的方式,但是上述分包在笔者开发过程之中产生了很多问题,比如快速找到某一个对象的转换,快速找到某一个对象的缓存处理等问题。为此提出了一种新的分包模式。

领域模型

首先从HTTP请求开始,对于一个请求会有两个内容,请求对象,请求响应对象,一般而言这两个对象是一个比较独立不会有特殊方法的对象(即一个普通的JavaBean),对于这块内容笔者将其分在了领域模型(domain-model),在缓存中的对象也可以放在领域模型中,它们基本上不会具有额外的处理方法。领域模型中存储各类模型对象,它们不具备特殊方法。

可以被归属在领域模型模块的内容有:

  1. 请求对象:request params 、request-body
  2. 请求响应对象:response
  3. 缓存对象
  4. 异常对象

继续说回HTTP请求,一个请求参数进入到web应用时开发者通常会对这些请求参数进行验证,对于验证有一些比较简单的验证可以通过hibernate-validator 中提供的注解来提高编码效率,在这种注解使用过程中会有各种group。下面举一个例子,UserAppViewReq类作为一个请求参数,在使用了验证相关注解后变成下面代码:

public class UserAppViewReq {
  @NotBlank(
      message = "appId不能为空",
      groups = {First.class, Second.class})
  private String appId;
}

在上述代码中出现了First,Second两个组别,对于这种组别有时候可能多达4个,同时在翻阅代码时比如Controller进入,具体代码如下:

@PostMapping("/addUserAppView")
public ResultVO addUserAppView(@RequestBody @Validated(First.class) UserAppViewReq req) {
  userService.addUserAppView(req);
  return ResultVO.success();
}

在这段代码中还需要记录First这个group标记,才可以对应到实体对象中的一些验证。同时这些验证只能支持一些简单的验证,遇到逻辑验证比如数据库唯一这个验证器没有办法给开发者直接提供帮助,开发者还是需要去写一个单独的验证方法。因此对于参数验证这块提取出了一个新的模块领域模型验证(domain-validator),在这个模块中开发者需要编写领域模型中对于数据验证的内容。在这个模块中它需要具备数据库操作或者其他的第三方数据操作功能(比如HTTP请求)。

说完了验证相关内容在前文的例子中HTTP请求参数已经进入到后端应用,假设数据全部处理完成现在到了返回对象的时候,此时开发者经常需要做一些数据转换,将数据库对象转换成请求返回对象。举一个例子:用户名密码,两者只能返回用户名,密码一般不会返回,通常这部分操作是在Service中进行,笔者认为这部分操作应该独立出来,它可以提取出一个新的模块领域模型转换(domain-convert)。在这个模块中笔者希望是一个独立的只有领域模型转换的一个工程,不希望有一些数据库或者缓存的操作,这只是希望,开发者还是可以在这个模块中进行一些数据库或者缓存的查询操作。但是不能直接操作RedisTemplateDataSource

领域模型小结

通过前文的描述可以提取出如下的一些maven工程

  • domain:存储领域模型相关工程
    • domain-convert:领域模型转换工程,负责领域模型的转换。
    • domain-model:领域模型存储工程,负责存储各类领域模型。
    • domain-validator:领域模型验证工程,负责对领域模型进行验证。

注意:在当前版本的领域模型中未对领域事件进行定义。有关领域事件可以使用SpringEvent相关内容作为补充

Controller

下面将介绍Controller相关的分类,对于一个WEB项目项目通常会有公开到互联网的接口也会有仅限于内部局域网访问的接口,在这两者之间可能还会存在一些公共的接口,对此可以总结下面三种类型的Controller:

  1. 公共Controller
  2. 互联网Controller
  3. 局域网Controller

公共Controller被互联网Controller和局域网Controller引用。根据这样的一个设计思路可以做出如下的maven工程:

  • controller:存储controller相关工程
    • controller-common:存储公共的Controller接口。
    • controller-inner:存储局域网内可访问的Controller接口。
    • controller-public-network:存储互联网上可以访问的Controller接口。

注意:在controller工程种只存有controller接口定义和控制层的一些基本处理不具备业务处理能力

业务相关分层

一个常规的业务处理的处理中开发者至少需要用到对象,数据库,缓存(某些项目可能没有)这三者。下面将根据这三者常用操作对象进行工程模块的划分。对象这块内容各位可以将其分数到领域模型种,这里不过多讨论,下面先来看数据库层面内容。

DAO

通常进行数据库交互会使用ORM框架,本文以MyBatis框架为例来对DAO层如何进行工程划分。在Mybatis中开发时可能会引入一些插件这些插件通常都需要通过Java类来进行配置或者配置文件等,根据此可以建立数据库配置工程(db-config),除此之外在MyBatis中还有两个关键要素一个是数据库实体对象另一个是mapper,这两个都需要独立进行拆分,数据库实体对象可以i建立数据库实体(db-entity)工程,mapper可以建立mapper工程

接下来对数据库实体的单独拆分进行说明,在一个单表对象的增删改查操作过程中请求参数对象有可能可以直接转换为数据库对象,因此为了避免多余的Java类引入,在domain-convert工程中可以只引入db-entity,来达到最小依赖原则。同理对于缓存也是一个含义因此将数据库实体单独拆分出一个工程。

根据前文的设计思路可以创建出如下maven工程

  • database-object:存储DAO相关工程。
    • db-config:存储数据库配置相关内容,主要是以MyBatis插件配置为主。
    • db-entity:存储数据库实体。
    • mapper:存储mapper接口和mapperXML文件。

缓存

接下来介绍缓存应该如何做好工程分组,首先需要知道缓存的对象可能是那些,前文提到了两种。第一种是在domain-model中存放的缓存对象,第二种是数据库实体对象(db-entity工程)。通过这两点可以确认缓存依赖于domain-model和db-entity工程。依赖项解释完成下面来看具体的工程分类,首先对于缓存的使用通常是redis,对于redis会有一些RedisTemplate相关的配置,这部分配置会被拆分成一个单独的配置工程(cache-configuration),此外还有缓存接口和缓存接口实现两个工程,拆分这两个工程的主要目的是为了对外直接提供缓存调用,配合dubbo框架进行使用。

根据前文设计思路可以创建出如下maven工程

  • cache:存储缓存相关工程
    • cache-api:存储缓存交互接口
    • cache-api-impl:存储缓存交互接口的实现类
    • cache-configuration:存储缓存相关的配置类

业务处理能力

业务处理能力即项目的核心,提供了本项目的所有功能。在这个工程中只有两个子工程,第一个是api第二个是api实现。在api实现工程中编写的内容就是常规开发中需要编写的代码,在这个模块中主要是将前文提到的各种依赖关系引入。

首先需要引入的依赖是数据库层面的两个依赖(db-entity和mapper)它们可以提供数据库交互(增删改查),继续引入的依赖是domain工程中的三个依赖(domain-convert、domain-model、domain-validator),其次是缓存相关依赖(如果没有缓存层可以不引入),需要引入的是(cache-api和cache-api-impl)。引入完成这些依靠即可进行业务相关开发。

根据上述思路可以将业务处理能力分为下面几个mavne工程

  • infrastructure:存储业务处理能力相关工程
    • infrastructure-api:存储业务相关的接口定义
    • infrastructure-api-impl:存储业务相关的接口实现类

最后业务处理能力要和controller工程中的三个工程进行整合,这部分整合根据项目需求进行整合即可

注意:在业务处理能力相关工程拆分时并没有按照网络环境进行区分。

项目内自定义配置

在项目开发过程中难免会定义一些项目内的配置,这部分配置不是SpringBoot的配置也不是SpringCloud相关的配置,而是在项目中独立的配置,比如友盟推送的一些配置。这部分配置需要有一个单独的地方来进行存储,由于配置类在SpringBoot中通常是一个简单的Java对象,因此配置工程不会有太多的子工程,目前配置工程的maven工程定义如下:

  • project-configuration:存储项目内自定义的配置类

注意:这部分自定义配置通常会引入到业务处理能力的模块中,即infrastructure-impl工程中。

启动类

接下来将介绍关于启动相关内容。根据前文提到的一些内容可以推论出下面三个启动类:

  1. 对内部局域网使用的启动类
  2. 对互联网使用的启动类
  3. 定时任务启动类

说是说三个启动类,但是它们应该是一个独立的工程,对于web工程来说他没有自己独立的拦截器、过滤器等内容,对于定时任务而言它会根据项目所选择的定时任务框架进行定制处理。

根据上述思路可以做出如下maven工程

  • start:存储启动工程
    • inner-web-start:存储内部局域网使用相关启动类及相关内容。
    • public-network-web-start:存储互联网使用相关启动类及相关内容。
    • task-start:存储定时任务启动类及相关内容。

在这要说明一些依赖关系,在两个web工程中都需要引入与之对应的controller工程,除此之外还需要引入额外的两个配置工程:数据库配置工程(db-config)和缓存配置工程(cache-configuration),定时任务同理按需引入需要的工程模块即可。

还有另外一种情况是内部RPC调用的情况,这里讲到的基本上都是web应用,都需要通过http协议进行调用,某些时候http并不是最优解还会有其他的RPC方式,比如dubbo。在笔者遇到的开发过程中有时候缓存层可能被其他项目进行调用,此时就会在start工程中建立一个dubbo-starter工程将需要的内容放到这个工程中然后发布一个dubbo项目对方即可直接通过dubbo来进行接口调用。其他的业务处理层也可以通过这样的方式开放接口。

对外提供服务

下面介绍对外提供服务,一般情况下工程是以WEB工程的形式运行,对外提供服务有两种模式第一种是HTTP-CLIENT另一种是OpenFeign,在HTTP-CLIENT中又可以细分httpclient和okhttp。根据这个思路可以做出如下maven工程

  • open-api:存储对外提供服务的工程。
    • with-http-client:存储httpClient相关工程。
      • open-api-httlclient:存储基于httpClient实现的外部服务。
      • open-api-okhttp:存储基于okHttp实现的外部服务。
    • with-open-feign:存储基于OpenFeign的外部接口调用服务。

其他

项目地址:https://github.com/huifer/spring-boot-template-project.git

展开阅读全文
打赏
2
3 收藏
分享
加载中
更多评论
打赏
0 评论
3 收藏
2
分享
返回顶部
顶部