Skip to main content

架构与分层

三层架构

三层架构是一种常见的软件架构模式,它将应用程序划分为三个逻辑层,分别是表示层(Presentation Layer)、业务逻辑层(Business Logic Layer)和数据访问层(Data Access Layer)。这种架构的目的是实现应用程序的高内聚、低耦合,从而提高系统的可维护性、可扩展性和复用性。

  • 表示层

    • 负责与用户交互,展示数据并接收用户输入。
    • 通常包括用户界面和用户交互逻辑。
  • 业务逻辑层

    • 负责处理核心业务逻辑,执行具体的业务规则。
    • 将表示层的输入转化为对数据的操作,并将处理结果返回给表示层。
    • 该层的主要职责是保证数据的完整性和一致性。
  • 数据访问层

    • 负责与数据库进行交互,执行 CRUD 操作。
    • 提供对数据存储和访问的抽象接口,屏蔽数据库的具体实现细节。

每一层都各负其责,那么如何将三层联系起来呢?在后端面向对象的系统中,通常通过实体层解决。

实体层不属于三层中的任何一层,但是它是必不可少的一层,每一层(UI—>BLL—>DAL)之间的数据传递(单向)是靠变量或实体作为参数来传递的,这样就构造了三层之间的联系,完成了功能的实现。

优点

  • 分离关注点:每个层次关注不同的职责,代码逻辑更加清晰。
  • 可维护性高:修改某一层的代码不会影响其他层。
  • 可复用性高:业务逻辑和数据访问层可以在不同的表示层中复用。

MVC

MVC(Model-View-Controller)是一种广泛应用的软件架构模式,用于组织应用程序的代码结构。这种模式的目标是将应用程序的逻辑与用户界面分离,从而提高代码的可维护性和可扩展性。

它将应用程序分为三个主要部分:

  • 模型(Model)

    • 代表应用程序的数据或业务逻辑。
    • 负责数据的获取、存储、验证以及数据库操作。
    • 模型不关心视图和控制器如何交互,只专注于数据和业务逻辑。
  • 视图(View)

    • 负责展示数据,也就是用户界面。
    • 视图从模型中获取数据,并将其展示给用户。视图并不直接处理业务逻辑。
  • 控制器(Controller)

    • 负责处理用户的输入,调度Service服务,以及进行API的路由管理。
    • 控制器接收用户请求,调用相应的模型逻辑,处理数据,然后将数据传递给视图进行展示。
    • 控制器起到协调模型和视图的作用,确保它们正确交互。

当一个HTTP请求到达服务器时,它首先会被Controller层接收,Controller层会根据请求调用Model层中的相应模块来处理业务逻辑,并将处理的结果返回给View层进行展示。

优点

  • 分离关注点:将数据处理、用户界面和控制逻辑分开,减少了代码的耦合性。
  • 提高可维护性:由于每个组件都独立,可以更容易地进行修改和扩展。
  • 易于扩展和复用:视图和模型可以独立发展,控制器可以用来协调不同的组件。
  • 支持多视图:相同的模型可以有多个视图(例如,Web界面和移动端界面),而不需要重复业务逻辑。

区别

三层架构和 MVC 在概念上有类似之处,都会将系统划分为不同的部分以实现职责分离,但它们的关注点、应用范围和目的是不同的。它们都强调分层和职责分离,但它们的层次划分和关注点不一样。

对比维度三层架构MVC 架构
关注点关注应用程序的逻辑分层,强调后端逻辑组织关注前端展示与交互的分层,强调视图与控制逻辑分离
应用范围应用于整个系统(包括后端服务与数据库),常见于企业系统架构应用于前端框架或 Web 应用界面设计,常见于用户界面开发
架构目标将系统划分为表示层、业务逻辑层和数据访问层,实现职责分离将界面层与业务逻辑层分离,提升用户体验开发效率
层次结构
  • 表示层
  • 业务逻辑层
  • 数据访问层
  • 模型
  • 视图
  • 控制器
职责重心处理请求 → 执行业务逻辑 → 数据持久化响应用户输入 → 调用逻辑 → 更新界面
典型用途多层次后端系统开发,适用于逻辑复杂、需要明确分层的服务端架构前端 UI 层开发,适用于富交互的网页和前端应用程序

