打造高效稳定的单体项目工程结构

简介: 本文主要说明下单体项目的工程结构如何设计,目前业界存在两种主流的应用工程结构:一种是阿里推出的《 Java 开发手册》中推荐的,另外一种是基于 DDD (领域驱动设计)推荐的,ddd有借鉴别的老师的。

本文主要说明下单体项目的工程结构如何设计,目前业界存在两种主流的应用工程结构:一种是阿里推出的《 Java 开发手册》中推荐的,另外一种是基于 DDD (领域驱动设计)推荐的。下面我们来看下两种工程结构是怎样的。

一、 基于阿里《 Java 开发手册》的分层结构

阿里巴巴的官方文档的分层主要是分为如下这些层:

  • 开放API 层:可直接封装Service 接口暴露成RPC 接口;通过Web 封装成http 接口;网关控制层等。
  • 终端显示层:各个端的模板渲染并执行显示的层。当前主要是velocity 渲染,JS 渲染,JSP 渲染,移动端展示等。
  • Web 层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。
  • Service 层:相对具体的业务逻辑服务层。
  • Manager 层:通用业务处理层,它有如下特征:

      1)对第三方平台封装的层,预处理返回结果及转化异常信息,适配上层接口。

      2)对Service 层通用能力的下沉,如缓存方案、中间件通用处理。

      3)与DAO 层交互,对多个DAO 的组合复用。

  • DAO 层:数据访问层,与底层MySQL、Oracle、Hbase、OB 等进行数据交互。
  • 第三方服务:包括其它部门RPC 服务接口,基础平台,其它公司的HTTP 接口,如淘宝开放平台、支付宝付款服务、高德地图服务等。
  • 外部数据接口:外部(应用)数据存储服务提供的接口,多见于数据迁移场景中。

调用关系如下:

涉及到的领域模型如下:

DO(Data Object):此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。

DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。

BO(Business Object):业务对象,可以由 Service 层输出的封装业务逻辑的对象。

Query:数据查询对象,各层接收上层的查询请求。注意超过2 个参数的查询封装,禁止使用Map 类来传输。 VO(View Object):显示层对象,通常是Web 向模板渲染引擎层传输的对象

基于这样的规范我创建了一个 demo 工程的目录,下面我来解释下这个结构

$├─src
│  ├─main
│  │  ├─java
│  │  │  └─com
│  │  │      └─demo
│  │  │          ├─client           --第三方服务:调用第三方服务
│  │  │          │  ├─model         --模型对象
│  │  │          │  └─service       --接口
│  │  │          ├─controler        --终端显示层
│  │  │          │  ├─request       --请求对象
│  │  │          │  ├─response      --响应包装对象
│  │  │          │  └─vo            --不同接口显示层对象
│  │  │          ├─dao              --DAO 层
│  │  │          │  ├─config        --数据库的配置文件
│  │  │          │  ├─dbclient      --与底层MySQL、Oracle、Hbase等进行数据交互的文件
│  │  │          │  ├─query         --数据查询对象
│  │  │          │  ├─do            --此对象与数据库表结构一一对应
│  │  │          │  ├─serializer    --序列化相关  
│  │  │          │  ├─sharding      --分库分表类目
│  │  │          │  └─utils         --一些公共类
│  │  │          ├─facede           --开放API 层:对外封装的Service接口暴露成RPC接口
│  │  │          │  ├─constant      --常量
│  │  │          │  ├─model         
│  │  │          │  ├─request       --请求对象
│  │  │          │  └─service       --接口层
│  │  │          ├─job              --开放API 层:处理job工作           
│  │  │          │  └─model         --数据传输对象
│  │  │          ├─manager          --Manager 层
│  │  │          │  └─dto           
│  │  │          ├─mq               --开放API 层:处理mq工作
│  │  │          │  ├─config
│  │  │          │  ├─consumer      --处理消费者工作
│  │  │          │  ├─model
│  │  │          │  └─publish       --处理生产者工作
│  │  │          ├─service          --Service 层
│  │  │             ├─schedule      --处理定时任务
│  │  │          │  ├─bo
│  │  │          │  └─dto
│  │  │          └─web              --Web 层
│  │  │              ├─filter       --过滤层
│  │  │              └─Interceptor  --拦截器层
│  │  └─resources                   --资源文件
│  │      └─META-INF
│  └─test                           --针对每个层次文件的测试文件
│      └─java
│          └─com
│              └─demo
│                  ├─client
│                  │  ├─model
│                  │  └─service
│                  ├─controler
│                  │  ├─request
│                  │  ├─response
│                  │  └─vo
│                  ├─dao
│                  │  ├─config
│                  │  ├─dbclient
│                  │  ├─do
│                  │  ├─query
│                  │  ├─sharding
│                  │  ├─serializer
│                  │  ├─sharding
│                  │  └─utils
│                  ├─facede
│                  │  ├─constant
│                  │  ├─model
│                  │  ├─request
│                  │  └─service
│                  ├─job
│                  │  └─model
│                  ├─manager
│                  │  └─dto
│                  ├─mq
│                  │  ├─config
│                  │  ├─consumer
│                  │  ├─model
│                  │  └─publish
│                  ├─service
│                  │  ├─schedule
│                  │  ├─bo
│                  │  └─dto
│                  └─web
│                      ├─filter
│                      └─Interceptor
└─$

