【软件系统架构】单体架构

一、引言

在软件开发的漫长历程中,架构的选择一直是至关重要的决策。单体架构作为一种经典的架构模式,曾经在许多项目中发挥着不可替代的作用。虽然如今微服务等架构逐渐流行,但理解单体架构对于深入掌握软件架构体系仍然有着重要意义。

二、单体架构定义

单体架构是一种将所有功能模块(如业务逻辑、数据访问、用户界面等)都打包在一个单一的可执行程序中的软件架构。就像是一个大的容器,里面包含了应用程序的所有部分,各个部分紧密耦合,共享代码库、数据库等资源。

三、单体架构发展历史

早期的软件系统相对简单,单体架构因其简单直接的构建方式而被广泛采用。在计算机技术发展的初期,硬件资源有限,开发人员更倾向于构建紧凑、一体化的应用。随着业务需求的不断增长,单体架构也在不断扩展,但基本的架构模式在很长一段时间内保持不变。例如,许多传统的企业级应用,如早期的ERP系统,大多采用单体架构构建。

四、单体架构特点

(一)简单性

开发相对简单,所有功能都在一个项目中,对于小型项目或者团队经验不足的情况下,易于上手。开发人员可以快速搭建起一个功能完整的应用,不需要处理复杂的分布式系统的通信、协调等问题。

(二)易于部署

整个应用只需要部署一个单元,与分布式系统相比,部署过程更加直接。不需要考虑多个服务之间的部署顺序、依赖关系等复杂情况。

(三)紧耦合

各个功能模块之间相互依赖,共享代码和数据结构。这种紧耦合的特性使得在修改一个模块时,可能会影响到其他模块的功能,导致系统的可维护性随着项目规模的扩大而降低。

五、单体架构细分类型

(一)分层式单体架构

  • 架构特点
    • 通常按照功能将应用分为表示层、业务逻辑层和数据访问层。表示层负责与用户交互,业务逻辑层处理业务规则,数据访问层与数据库交互。这种分层结构使得代码具有一定的组织性,便于开发人员理解和维护。
    • 各层之间通过接口进行通信,上层依赖下层,下层为上层提供服务。例如,表示层调用业务逻辑层的方法来处理用户请求,业务逻辑层再调用数据访问层的方法来获取或存储数据。
  • 适用场景
    • 适用于业务逻辑相对简单、规模较小的应用。比如一些小型的企业内部管理系统,如员工考勤系统,主要功能集中在数据的录入、查询和简单的统计分析,分层式单体架构可以很好地满足需求。

(二)模块化单体架构

  • 架构特点
    • 将应用划分为多个模块,每个模块负责特定的功能集。模块之间有一定的独立性,但仍然在同一个代码库中。例如,在一个电商应用中,可以将用户管理、商品管理、订单管理等分别作为不同的模块。
    • 模块之间可能存在一定的依赖关系,通过定义良好的接口来进行交互。这种架构在一定程度上提高了代码的可维护性和可扩展性,相比分层式单体架构,模块之间的耦合度更低。
  • 适用场景
    • 对于中等规模、功能相对较多但还不足以拆分微服务的应用比较适合。比如一个具有多种业务功能,如在线教育平台中的课程管理、学生学习记录管理、教师管理等功能的应用,可以采用模块化单体架构。

六、单体架构优缺点

分类 具体描述 示例说明
优点
开发效率 单代码库快速开发,无需处理分布式系统复杂性,适合快速原型开发和简单需求项目。 初创公司开发用户管理系统,1 周内完成核心功能并上线验证市场。
资源利用 仅需单个运行环境,硬件资源占用低,适合小型项目或资源受限场景。 小型企业使用 1 台服务器部署单体 ERP 系统,支撑 50 人同时在线办公。
缺点
可维护性差 代码库庞大复杂,模块紧耦合,修改易引发连锁反应。 修改订单表字段需同步更新订单处理、库存管理、财务结算等多个模块。
扩展性有限 无法针对单一功能扩展,需整体升级,导致资源浪费。 电商平台订单量激增时,需整体扩容服务器,而非仅扩展订单处理模块。

(一)优点

开发效率

在项目初期,由于开发人员可以在一个代码库中快速开发功能,不需要考虑复杂的分布式系统构建,所以开发速度较快。对于一些快速原型开发或者需求明确、功能简单的项目来说,能够快速上线。

资源利用

单体架构的应用只需要一个运行环境,相比于分布式架构,在资源消耗方面可能更低。例如,不需要为多个服务分别配置独立的服务器资源,在小型项目中可以充分利用有限的硬件资源。

(二)缺点

可维护性差