结合使用

三层架构和MVC可以结合使用,它们并不互斥。

  • 表示层中可以采用 MVC 模式,将前端代码组织为视图、模型和控制器,处理用户交互逻辑。
  • 业务逻辑层和数据访问层则采用传统的三层架构模式,处理核心业务逻辑和数据存储。

IoC

由于三层架构和 MVC 的出现,在后端体系中,通常包含下面几个组成部分:

  • Controller(控制器):负责接受客户端的请求和响应返回。
  • Service(服务):处理业务逻辑。
  • Dao(数据访问对象)或 Repository(仓库):负责对数据执行增删改查操作。
  • DataSource(数据源):根据配置信息连接和管理数据库。

这意味着,在后端的开发过程中,需要按照合适的顺序创建这些内容。比如下面的伪代码:

const dataSource = new DataSource(config);
const dao = new Dao(dataSource);
const service = new Service(dao);
const controller = new Controller(service);

在后端系统中,通常有很多的业务模块,也就意味着,有很多不同的 Controller,Service 和 Dao。而且,经常会出现多个不同的 Controller 模块,调用同一个 Service 的情况。那这个时候,如果重新 new 不同的 Service 对象,是不合适的,因为希望确保它们使用的是同一个 Service 实例,也就是维持单例模式。

在大型应用中,手动管理这种依赖关系,会变得非常复杂和繁琐。所以,IoC(Inverse of Control)即控制反转,就提供了这么一种解决方案。

IoC容器在应用初始化时,会查找每个类上声明的依赖,并按照顺序创建相应的实例,并且管理这些实例,当需要使用某个依赖时,IoC容器会帮你提供相应的对象实例

由于一般情况下,都是根据类的依赖关系来进行容器的实例创建的,所以,也会更具这种实现方式称之为依赖注入(Dependency Injection, DI),通过依赖注入,外部容器将所需的依赖对象注入到目标对象中,而不是由目标对象自己创建依赖对象

IoC 提供了一种结构上的思想:将控制权从程序的对象转移到外部容器或框架中。

DIIoC 的具体实现之一,它通过外部注入依赖的方式将对象所依赖的组件或服务交给容器进行管理。

总之,通过 IoC,从主动创建和维护对象,转变为了被动等待依赖注入,这就是IoC控制反转的精髓。

class Container {
constructor() {
// 存储服务实例
this.services = new Map();
}

// 注册服务到容器中
register(name, service) {
this.services.set(name, service);
}

// 获取服务实例
get(name) {
const service = this.services.get(name);
if (!service) {
throw new Error(`Service ${name} not found!`);
}
// 如果服务是一个工厂函数,执行它来创建实例
if (typeof service === 'function') {
const instance = service(this); // 创建实例
this.services.set(name, instance); // 缓存实例
return instance;
}
return service;
}
}

// PaymentService 负责处理支付
class PaymentService {
processPayment(amount) {
console.log(`处理付款金额: $${amount}`);
// 假设支付成功
return true;
}
}

class ShippingService {
shipOrder(orderDetails) {
console.log(`订单发货至: ${orderDetails.address}`);
}
}

// OrderService 依赖 PaymentService和ShippingService,创建订单时需要调用支付和发货服务
class OrderService {
constructor(paymentService, shippingService) {
this.paymentService = paymentService;
this.shippingService = shippingService;
}

createOrder(amount, address) {
console.log(`创建订单金额:$${amount}`);
const paymentSuccess = this.paymentService.processPayment(amount);
if (paymentSuccess) {
console.log(`订单创建成功!`);
this.shippingService.shipOrder({ address });
} else {
console.log(`付款失败。订单创建已中止!`);
}
}
}

// 创建一个 IoC 容器实例
const container = new Container();

container.register('paymentService', new PaymentService()); // 直接注册 PaymentService
container.register('shippingService', new ShippingService()); // 直接注册 ShippingService

container.register('orderService', (container) => {
return new OrderService(
container.get('paymentService'),
container.get('shippingService')
); // 注入 PaymentService 和 ShippingService
});

// 从容器中获取 OrderService,并创建一个订单
const orderService = container.get('orderService');
orderService.createOrder(200, '成都市天府三街');