Skip to content
0

软件架构设计

层次型软件架构(⭐⭐⭐⭐)

软件架构贯穿于软件研发的整个生命周期内,具有三方面的重要影响:

  1. 利益相关人员之间的交流。
  2. 系统设计的前期决策。
  3. 可传递的系统级抽象。

C/S与B/S架构

层次架构是最通用的架构,常作为初始架构。其关注分离,即每层只负责本层的工作。

污水池反模式:请求流简单地穿过几个层,每层基本没做(或很少做)业务逻辑处理。根据二八原则,如果系统中超过20%的请求都是这样简单穿过,则应该考虑让一些层变成开放的,即允许某些层之间直接交互,绕过中间的一些层,减少不必要的层次穿透带来的性能开销,简化系统设计。

表现层

MVC

  • Model【模型层】:应用问题域中包含的抽象领域知识,即应用程序的主体部分。一个模型可为多个视图提供数据,提高应用的可复用性。

  • View【视图层】:用户看到并与之交互的界面。接受用户输入数据,向用户展示数据,但它不进行任何实际的业务处理。

  • Controller【控制层】:接受用户输入并调用模型和视图去完成用户的需求,是用户界面与模型的接口。一方面解释视图的输入,将其解释为系统能够理解的对象,同时识别用户运作,将其解释为对模型特定方法的调用。另一方面处理来自模型的事件和模型逻辑执行的结果,调用适当的视图为用户提供反馈。

从上图可看到,控制器接收用户的请求,并决定应该调用哪个模型来处理;然后模型根据用户请求进行相应的业务逻辑处理,并返回数据;最后,控制器调用相应的视图来格式化模型返回的数据,并通过视图呈现给用户。

MVC 模式的优点:

  1. 允许多种用户界面的扩展。
  2. 易于维护
  3. 功能强大的用户界面。

在 J2EE 体系中:

  • 模型:Session Bean、Entity Bean
  • 视图:JSP、JSF
  • 控制层:Servlet

MVP

MVP是MVC的变种,其优点如下:

  • 模型和视图完全分离,可以修改视图而不影响模型。
  • 可以更高效地使用模型,因为所有交互都发生在一个地方内部(Presenter)。
  • 可以将一个 Presenter 用于多个视图,而不需要改变 Presenter 的逻辑。
  • 如果把逻辑放在 Presenter 中,就可以脱离用户接口来测试这些逻辑(单元测试)。

特点:各部分是双向通信的。

主要应用场景:Android 开发。

MVVM

MVVM 为解决 MVP 中 UI 种类变多导致接口也会不断增加的问题。

  • 视图模型:通过 DataBinding 实现 View 和 Model 之间的双向绑定,其内容包括数据状态处理、数据绑定以及数据转换。

使用场景:数据驱动,数据操作特别频繁的场景。如 Vue 和 Angular 框架。

UIP框架

UIP 框架把表现层分为:

  • UIC(User Interface Componets):原来的表现层,用户看到的和进行交互的都是这个组件,它负责获取用户的数据并且返回结果。
  • UIP(User Interface Process Components):这个组件用于协调用户界面的各部分,使其配合后台的活动,例如导航和工作流控制,以及状态和视图的管理。用户看不到这一组件,但是这些组件为 UIC 提供了重要的支持功能。

表现层动态生成设计思想

基于XML界面管理技术,包括界面配置、界面动态生成、界面定制三部分。

中间层【业务层】

业务逻辑层工作流设计

业务逻辑层框架

数据访问层【持久层】

数据访问模式

  • 在线访问:每个数据库操作都会通过数据库连接不断地与后台的数据源进行交互。
  • DAO(Data Access Object):底层数据访问操作与高层业务逻辑分离。
  • DTO(Data Transfer Object):跨不同的进程或是网络的边界来传输数据。
  • 离线数据模式:从数据源获取数据后,存放到本地处理。
  • 对象/关系映射(ORM):应用系统中的对象与数据库中的数据表形成映射关系。

数据访问层设计

ORM(Object Relational Mapping):对象与关系数据之间的映射。

映射关系表:

面向对象关系数据库
类(class)表(table)
对象(Object)记录(record,行数据)
对象的属性(Attribute)字段(Field)

Java ORM实现技术对比:

维度HibernateMyBatis
简单对比强大、复杂、间接、SQL无关小巧、简单、直接、SQL相关
可移植性好(不关心具体数据库)差(根据数据库SQL编写)
复杂多表关联不支持支持

SpringBoot开发

分层SpringBoot
表现层Controller
业务层Service
访问层ORM
数据层MySQL、PostgreSQL等数据库

使用 ORM 框架:

  • 定义模型:Pojo、Entity、Bean、Domain 都可以简单理解为模型。
  • 定义接口:通常是 xxxMapper.java
  • Mapper资源文件:通常是 xxxMapper.xml
