基础微服务项目架构构建总结


title: 基础微服务项目架构构建总结
date: 2022-11-16 14:08:04
tags:

  • 微服务
  • IDEA
    categories:
  • 开发实践
    cover: https://cover.png
    feature: false

1. Maven 依赖版本管理

1.1 Maven 依赖的优先级

1、最短路径优先

  • 工程中依赖了 B、C 两个 jar 包
  • 在 B jar 包内引用了 C jar 包版本为 1.0
  • 在工程内直接引用的 C jar 包版本为 2.0

Project -> B -> C(1.0) ,Project -> C(2.0)。由于 C(2.0) 路径最短,所以项目使用的是 C(2.0)

2、POM 申明顺序优先

如果 project -> B -> C(1.0) ,project -> D -> C(2.0) 这样的路径长度一样怎么办呢?

这样的情况下,Maven 会根据 POM 文件声明的顺序加载,如果先声明了 B,后声明了 D,那就最后的依赖就会是 C(1.0)

1.2 Maven 包版本控制

在项目顶层的父 POM 中可以定义如下的三方 jar 的版本定义(这里的 jar 包只是定义,并没有引用,即不会下载依赖)

<dependencyManagement>
  <dependency>
      <groupId>com.alibabagroupId>
      <artifactId>fastjsonartifactId>
      <version>1.1.43version>
    dependency>
dependencyManagement>

这样需要引用这个 jar 的子模块可以忽略版本定义直接引用(此时才真正下载依赖)

<dependencies>
  <dependency>
      <groupId>com.alibabagroupId>
      <artifactId>fastjsonartifactId>
    dependency>
dependencies>

1.3 多项目全局管理

随便打开一个 SpringBoot 的项目,打开 POM 文件,父级依赖都是

<parent>
  <groupId>org.springframework.bootgroupId>
  <artifactId>spring-boot-starter-parentartifactId>
  <version>${springboot-version}version>
parent>

点击这个 parent 的依赖进去看到顶级的父级依赖为

<parent>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-dependenciesartifactId>
    <version>${springboot-version}version>
parent>

继续点进去,里面已经没有任何 jar 的实际引用了,只有各种各样的 SpringBoot 或者 Spring 生态可能会依赖到的 jar 包的版本定义

Spring 通过定义一个顶层的父级版本依赖,只要是符合 SpringBoot 大版本下的 Spring 组件内的各个 jar 版本都是统一的,如果出现依赖升级的情况,不需要再去升级一个个组件的版本,直接升级父级的依赖管理 POM 中的版本即可

参照 Spring Maven 版本管理的思路,我们也可以定义这样一个业务的顶层 Maven 版本管理工程,如 common-dependency

  1. 版本管理工程的 POM 的父 POM 依赖 spring-boot
  2. 版本管理工程的 POM 内定义业务通用的一些 Maven 依赖版本
  3. 推送该工程至中央仓库(本地可以直接执行 maven install 打包到本地仓库)
  4. 业务应用将父 POMspring-boot 切换为 common-dependency

即在单个业务项目上抽离出一个版本管理工程作为父工程,所有的项目都使用统一的通用的依赖版本,假如某个项目需要自定义依赖或依赖版本,在项目的顶层 POM 文件中再进行定义即可,根据依赖优先级,会优先使用项目的 POM 文件中自定义的依赖版本

1.4 common-dependency

1、新建一个项目

基础微服务项目架构构建总结_第1张图片

基础微服务项目架构构建总结_第2张图片

2、删掉多余的其他文件

基础微服务项目架构构建总结_第3张图片

3、进行版本管理,父依赖为 spring-boot-starter-parent

注:这里 build 里的 spring-boot-maven-plugin 插件只是为了演示同样有依赖版本继承所以才在此处定义(注释状态),该插件只需定义在项目的主启动类的 POM 文件里即可。单模块项目不影响,假如为多模块项目,打包时会报错 Unable to find main class


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.7.5version>
    parent>

    <groupId>fangroupId>
    <artifactId>common-dependencyartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <modules>
        <module>demomodule>
    modules>

    <modelVersion>4.0.0modelVersion>
    <packaging>pompackaging>
    <name>common-dependencyname>
    <description>common-dependencydescription>

    
    <properties>
        <maven.compiler.source>8maven.compiler.source>
        <maven.compiler.target>8maven.compiler.target>
        <lombok.version>1.18.24lombok.version>
        <mybatis.plus.version>3.5.1mybatis.plus.version>
        <druid.version>1.2.9druid.version>
    properties>

    <dependencyManagement>
        <dependencies>
            
            <dependency>
                <groupId>mysqlgroupId>
                <artifactId>mysql-connector-javaartifactId>
                <version>8.0.19version>
            dependency>

            
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <version>${lombok.version}version>
            dependency>

            
            <dependency>
                <groupId>com.baomidougroupId>
                <artifactId>mybatis-plus-boot-starterartifactId>
                <version>${mybatis.plus.version}version>
            dependency>
            
            <dependency>
                <groupId>com.baomidougroupId>
                <artifactId>dynamic-datasource-spring-boot-starterartifactId>
                <version>${mybatis.plus.version}version>
            dependency>

            
            <dependency>
                <groupId>com.alibabagroupId>
                <artifactId>druid-spring-boot-starterartifactId>
                <version>${druid.version}version>
            dependency>
        dependencies>
    dependencyManagement>