随着应用规模的增大,代码库变得庞大复杂。由于各个功能模块之间的紧耦合,一个小的修改可能会在整个应用中产生连锁反应。例如,修改数据库结构可能会影响到多个业务逻辑模块,需要对整个应用进行全面的测试。

扩展性有限

当需要对应用的某个功能进行大规模扩展时,由于单体架构的限制,很难做到独立扩展。例如,在一个单体架构的电商应用中,如果订单处理模块需要应对大量订单的处理,很难单独对该模块进行水平扩展,往往需要对整个应用进行扩展,这可能会带来不必要的资源浪费。

七、单体架构的案例

以一个传统的图书馆管理系统为例。这个系统包含了图书信息管理(包括图书的录入、查询、借阅等)、读者信息管理(读者注册、借阅记录查询等)以及系统管理(用户权限管理等)等功能。整个系统采用单体架构构建,所有功能都在一个可执行程序中。开发人员使用分层式单体架构,将表示层、业务逻辑层和数据访问层分开。表示层提供用户界面,业务逻辑层处理诸如借阅规则、还书规则等业务逻辑,数据访问层与数据库交互存储和获取相关信息。

早期的 eBay 网站​

eBay 在发展初期采用了单体架构。当时,其业务主要集中在提供一个在线拍卖平台,功能相对单一。单体架构使得 eBay 能够快速开发和部署网站,满足用户的基本需求。开发团队可以专注于核心业务功能的实现,如商品发布、竞拍、交易等功能的开发。随着业务的增长,eBay 逐渐面临性能和可扩展性的挑战,才开始逐步向分布式架构转型。​

小型企业的内部管理系统​

许多小型企业的内部管理系统,如财务管理系统、人力资源管理系统等,常采用单体架构。以一个小型制造企业为例,其财务管理系统涵盖了账务处理、报表生成、预算管理等功能。由于企业规模较小,业务流程相对简单,采用单体架构可以满足企业的日常管理需求。开发和维护成本较低,企业内部的 IT 人员能够轻松应对系统的日常运维工作。

八、系统整体框架代码举例

以简单的Java分层式单体架构为例

(一)表示层(以JSP页面为例)

html 复制代码
<%@ page contentType="text/html; charset=UTF - 8" %>
<html>
<head>
    <title>图书馆管理系统 - 图书查询</title>
</head>
<body>
    <h1>图书查询</h1>
    <form action="bookQueryServlet" method="post">
        <label for="bookTitle">图书标题:</label>
        <input type="text" id="bookTitle" name="bookTitle"><br>
        <input type="submit" value="查询">
    </form>
</body>
</html>

(二)业务逻辑层(BookQueryService.java)

java 复制代码
import java.util.List;

public class BookQueryService {
    private BookDao bookDao;

    public BookQueryService(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    public List<Book> queryBooksByTitle(String title) {
        return bookDao.getBooksByTitle(title);
    }
}

(三)数据访问层(BookDao.java)

java 复制代码
import java.util.List;

public interface BookDao {
    List<Book> getBooksByTitle(String title);
}

// 假设的数据库连接类
class DatabaseConnection {
    // 数据库连接相关代码
}

// 实现BookDao接口的类
class BookDaoImpl implements BookDao {
    @Override
    public List<Book> getBooksByTitle(String title) {
        // 数据库查询逻辑,假设使用JDBC
        DatabaseConnection connection = new DatabaseConnection();
        // 执行查询并返回结果
        return null;
    }
}

以 Java Spring Boot 为例)

下面是一个简单的基于 Spring Boot 的单体架构 Web 应用示例,实现了一个用户管理模块的基本功能,包括用户注册和查询。​

项目结构​

java 复制代码
src/​

├── main/​

│ ├── java/​

│ │ └── com/​

│ │ └── example/​

│ │ ├── config/​

│ │ │ └── WebMvcConfig.java​

│ │ ├── controller/​

│ │ │ └── UserController.java​

│ │ ├── model/​

│ │ │ └── User.java​

│ │ ├── repository/​

│ │ │ └── UserRepository.java​

│ │ ├── service/​

│ │ │ └── UserService.java​

│ │ └── Application.java​

│ └── resources/​

│ ├── application.properties​

│ └── static/​

│ └── index.html​

└── test/​

└── java/​

└── com/​

└── example/​

└── ApplicationTests.java​

User 模型类​

java 复制代码
package com.example.model;​

import javax.persistence.Entity;​

import javax.persistence.GeneratedValue;​

import javax.persistence.GenerationType;​

import javax.persistence.Id;​

​

@Entity​