java
@Data
public class School {
    @Schema(title = "主键")
    Integer id;

    @Schema(title = "学校名称")
    String name;

    @Schema(title = "学校所在城市")
    String city;
}
java
public interface SchoolMapper {
    School read(Integer id);

    List<School> readMulti(String name, String city);

    void create(School school);
}
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.wgj.ssm.mapper.SchoolMapper">

  <select id="read" resultType="org.wgj.ssm.bean.School">
    select * from school where id=#{id}
  </select>
  <select id="readMulti" resultType="org.wgj.ssm.bean.School">
    select * from school
    <where>
      <if test="name != '' and name != null">
        name=#{name}
      </if>
      <if test="city != '' and city != null">
        city=#{city}
      </if>
    </where>
  </select>

  <insert id="create">
    insert into school(name, city) values(#{name}, #{city})
  </insert>
</mapper>
java
@Service
public class SchoolService
{
    @Autowired
    private SchoolMapper schoolMapper;

    public getSchoolById(Integer id)
    {
      return schoolMapper.read(id);
    }

    public getSchoolList(String name, String city)
    {
        return schoolMapper.readMulti(name, city);
    }

    public createSchool(School school)
    {
        schoolMapper.create(school);
    }
}
java
@RestController
@RequestMapping("school")
@Tag(name = "School", description = "学校相关接口")
public class SchoolController {

    private SchoolService schoolService;

    @Operation(summary = "根据ID查询", description = "根据ID查询学校")
    @GetMapping("/{id}")
    public School read(@PathVariable Integer id) {
        return schoolService.getSchoolById(id);
    }

    @Operation(summary = "查询多条", description = "查询学校")
    @GetMapping("")
    public List<School> readMulti(
        @Parameter(description = "学校名称") String name,
        @Parameter(description = "城市") @RequestParam(value = "city", required = false) String city)
    {
        return schoolService.getSchoolList(name, city);
    }

    @Operation(summary = "创建")
    @PostMapping("")
    public void create(@RequestBody School school) {
        schoolService.createSchool(school);
    }
}

物联网分层架构

分层描述设备
应用层解决信息处理和人机交互问题应用服务、智能终端
平台层操作系统、软件开发、设备管理平台、连接管理平台
网络层传递信息和处理信息网络、通信标准/协议
感知层解决数据获取问题传感器、芯片、通信模组、感知类智能设备/装置

大数据分层架构

RIA架构(⭐⭐)

RIA(Rich Internet Applications,富互联网应用)架构是一种通过在客户端(通常是Web浏览器)上运行富客户端应用程序来增强用户体验和应用程序性能的Web应用架构。它旨在结合传统桌面应用程序的响应性和互动性与Web应用程序的可访问性和易更新性。

RIA架构的主要特点

优点:反应速度快、易于传播、交互性强

典型例子:Google Docs,在浏览器上编辑Word文档、Excel等。国内的腾讯文档 Web 端应该也属于 RIA 架构。

面向服务的软件架构(⭐⭐⭐⭐)

SOA

面向服务架构(Service Oriented Architecture,SOA)可视为组件模型,其将系统整体拆分为多个独立的功能模块,模块之间通过调用接口进行交互,有效整合了应用系统的各项业务功能,系统各个模块之间是松耦合的。

SOA的优点:

SOA 架构以企业服务总线 ESB 作为核心基础设施,链接各个子系统,是集中式的技术架构。

SOA 架构的服务实现方式有 Web Service 和 RESTful API 等。

Web Service

3种角色:服务注册中心、服务提供者、服务请求者

3种操作:发布、查找、绑定

3种技术:

  • SOAP:通信协议,用于以服务的方式在互联网上发布有用的程序模块,SOAP = HTTP + XML
  • WSDL(Web Services Description Language):基于XML,用于描述WebService及其函数、参数和返回值等,为用户提供详细的接口说明。
  • UDDI(Universal Description Discovery and Integration):提供一种统一发布、查找和定位web服务的方法。

RESTful

REST(Representational State Transfer,表述性状态转移)是一种软件设计风格。

RESTful 是人们借助 HTTP、JSON、URI、HTML等 Web 服务开发中广泛使用的标准和协议,同时使用不同的编程语言编写客户端和服务端,通过 HTTP 方法操作资源状态,最后遵循 REST 设计原则实现的应用程序或服务架构,其可以降低开发的复杂性,提高系统的可伸缩性。

  • 资源:每一个对象、实体或数据都被抽象为一个资源。例如,用户、文章等都可以作为资源。
  • 表述:资源某一时间的状态,呈现形式有HTML、JSON、XML
  • 状态转移:使用GET、POST等方法
  • 超链接:通过超链接可与其它资源联系

5个原则

  • 网络上的所有事物都被抽象为资源
  • 每个资源对应一个唯一的资源标识
  • 通过通用的连接件接口对资源进行操作
  • 对资源的各种操作不会改变资源标识
  • 所有的操作都是无状态的

微服务

微服务属于面向服务架构(SOA)的一种,是在SOA基础上进一步发展的产物,去除了 ESB 企业服务总线,是一个真正意义上去中心化的分布式架构。

微服务体现在细粒度,主要用到容器技术。传统单体架构将所有功能放到一个进程中,而微服务每个功能放入一个独立进程中。

微服务的优点:

  • 复杂应用解耦:将单一模块应用分解为多个微服务,同时保持总体功能不变。每个服务专注于单一功能,通过良好的接口清晰表述服务边界。由于功能单一、复杂度低,易于小团队开发,提高开发效率且易于维护。
  • 独立性:独立开发、测试、部署以及独立运行(每个服务独立在其独立进程中)
  • 技术选型灵活:微服务是去中心化的,每个开发团队可以根据自身应用的业务需求发展状况选择合适的体系架构与技术,从而更方便地根据实际业务情况获得系统应用最佳解决方案。支持异构(如每个服务使用不同的数据库),架构转型或技术升级风险低。
  • 容错:传统单体架构下,某一模块发生故障,可能导致整个系统瘫痪。而微服务架构下,服务之间相互独立,故障被隔离在单个服务中,系统其他微服务可通过重试、平稳退化等机制实现应用层的容错。
  • 松耦合,易扩展:传统单体架构通过将整个应用复制到不同节点,从而实现横向扩展。当系统应用的不同组件在扩展需求上存在差异时,会导致系统的水平扩展成本很高。而微服务架构中每个服务之间是松耦合的,可根据实际需求实现独立扩展。

微服务面临的问题和挑战:

  • 分布式特点带来的复杂性,使服务发现与服务调用链跟踪变得困难。
  • 分区数据库体系下,受限于 CAP 理论而不得不放弃传统数据库的强一致性,转而追求最终一致性。
  • 测试复杂性:服务间的依赖关系使测试变得复杂。
  • 运维的复杂性:在监控、管理、分发及扩容等方面存在巨大挑战。

微服务与SOA的区别:

微服务SOA
能拆分就拆分整体的,服务能放在一起的都放在一起
细粒度粗粒度
使用轻量级的通信方式,如HTTPESB
类似独立子公司类似大公司里面划分了一些业务单元(BU)
纵向业务划分水平分多层
由单一组织负责按层级划分不同部门的组织负责
业务逻辑存在于每一个服务中业务逻辑横跨多个业务领域
组件小存在较复杂的组件

微服务架构模式方案

聚合器微服务

主要用于组合多个独立的微服务的响应,向客户端返回统一的聚合结果,减少客户端与多个微服务之间的交互,降低网络开销。聚合器微服务可直接组合结果返回,也可做业务处理后返回,它有自己的缓存和数据库。

比如电商的订单详情页有订单数据、商品数据、用户数据等。客户端只需要调用一个接口,该接口通过并发请求订单服务、商品服务、用户服务,将结果聚合返回给客户端。

聚合器微服务的变种——代理微服务:仅进行委派请求和数据转换工作。

聚合器微服务的扩展——分支微服务:通常根据不同的条件或参数值来执行不同的业务流程,允许同时调用两个或多个微服务链。

链式微服务

这个容易理解,需注意该模式下,所有服务调用都采用同步消息传递方式,在一条完整地服务链调用完成之间,客户端或调用服务会一直阻塞。因此,使用链式微服务要避免调用链过长,每个服务接口耗时也不宜过长。

数据共享微服务

该模式通常用于单体架构转换到微服务架构的过渡阶段。

异步消息传递微服务

链式微服务的同步消息传递容易造成阻塞,此时可以使用异步消息传递微服务模式,加快系统响应速度。例如客户端调用 A 服务,服务 A 将数据写入消息队列后就可以立即返回,耗时的逻辑处理可以由另外的一个或多个(如 Kafka 的消费组提供并发消费能力)服务来处理,处理完之后将结果保存并通知客户端来获取结果。

该模式可能会降低系统可用性,并增加系统复杂性,高可用的消息队列选型是关键。

微服务设计

微服务设计约束

  1. 微服务个体约束:每个微服务都是独立的,修改一个微服务不能影响另一个微服务。
  2. 微服务与微服务之间的横向关系:通过第三方服务注册中心来满足服务的可发现性。
  3. 微服务与数据层之间的纵向约束:数据是微服务的“私产”,访问时需要通过微服务。
  4. 全局视角下的微服务分布式约束:高效运维整个系统。

微服务设计应注意的问题

  1. 对于服务粒度的控制:通常来说,对于将暴露在整个系统外部的服务推荐使用粗粒度的接口,而相对较细粒度的服务接口用于企业系统架构的内部。
  2. 对于无状态服务的设计:服务不应该依赖于其他服务的上下文和状态。

微服务技术