project>

4、推送到中央仓库,本地的话 maven install 到本地仓库

基础微服务项目架构构建总结_第4张图片

5、其他项目父依赖改为 common-dependency,即可进行统一的依赖版本管理


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>fangroupId>
        <artifactId>common-dependencyartifactId>
        <version>0.0.1-SNAPSHOTversion>
    parent>

    <groupId>fangroupId>
    <artifactId>common-projectartifactId>
    <version>0.0.1-SNAPSHOTversion>

    <modelVersion>4.0.0modelVersion>
    <packaging>pompackaging>
    <name>common-projectname>
    <description>common-projectdescription>

    
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
        dependency>
    dependencies>
project>

依赖结构如下,不同的颜色的框表示不同的层级,或者说独立工程。如 common-dependency 为通用的顶层版本依赖工程,与 project 是独立的。project 将父依赖设置为 common-dependency,其内部模块的父依赖即相应的项目顶层 POM,module 是包含在 project 里的

基础微服务项目架构构建总结_第5张图片

单项目的话可以直接在项目顶层 POM 中进行版本控制即可,即从上图的 project 开始,或者说直接将 common-dependency 当成 project,这时 common-dependency 应该改为对应的项目名

基础微服务项目架构构建总结_第6张图片

基础微服务项目架构构建总结_第7张图片

2. 项目内划分模块

2.1 分模块

业务模块划分没有一个严格的业界标准,也没有说一定要按照怎么设计,这里根据个人使用总结为以下几个模块,具体使用可根据情况自己进行调整:

Maven 模块 模块描述 特殊说明
api 将 rpc 相关的接口、所必须的交互实体、枚举等定义在此处,提供给内部其他系统进行服务调用 单体服务可去除此模块
base/comm 与业务无关的通用配置定义在此处,如统一结果返回类、统一工具类等。具有业务无关性,与业务相关的工具类、枚举等可定义在具体的业务模块内
rpc api 包的 rpc 接口定义实现,一般来说是调用模块内的具体业务接口进行相关的处理 单体服务可去除此模块
service 具体的服务模块,进行业务处理,不特指某一个模块名
web 在此处定义启动类,配置文件(resources 目录),配置类(RedisConfig/MyBatisConfig)等项目配置

依赖结构如下,在之前的基础上添加项目内部划分的模块间的依赖关系

基础微服务项目架构构建总结_第8张图片

此处同样可以将上述划分好模块的 project 抽离出来成一个 common-project 用于多项目的统一的通用配置

2.2 common-project

1、同样的新建一个项目

基础微服务项目架构构建总结_第9张图片

2、删除其他多余文件,并按照前面的模块划分创建好对应的模块

基础微服务项目架构构建总结_第10张图片

3、顶层 POM 如下,父依赖为 common-dependency,并把各个模块依赖进去


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>fangroupId>
        <artifactId>common-dependencyartifactId>
        <version>0.0.1-SNAPSHOTversion>
    parent>

    <modules>
        <module>common-apimodule>
        <module>common-basemodule>
        <module>common-rpcmodule>
        <module>common-servicemodule>
        <module>common-webmodule>
    modules>

    <groupId>fangroupId>
    <artifactId>common-projectartifactId>
    <version>0.0.1-SNAPSHOTversion>

    <modelVersion>4.0.0modelVersion>
    <packaging>pompackaging>
    <name>common-projectname>
    <description>common-projectdescription>

    
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
        dependency>
    dependencies>

    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>fangroupId>
                <artifactId>common-apiartifactId>
                <version>0.0.1-SNAPSHOTversion>
            dependency>

            <dependency>
                <groupId>fangroupId>
                <artifactId>common-baseartifactId>
                <version>0.0.1-SNAPSHOTversion>
            dependency>

            <dependency>
                <groupId>fangroupId>
                <artifactId>common-rpcartifactId>
                <version>0.0.1-SNAPSHOTversion>
            dependency>

            <dependency>
                <groupId>fangroupId>
                <artifactId>common-serviceartifactId>
                <version>0.0.1-SNAPSHOTversion>
            dependency>

            <dependency>
                <groupId>fangroupId>
                <artifactId>common-webartifactId>
                <version>0.0.1-SNAPSHOTversion>
            dependency>
        dependencies>
    dependencyManagement>