可能上面的信息比较多看者比较不清晰,下面我们用列表来展示下:

层次

包名

对象名规范

备注

测试层

test

对象名规范同各层级

开放API 层

job、mq、

model

model为模型对象

终端显示层

controler

request、response、vo

Web 层

web

Service层

service

bo、dto

Manager层

manager

dto

DAO层

dao

query、do

第三方服务

client

model

二、 基于DDD(领域驱动设计)的分层结构

DDD(领域驱动设计,Domain-Driven Design)的分层结构主要是为了更好地组织和分离关注点,确保业务逻辑的清晰性和可维护性。DDD 的分层结构通常包括以下几个层次:

  1. 用户接口层(User Interface Layer):
  • 这一层主要负责与前端应用或其他外部服务的交互。
  • 它会接收和响应外部请求,并将业务逻辑的结果展示给用户。
  • 这一层不应该包含任何业务逻辑,主要是数据的展示和接收。
  1. 应用服务层(Application Service Layer):
  • 这一层是DDD中的业务编排层,它协调领域层中的各个组件来执行特定的业务用例。
  • 应用服务层通常包含服务组合、编排、安全认证、权限校验、事务控制等功能。
  • 应用服务层可能会调用多个领域服务来完成一个复杂的业务操作。
  1. 领域层(Domain Layer):
  • 这是DDD中的核心层,包含了业务领域的所有核心逻辑和概念。
  • 领域层主要由实体(Entity)、值对象(Value Object)、领域服务(Domain Service)和领域事件(Domain Event)等组成。
  • 实体和值对象表达了业务概念,而领域服务则实现了复杂的业务规则。
  1. 基础设施层(Infrastructure Layer):
  • 这一层提供了技术实现和支撑服务,如数据库访问、消息传递、缓存等。
  • 基础设施层为上层提供了技术细节的实现,隐藏了底层技术的复杂性。
  • 基础设施层通常实现了领域层中定义的接口和协议,使得上层可以专注于业务逻辑。

DDD 的分层结构遵循了依赖倒置原则(Dependency Inversion Principle),即高层模块不应该依赖于低层模块,它们都应该依赖于抽象。此外,DDD 的分层结构还强调了领域层的独立性和核心地位,确保业务逻辑的稳定性和可重用性。四个层次调用关系如下:

根据这样的架构我们来理解下和工程结构的映射关系大致如下:

基于 DDD ,我们也可以根据这样的架构来设计一个工程结构,如下:

│    │ 
│    ├─apis   API接口层 
│    │    └─controller       控制器,对外提供(Restful)接口
│    │ 
│    ├─application  应用层
│    │    ├─model            数据传输对象模型及其装配器(含校验)
│    │    │    ├─assembler   装配器,,实现模型转换eg. apiModel<=> domainModel
│    │    │    └─dto         模型定义(含校验规则)      
│    │    ├─service          应用服务,非核心服务,跨领域的协作、复杂分页查询等
│    │    ├─task             任务定义,协调领域模型
│    │    ├─listener         事件监听定义
│    │    └─***              others
│    │ 
│    ├─domain   领域层
│    │    ├─common           模块0-公共代码抽取,限于领域层有效  
│    │    ├─module-xxx       模块1-xxx,领域划分的模块,可理解为子域划分     
│    │    ├─module-user      模块2-用户子域(领域划分的模块,可理解为子域划分)
│    │    │    ├─action      行为定义
│    │    │    │    ├─UserDomainService.java        领域服务,用户领域服务
│    │    │    │    ├─UserPermissionChecker.java    其他行为,用户权限检查器
│    │    │    │    ├─WhenUserCreatedEventPublisher.java     领域事件,当用户创建完成时的事件 
│    │    │    ├─model       领域聚合内模型 
│    │    │    │    ├─UserEntity.java                领域实体,有唯一标识的充血模型,如本身的CRUD操作在此处
│    │    │    │    ├─UserDictVObj.java              领域值对象,用户字典kv定义       
│    │    │    |    ├─UserDPO.java                   领域负载对象    
│    │    │    ├─repostiory  领域仓储接口
│    │    │    │    ├─UserRepository.java
│    │    │    ├─reference   领域适配接口
│    │    │    │    ├─UserEmailSenderFacade.java
│    │    │    └─factory     领域工厂  
│    │ 
│    ├─infrastructure  基础设施层
│    │    ├─persistence      持久化机制
│    │    │    ├─converter   持久化模型转换器
│    │    │    ├─po          持久化对象定义 
│    │    │    └─repository.impl  仓储类,持久化接口&实现,可与ORM映射框架结合
│    │    ├─general          通用技术支持,向其他层输出通用服务
│    │    │    ├─config      配置类
│    │    │    ├─toolkit     工具类  
│    │    │    ├─extension   扩展定义  
│    │    │    └─common      基础公共模块等 
│    │    ├─reference        引用层,包装外部接口用,防止穿插到Domain层腐化领域模型等
│    │    │    ├─dto         传输模型定义
│    │    │    ├─converter   传输模型转换器       
│    │    │    └─facade.impl 适配器具体实现,此处的RPC、Http等调用
│    │ 
│    └─resources  
│        ├─statics  静态资源
│        ├─template 系统页面 
│        └─application.yml   全局配置文件

以上就是我对于两种工程结构的阐述,对于两种项目结构,大家要根据自己的项目情况和公司情况进行微调。原因是每种理论都有自己的产生背景、适用条件和使用方法,不能硬搬硬套。而且理论也是有很多变种的,例如 DDD 也有很多种架构的,例如:洋葱架构、六边形架构等,根据每项架构我们的工程结构肯定还会有一些变种,限于知识的理解不深,本次就不再描述了。DDD 我了解的专家是张逸老师,他的著作《解构式领域驱动设计》对于 DDD 的解释是比较全面的,我们做技术的也是基于在一个公司里面做业务,所以工作的上下文中又有了公司和业务者两块,针对这两块,现在的认识是要通过学习 TOGAF 这样的企业架构框架和周金根老师的《业务架构解构与实践》这样的著作来深化自己对做好技术工作的理解。

三、两种结构的适用背景

1、阿里巴巴分层架构

现在对这个结构的认识是比较适用于:

1、在团队还比较小,职责还不是那么清晰的时候,

2、发展变化比较大的业务,因为此时还没有明确的领域概念,团队设置上也没有专人来保证领域的设计;

3、重心在与快速的支撑业务。

2、DDD分层架构

DDD 模式适用于以下几种场景:

1、支持处理复杂业务逻辑场景:

当应用程序需要处理复杂的业务逻辑时,DDD 可以将业务逻辑封装在领域模型中,从而更好地反映业务需求和业务流程,降低了系统架构的复杂度;

2、多团队协作:

当多个团队共同开发一个大型系统时,DDD 分层架构可以帮助团队之间更好地协作,每个团队负责不同的领域模型和业务逻辑,减少冲突和重复开发;

3、大型业务需要长期演化并快速迭代和交付的场景:

每个子域都可以独立开发、部署和扩展,这样可以使得团队可以快速迭代和交付应用程序。