public class User {​

@Id​

@GeneratedValue(strategy = GenerationType.IDENTITY)​

private Long id;​

private String username;​

private String password;​

​

// Getters and Setters​

public Long getId() {​

return id;​

}​

​

public void setId(Long id) {​

this.id = id;​

}​

​

public String getUsername() {​

return username;​

}​

​

public void setUsername(String username) {​

this.username = username;​

}​

​

public String getPassword() {​

return password;​

}​

​

public void setPassword(String password) {​

this.password = password;​

}​

}​

​

User 仓库接口​

java 复制代码
package com.example.repository;​

​

import com.example.model.User;​

import org.springframework.data.jpa.repository.JpaRepository;​

​

public interface UserRepository extends JpaRepository<User, Long> {​

}​

User 服务类​

java 复制代码
package com.example.service;​

import com.example.model.User;​

import com.example.repository.UserRepository;​

import org.springframework.beans.factory.annotation.Autowired;​

import org.springframework.stereotype.Service;​

​

@Service​

public class UserService {​

@Autowired​

private UserRepository userRepository;​

​

public User saveUser(User user) {​

return userRepository.save(user);​

}​

​

public User findUserById(Long id) {​

return userRepository.findById(id).orElse(null);​

}​

}​

User 控制器类​

java 复制代码
package com.example.controller;​

import com.example.model.User;​

import com.example.service.UserService;​

import org.springframework.beans.factory.annotation.Autowired;​

import org.springframework.web.bind.annotation.*;​

​

@RestController​

@RequestMapping("/users")​

public class UserController {​

@Autowired​

private UserService userService;​

​

@PostMapping("/register")​

public User registerUser(@RequestBody User user) {​

return userService.saveUser(user);​

}​

​

@GetMapping("/{id}")​

public User getUserById(@PathVariable Long id) {​

return userService.findUserById(id);​

}​

}​

应用主类​

java 复制代码
package com.example;​​

import org.springframework.boot.SpringApplication;​

import org.springframework.boot.autoconfigure.SpringBootApplication;​

​

@SpringBootApplication​

public class Application {​

public static void main(String[] args) {​

SpringApplication.run(Application.class, args);​

}​

}​

这个简单的示例展示了一个单体架构 Web 应用的基本结构,各个模块(模型、仓库、服务、控制器)协同工作,实现了用户管理的部分功能。

九、未来发展趋势

随着技术的不断发展,单体架构虽然在一些场景下仍然会被使用,但也在逐渐向混合架构发展。例如,在大型企业应用中,可能会将一些核心的、不易拆分的功能保留在单体架构部分,而将一些边缘的、需要独立扩展或频繁更新的功能拆分成微服务或者Serverless函数。同时,容器化技术也为单体架构的部署和管理提供了新的思路,通过将单体应用容器化,可以提高其可移植性和资源利用率。另外,为了提高单体架构的可维护性,代码模块化和自动化测试技术也会不断得到加强。

尽管面临着诸多挑战,但单体架构在未来仍有其存在的价值。在一些特定场景下,如小型项目、对成本和开发速度要求极高的初创业务、功能简单且不需要频繁扩展的内部工具等,单体架构依然是一个不错的选择。同时,随着技术的发展,单体架构也在不断演进。例如,通过采用容器化技术(如 Docker),可以提高单体应用的部署效率和可移植性;利用云原生技术,将单体应用更好地融入云环境,享受云服务带来的弹性和便捷。此外,结合一些现代化的开发理念和工具,如微前端架构,可以在一定程度上缓解单体架构在大型项目中面临的可维护性和扩展性问题。未来,单体架构将与其他新型架构相互补充,共同为软件系统的构建提供多样化的选择。​

相关推荐
Gavynlee1 小时前
plantuml用法总结
设计模式
DKPT1 小时前
Java享元模式实现方式与应用场景分析
java·笔记·学习·设计模式·享元模式
缘来是庄1 小时前
设计模式之迭代器模式
java·设计模式·迭代器模式
程序员JerrySUN2 小时前
RK3588 Android SDK 实战全解析 —— 架构、原理与开发关键点
android·架构
No Silver Bullet2 小时前
软件工程功能点估算基础
软件工程·功能点估算
No Silver Bullet2 小时前
软件工程功能点估算法常用术语介绍
java·开发语言·软件工程
摘星编程4 小时前
深入解析迭代器模式:优雅地遍历聚合对象元素
设计模式·迭代器模式·软件开发·编程技巧·面向对象设计
DKPT9 小时前
Java桥接模式实现方式与测试方法
java·笔记·学习·设计模式·桥接模式
ai小鬼头12 小时前
AIStarter如何助力用户与创作者?Stable Diffusion一键管理教程!
后端·架构·github
掘金-我是哪吒15 小时前
分布式微服务系统架构第156集:JavaPlus技术文档平台日更-Java线程池使用指南
java·分布式·微服务·云原生·架构