project>

4、同时根据依赖关系依赖对应的模块

如 common-api 依赖 common-base



<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>common-projectartifactId>
        <groupId>fangroupId>
        <version>0.0.1-SNAPSHOTversion>
    parent>
  
    <modelVersion>4.0.0modelVersion>

    <artifactId>common-apiartifactId>
    <name>common-apiname>

    <dependencies>
        <dependency>
            <groupId>fangroupId>
            <artifactId>common-baseartifactId>
        dependency>
    dependencies>
project>

common-rpc 又依赖 common-api。由于 common-api 已经依赖了 common-base,所以不需要重复引入 common-base



<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>common-projectartifactId>
        <groupId>fangroupId>
        <version>0.0.1-SNAPSHOTversion>
    parent>

    <modelVersion>4.0.0modelVersion>

    <artifactId>common-rpcartifactId>
    <name>common-rpcname>

    <dependencies>
        <dependency>
            <groupId>fangroupId>
            <artifactId>common-apiartifactId>
        dependency>
    dependencies>
project>

common-service 依赖 common-base



<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>common-projectartifactId>
        <groupId>fangroupId>
        <version>0.0.1-SNAPSHOTversion>
    parent>

    <modelVersion>4.0.0modelVersion>

    <artifactId>common-serviceartifactId>
    <name>common-servicename>

    <dependencies>
        <dependency>
            <groupId>fangroupId>
            <artifactId>common-baseartifactId>
        dependency>
    dependencies>
project>

同样推送到中央仓库,本地的话 maven install 到本地仓库

3. 业务模块内分层

3.1 分层

目前很多业务系统都是基于 MVC 三层架构来开发的,MVC 三层架构中的 M 表示 Model,V 表示 View,C 表示 Controller。它将整个项目分为三层:展示层、逻辑层、数据层。MVC 三层开发架构是一个比较笼统的分层方式,落实到具体的开发层面,很多项目也并不会 100% 遵从 MVC 固定的分层方式,而是会根据具体的项目需求,做适当的调整

很多 Web 或者 App 项目都是前后端分离的,后端负责暴露接口给前端调用。这种情况下,一般就将后端项目分为 Repository 层、Service 层、Controller 层。其中,Repository 层负责数据访问,Service 层负责业务逻辑,Controller 层负责暴露接口。这里的 Service 层假如业务复杂,可再细分为如下三层:

  • Manager 层: 负责将 Dao 层中的数据库操作组合复用,主要是一些缓存方案,中间件的处理,以及对第三方平台的封装
  • Service 层: 更加关注业务逻辑,是业务处理层,将 Manager 组合过的操作和业务逻辑组合在一起,再封装成业务操作
  • Biz 层: 包含 Service 层,Service 层注重基础业务的处理,Biz 层是复杂应用层的业务层

当然,这只是其中一种分层和命名方式。不同的项目、不同的团队,可能会对此有所调整。不过,万变不离其宗,只要是依赖数据库开发的 Web 项目,基本的分层思路都大差不差

3.2 数据载体划分

3.2.1 PO(Persistant Object)/Entity

持久化对象,通过 DAO 层向上传输的数据源对象,实体属性与表字段一一对应。简单来说 PO 就是数据库中的记录,一个 PO 的数据结构对应着库中表的结构,表中的一条记录就是一个 PO 对象,通常 PO 里面除了 getter,setter 之外没有别的方法。概念与 Entity 一致

3.2.2 BO(Business Object)

业务对象,由 Service 层输出的封装业务逻辑的对象。BO 即 PO 的组合,如 PO1 是交易记录,PO2 是商品浏览记录,PO3 是添加购物车记录,等等组合起来形成 BO ,就是个人网站行为对象

一类业务就会对应一个 BO,数量上没有限制,而且 BO 会有很多业务操作,也就是说除了 getter,setter 方法以外,BO 会有很多针对自身数据进行计算的方法

现在很多持久层框架自身就提供了数据组合的功能,因此 BO 有可能是在业务层由业务来拼装 PO 而成,也有可能是在数据库访问层由框架直接生成

3.2.3 DO

DO 主要有两种定义

  • 一种在阿里巴巴开发手册中的定义,DO( Data Object),等同于上面的 PO
  • 一种是在 DDD(Domain-Driven Design)领域驱动设计中,DO(Domain Object),等同于上面的 BO

3.2.4 DTO(Data Transfer Object)

数据传输对象,这个传输通常指的前后端之间的传输,Service 或 Manager 向外传输的对象