如果将普通的 CRUD 业务系统也按照这套模式实现,反而会增加系统的复杂度。在现在的认识中,一般中大型的技术团队要能支撑起 DDD 这样的分层架构,在组织层面也至少达到了一定的复杂程度才可见效,例如至少有企业架构师、领域架构师(应用架构师)、业务架构师(应用架构师)、技术架构师、基础结构架构师,基础架构架构师有的有会分为、安全架构师、数据架构师其中的几个,但是在团队比较小变化大的业务中,很难支撑这样的团队配置,导致要在做好业务架构设计和时间、人力成本、机会成本、人员职责等产生了矛盾,最终妥协的结构应该就是采取阿里巴巴分层来快速低成本的支持业务跑起来。

相关实践学习
消息队列RocketMQ版:基础消息收发功能体验
本实验场景介绍消息队列RocketMQ版的基础消息收发功能,涵盖实例创建、Topic、Group资源创建以及消息收发体验等基础功能模块。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
目录
相关文章
|
3月前
|
缓存 测试技术 应用服务中间件
聊聊传统项目与互联网项目
聊聊传统项目与互联网项目
|
4月前
|
Java Docker 微服务
微服务架构已成为Java Web开发的新趋势,它通过将应用分解为独立、可部署的服务单元,提升了系统的灵活性与可维护性。
微服务架构已成为Java Web开发的新趋势,它通过将应用分解为独立、可部署的服务单元,提升了系统的灵活性与可维护性。每个服务负责特定功能,通过轻量通信机制协作。利用Spring Boot与Spring Cloud等框架可简化开发流程,支持模块化设计、独立部署、技术多样性和容错性,适应快速迭代的需求。
82 1
|
4月前
|
Serverless 微服务
软件设计与架构复杂度问题之ady Booch描述软件的复杂性如何解决
软件设计与架构复杂度问题之ady Booch描述软件的复杂性如何解决
|
4月前
|
SQL 分布式计算 大数据
Android项目架构设计问题之平衡技术选型与业务需求之间的关系如何解决
Android项目架构设计问题之平衡技术选型与业务需求之间的关系如何解决
65 0
|
5月前
|
存储 设计模式 前端开发
软件架构设计的原则与模式:构建高质量系统的基石
【7月更文挑战第26天】软件架构设计是构建高质量软件系统的关键。遵循高内聚、低耦合、单一职责等设计原则,并灵活运用分层架构、微服务架构、客户端-服务器架构等设计模式,可以帮助我们设计出更加灵活、可扩展、可维护的软件系统。作为开发者,我们应该不断学习和实践这些原则与模式,以提升自己的架构设计能力,为团队和用户提供更加优秀的软件产品。
|
6月前
|
消息中间件 存储 监控
通过将大型应用拆分成一系列小型、独立的服务,微服务架构为后端开发带来了更高的灵活性、可扩展性和可维护性
【6月更文挑战第10天】本文探讨了构建高效微服务架构的后端开发最佳实践。微服务的核心原则是服务独立、去中心化、自治和轻量级通信,优势在于可扩展性、独立性、技术灵活性和团队协作。实践中,应注意服务的拆分粒度,选择合适的通信协议(如RESTful、RPC、消息队列),处理数据一致性与分布式事务,实施服务治理和监控,以及确保安全性与权限控制。未来,微服务将结合服务网格、容器化和云原生技术,持续发展和优化。
124 0
|
7月前
|
算法 测试技术 数据处理
【C++ 设计思路】优化C++项目:高效解耦库接口的实战指南
【C++ 设计思路】优化C++项目:高效解耦库接口的实战指南
198 5
|
7月前
|
消息中间件 开发者 微服务
构建高效代码:模块化设计原则的实践与思考
在软件开发的世界中,编写可维护、可扩展且高效的代码是每个开发者追求的目标。本文将探讨如何通过应用模块化设计原则来提升代码质量,分享一些实践中的经验教训以及对未来技术趋势的思考。
|
7月前
|
前端开发
第8期 volta保证团队开发环境的完全统一
第8期 volta保证团队开发环境的完全统一
54 0
|
监控 前端开发 安全
大型 SPA 项目架构设计与重构
本文主要为分享我司 控制台 最近两年的架构演进,遇到的问题和解决方案等。控制台项目包含近百个不同产品,跨部门、跨地域协作开发,是一个比较典型的大型 SPA 前端项目。
大型 SPA 项目架构设计与重构
下一篇
DataWorks