BO 和 DTO 的区别

这两个的区别主要是就是字段的删减。BO 对内,为了进行业务计算需要辅助数据,或者是一个业务有多个对外的接口,BO 可能会含有很多接口对外所不需要的数据,而 DTO 在 BO 的基础上,只要自己需要的数据,然后对外提供。在这个关系上,通常不会有数据内容的变化

现在微服务盛行,服务和服务之间调用的传输对象能叫 DTO 吗?

DTO 本身的一个隐含的意义是要能够完整的表达一个业务模块的输出,如果服务和服务之间相对独立,那就可以叫 DTO;如果服务和服务之间不独立,每个都不是一个完整的业务模块,拆开可能仅仅是因为计算复杂度或者性能的问题,那这就不能够叫做 DTO,只能是 BO

3.2.5 VO(Value Object)

数据展示对象,通常是 Web 向模板渲染引擎层传输的对象,字段值与前端要求的字段名称保持一致。即 JSON 里的数据对象

VO 和 DTO 的区别

对于绝大部分的应用场景来说,DTO 和 VO 的属性值基本是一致的,而且它们通常都是 POJO,因此没必要多此一举,但这是实现层面的思维,对于设计层面来说,概念上还是应该存在 VO 和 DTO,因为两者有着本质的区别,DTO 代表服务层需要接收的数据和返回的数据,而 VO 代表展示层需要显示的数据

通常可能的区别如下:

  • 字段不一样,假如这个服务同时供多个客户端使用(不同门户),而不同的客户端对于表现层的要求有所不同,VO 可能会根据需要删减一些字段
  • 值不一样,VO 会根据需要对 DTO 中的值进行展示业务的解释

比如服务层有一个 getUser() 的方法返回一个系统用户,其中有一个属性是 gender(性别),对于服务层来说,它只从语义上定义:1-男性,2-女性,0-未指定,而对于展示层来说,它可能需要用“帅哥”代表男性,用“美女”代表女性,用“秘密”代表未指定

  • DTO 可能是这样的:{"gender": "男", "age": 35}
  • 经过业务解释的 VO 是这样的:{"gender":"帅哥", "age": "30~39"}

这时可能说,在服务层直接就返回“帅哥美女”不就行了吗?

对于大部分应用来说,这不是问题,但如果需求允许客户可以定制风格,而不同风格对于“性别”的表现方式不一样,又或者这个服务同时供多个客户端使用(不同门户),而不同的客户端对于表现层的要求有所不同,那么,问题就来了。再者,回到设计层面上分析,从单一职责原则来看,服务层只负责业务,与具体的表现形式无关,因此,它返回的 DTO,不应该出现与表现形式的耦合

3.2.6 Query

数据查询对象,各层接收上层的查询请求,超过 2 个参数的查询封装,禁止使用 Map 类来传输

3.3 结构图

示例结构图如下,个人理解可能不一样,可根据具体情况进行调整

基础微服务项目架构构建总结_第11张图片

4. 项目实践

4.1 结构图

这里由于是单体服务,去掉了 api 和 rpc 模块,假如有多个服务需要互相调用的话,加上 api 和 rpc 模块同样依赖对应的 common-api 和 common-rpc 模块即可,同时服务模块都依赖 common-service

基础微服务项目架构构建总结_第12张图片

4.2 实践

1、按照上面的模块划分,创建好项目

基础微服务项目架构构建总结_第13张图片

2、依赖 common-project 对应的模块

如 resource_nav_comm 依赖 common-base


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<parent>
		<groupId>fangroupId>
		<artifactId>ResourceNavigationartifactId>
		<version>0.0.1-SNAPSHOTversion>
	parent>

	<groupId>fangroupId>
	<artifactId>resource_nav_commartifactId>
	<version>0.0.1-SNAPSHOTversion>

	<modelVersion>4.0.0modelVersion>
	<name>resource_nav_commname>
	<description>resource_nav_commdescription>

	<dependencies>
		<dependency>
			<groupId>fangroupId>
			<artifactId>common-baseartifactId>
		dependency>
	dependencies>
project>

resource_nav_web 依赖 common-web


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>fangroupId>
        <artifactId>ResourceNavigationartifactId>
        <version>0.0.1-SNAPSHOTversion>
    parent>

    <modelVersion>4.0.0modelVersion>

    <artifactId>resource_nav_webartifactId>
    <name>resource_nav_webname>

    <dependencies>
        <dependency>
            <groupId>fangroupId>
            <artifactId>common-webartifactId>
        dependency>
    dependencies>
project>

resource_nav_system 模块依赖 common-service

3、然后再根据上面的模块内分层,在对应层进行相应的开发

你可能感兴趣的:(开发实践,微服务,架构,intellij-idea)