type
status
date
slug
summary
tags
category
icon
password
catalog
sort
“写代码就像盖城市,短期靠堆人、长期靠规划。” —— 本文题记

前言:为什么要写这篇文章?

过去 12 个月,我在 2个不同行业的团队里做架构治理,发现 90% 的项目在演进过程中都会掉进同一类陷阱
  1. 代码像“一锅粥”:业务、数据、协议逻辑搅在一起,改一行毁十行;
  1. 命名随心所欲:a1temp 满天飞,新人三天就想跑路;
  1. 领域模型“四不像”:VO、DTO、DO、PO 傻傻分不清,项目一膨胀就崩;
  1. 微服务拆分拍脑袋:今天拆、明天合,上线后性能雪崩。
而这三篇文章(CSDN 工程规范架构篇分层实战)恰好把**“从规范到架构”**的完整链路讲透了。
为了让知识真正可落地,我花了 14 天把三篇内容吃干抹尽,结合阿里、美团、京东、字节 20+ 公开案例,整理出这份终极笔记。
读完你将获得:
  • 一张可打印Java 工程规范 Checklist
  • 一套从贫血到充血的领域建模路线图;
  • 一份分层架构反模式地图(避开 80% 团队踩过的坑);
  • 一个可套用的微服务拆分公式

1. 核心概念:系统、模块、组件、框架与架构

“概念是思考的锚点,锚点不稳,船就飘。”
—— 《架构整洁之道》
在讨论规范与架构前,必须先明确基础概念的边界。很多团队的混乱,从“把模块当组件”“把框架当架构”开始。
软件架构作为系统的顶层结构,其核心价值在于通过系统性的思考与决策,在资源约束下构建出既满足当前业务需求,又能适应未来发展的系统骨架。理解架构的本质,是进行规范设计的前提。
软件架构是指系统的顶层结构,包含四个关键要素:
  • 系统性决策:涵盖技术选型、解决方案设计等战略性选择
  • 明确的系统骨架:定义系统由哪些子系统、模块、组件构成
  • 协作关系:确定各组成部分之间的交互方式与接口规范
  • 约束与原则:建立指导系统开发与演化的规则体系
正如《架构即未来》中所述:"架构的本质是对系统进行有序化重构,以符合业务发展并支持快速扩展"。一个优秀的架构设计应当是业务驱动的,而非技术驱动的炫技产物。

1.1 五大核心概念对比

概念
一句话定义
举例
常见误区
系统
完成特定目标的关联个体集合
电商交易系统、支付系统
把“子系统”等同于“模块”
模块
逻辑上的功能单元
订单模块、用户模块
与“包(package)”混淆(模块是逻辑划分,包是物理组织)
组件
可独立部署的物理单元
MySQL、Redis、订单服务
把“jar包”当组件(组件需具备独立部署能力)
框架
半成品可复用代码
Spring、MyBatis
把“架构”等同于“框架”(框架是工具,架构是设计)
架构
顶层结构+决策约束
微服务架构、六边形架构
认为“画几张框图就是架构”(架构需包含约束)
📌 记忆口诀:系统是大楼,模块是楼层,组件是砖头,框架是脚手架,架构是图纸+施工规范。

1.2 概念关系:从抽象到具体

用“盖楼”类比:
  • 系统是整栋大楼(如“商业综合体”);
  • 模块是楼层功能划分(如“餐饮层”“零售层”);
  • 组件是可独立更换的物理单元(如“电梯”“空调系统”);
  • 框架是脚手架(如“钢筋骨架”,规定了搭建方式);
  • 架构是图纸+施工规范(不仅规定大楼形状,还明确“承重墙不能拆”“管道必须走吊顶”等约束)。
理解这种关系的关键:模块是逻辑划分,组件是物理实现;框架是代码复用,架构是决策集合

1.3 架构的分类体系

从不同维度可将架构划分为以下类型,各类型既相互独立又协同工作:
架构类型
核心关注点
典型产出物
业务架构
业务模块划分与流程设计
业务流程图、领域模型
应用架构
系统逻辑分层与模块边界
应用架构图、接口规范
数据架构
数据存储与流转设计
ER图、数据字典
代码架构
代码组织与开发规范
代码目录结构、编码规范
技术架构
非功能性需求实现
部署拓扑、中间件选型
部署架构
物理资源分配
服务器部署图、网络拓扑
业务架构作为战略层,决定了应用架构的设计方向;应用架构作为战术层,承接业务需求并指导技术选型;而代码架构则是架构落地的最终载体,直接影响开发效率与系统质量。

1.4 架构设计的基本原则

优秀的架构设计应当遵循以下原则:
  • 合适性优先:不存在普适的"最优架构",只有适合具体业务场景的架构
  • 演进式设计:架构应随业务发展逐步优化,而非一次性完美设计
  • 关注点分离:通过分层与模块化降低系统复杂度
  • 高内聚低耦合:模块内部功能紧密相关,模块之间依赖最小化
  • 前瞻性设计:预留扩展点以适应未来1-2年的业务发展
阿里巴巴《Java开发手册》中强调:"脱离业务的架构设计是耍流氓"。架构设计的最终目标是解决业务问题,任何技术选型都应服务于此。

2. 六大设计原则:架构设计的“宪法”

设计原则是架构规范的底层逻辑,所有分层、命名、模型设计都需遵循这些原则。违反原则的代码,短期可能“能跑”,但长期必然陷入混乱。

2.1 单一职责原则(SRP)

定义

一个类/模块只负责一项职责,即“一个类只有一个引起它变化的原因”。

核心思想

  • 职责=引起变化的原因:如果一个类需要因A需求改一次,因B需求改一次,说明它承担了两个职责。
  • 目的:降低类的复杂度(只做一件事)、提高可读性(功能聚焦)、减少变更影响(改A不影响B)。

反例(违反SRP)

正例(遵循SRP)

框架中的应用

  • MyBatis的SqlSession:只负责SQL执行,不处理连接池(连接池由DataSource负责);
  • Spring的@Controller:只处理HTTP协议适配,业务逻辑交给@Service

2.2 开闭原则(OCP)

定义

对扩展开放,对修改关闭。即“新增功能时,通过扩展现有代码实现,而非修改原有代码”。

核心思想

  • 关键:抽象稳定,实现可变。通过接口/抽象类定义稳定契约,具体实现类可扩展。
  • 目的:避免修改原有代码引入风险(尤其核心逻辑),提高系统可扩展性。

反例(违反OCP)

正例(遵循OCP)

框架中的应用

  • Spring的BeanPostProcessor:通过实现接口扩展Bean初始化逻辑,无需修改Spring核心代码;
  • 日志框架SLF4J:通过抽象接口定义日志规范,具体实现(Logback/Log4j)可自由替换。

2.3 里氏替换原则(LSP)

定义

子类必须能替换其父类(即“任何使用父类的地方,都能透明地使用子类对象”)。

核心思想

  • 子类不能破坏父类的契约(如前置条件不能更强、后置条件不能更弱);
  • 目的:保证继承关系的合理性,避免因子类“越界”导致父类逻辑失效。

反例(违反LSP)

正例(遵循LSP)

框架中的应用

  • Java集合List接口:ArrayListLinkedList都严格遵循List契约,可互相替换;
  • Spring的Resource接口:FileResource/ClassPathResource都实现了资源读取契约,替换后不影响调用方。

2.4 依赖倒置原则(DIP)

定义

依赖抽象,而非具体实现。即“高层模块不依赖低层模块,两者都依赖抽象;抽象不依赖细节,细节依赖抽象”。

核心思想

  • 抽象(接口/抽象类)是稳定的,具体实现是可变的;
  • 目的:降低模块间耦合(高层不绑定低层实现),提高系统灵活性。

反例(违反DIP)

正例(遵循DIP)

框架中的应用

  • Spring的依赖注入(DI):通过接口注入实现,高层Bean依赖接口而非具体类;
  • Java的JDBC:Connection/Statement是抽象接口,具体实现由数据库驱动提供,应用程序依赖接口即可。

2.5 接口隔离原则(ISP)

定义

客户端不应依赖其不需要的接口(即“接口应最小化,避免臃肿”)。

核心思想

  • 一个接口只包含客户端需要的方法,不包含无关方法;
  • 目的:避免客户端被迫依赖无用方法,减少变更影响(修改接口中无用方法时,客户端无需感知)。

反例(违反ISP)

正例(遵循ISP)

框架中的应用

  • Java的Collection体系:List/Set/Queue拆分不同接口,避免客户端依赖无关方法;
  • Spring的InitializingBean/DisposableBean:拆分初始化和销毁接口,Bean可按需实现,无需依赖无用方法。

2.6 迪米特法则(LOD)

定义

一个对象应尽可能少地了解其他对象(即“只与直接朋友通信”)。

核心思想

  • 直接朋友:成员变量、方法参数、返回值中的对象;
  • 非直接朋友:方法内部创建的临时对象、通过朋友的朋友获取的对象;
  • 目的:减少对象间耦合(知道得越少,受影响越小)。

反例(违反LOD)

正例(遵循LOD)

框架中的应用

  • Spring的ApplicationContext:提供getBean()方法,隐藏Bean的创建细节,调用方无需关心Bean的依赖关系;
  • MyBatis的SqlSession:提供selectOne()/insert()等方法,封装JDBC底层操作,调用方无需了解Connection/Statement。

2.7 六大原则关系总结

原则
核心解决问题
关键词
单一职责
类/模块的功能聚焦
职责唯一
开闭原则
扩展与修改的平衡
抽象稳定,实现可变
里氏替换
继承关系的合理性
子类不破坏父类契约
依赖倒置
模块间的耦合方向
依赖抽象,不依赖具体
接口隔离
接口的最小化设计
接口只包含必要方法
迪米特法则
对象间的通信边界
只与直接朋友通信
记忆口诀:单开理依接迪(单一职责、开闭、里氏替换、依赖倒置、接口隔离、迪米特)。

3 Java工程的分层架构设计

合理的分层是Java工程规范的基础,它通过明确各层职责,实现"高内聚、低耦合"的设计目标,使系统更易于理解、开发和维护。

3.1 经典分层架构

主流的Java工程采用多层次架构设计,各层职责清晰且遵循严格的依赖关系。典型的分层结构如下:
  1. 开放接口层(API/Controller)
      • 职责:封装服务接口,处理HTTP/RPC请求,进行参数校验与结果封装
      • 特征:轻业务逻辑,仅负责请求转发与基本校验
      • 技术实现:Spring MVC的@Controller/@RestController
  1. Web层
      • 职责:请求路由、参数解析、简单权限控制
      • 特征:不处理复杂业务,仅做请求预处理
      • 典型组件:拦截器、过滤器、请求适配器
  1. 业务逻辑层(Service)
      • 职责:实现核心业务逻辑,处理事务管理
      • 特征:包含具体业务规则,可调用多个Manager或DAO
      • 技术实现:@Service注解,@Transactional事务控制
  1. 通用业务处理层(Manager)
      • 职责:封装通用业务能力,处理跨领域逻辑
      • 特征:高复用性,可封装第三方接口、缓存逻辑等
      • 典型应用:MQ消息处理、缓存管理、第三方API适配
  1. 数据访问层(DAO)
      • 职责:与数据库交互,执行CRUD操作
      • 特征:仅处理数据存取,不包含业务逻辑
      • 技术实现:MyBatis的Mapper接口,JPA的Repository
  1. 领域模型层(Model)
      • 职责:定义各类数据对象(DO/DTO/VO等)
      • 特征:POJO类,主要包含属性与getter/setter
  1. 外部接口层
      • 职责:与第三方系统交互
      • 特征:封装外部系统接口,处理协议转换与异常映射
各层之间遵循严格的依赖规则:上层可依赖下层,下层不可依赖上层。即API层可依赖Web层和Service层,Service层可依赖Manager层和DAO层,但反之则不允许。

3.2 代码分层:为什么4层比3层更稳?

传统教科书讲“Controller/Service/DAO”三层,但生产环境中,Service层往往因承担过多职责而膨胀。美团、阿里等团队的实践证明,在Service与DAO之间增加“Manager层”能显著提升代码清晰度。
经典三层面临的问题
  • Service 膨胀:一个类 3000 行,改一行得读 30 分钟;
  • 跨层调用:Controller 直接调 DAO,一删字段全崩;
  • 无法单测:Service 依赖 HttpServletRequest,单测要启动 Spring。
四层架构职责划分
层级
定位
禁止做的事
事务边界
Controller
协议适配(HTTP/Dubbo/gRPC)
① 业务逻辑 ② 直接操作 DAO
Service
编排业务流程(组合多个 Manager)
① 直接 SQL ② 远程调用无兜底
有(@Transactional)
Manager
复用性业务(缓存、第三方接口封装)
① 事务注解 ② 依赖 DAO 以外的下层
DAO/Mapper
数据访问(MyBatis/JPA)
① 业务判断 ② 返回 VO/DTO
📌 美团点评在 2023 年公开案例里,把 Manager 叫 InfrastructureService,本质一样:屏蔽技术细节,让 Service 更纯粹。

3.3 四层架构实践

  1. Controller层
      • 负责接收前端请求(HTTP/JSON)或RPC调用,转换为DTO;
      • 进行参数校验(如@Valid注解);
      • 调用Service层,将结果转换为VO返回;
      • 处理协议相关逻辑(如HTTP状态码、异常转换)。
      示例:
  1. Service层
      • 负责核心业务流程编排(如“下单=扣库存+创建订单+通知用户”);
      • 管理事务边界(确保操作原子性);
      • 协调多个Manager或DAO完成业务。
      示例:
  1. Manager层
      • 封装复用性逻辑(如缓存操作、第三方接口调用);
      • 对DAO层进行二次封装(如分页查询、结果转换);
      • 处理跨领域的通用业务(如日志记录、权限校验)。
      示例:
  1. DAO层
      • 负责与数据库交互(MyBatis/JPA);
      • 只做CRUD操作,不包含业务逻辑;
      • 输入输出为DO(与数据库表映射)。
      示例:

4 领域模型思想

领域模型的模式是“构建块”,包括实体、值对象、聚合(根)、领域服务、事件等,用于精准映射业务概念;血缘关系是“连接线”,通过结构关联和行为协作,将构建块组织成有机的领域模型。
  • 设计模式的核心是“职责划分”:实体负责自身状态和行为,值对象描述特征,聚合保证一致性,服务处理跨实体逻辑,事件解耦协作。
  • 血缘关系的核心是“合理依赖”:聚合内强关联、聚合外弱引用,通过事件和服务减少直接耦合,保证模型的灵活性和可维护性。
理解两者的关系,能帮助开发者在复杂业务中设计出既贴合业务、又低耦合的领域模型。

4.1 领域模型的设计模式

领域模型的设计模式是对领域中重复出现的业务场景和结构的抽象,核心目标是精准映射业务逻辑、降低复杂度,并保证模型的稳定性和可扩展性。常见的设计模式包括以下几类:

1. 核心领域对象模式

这类模式是领域模型的基础构成单元,用于抽象业务中的核心实体和概念。
  • 实体(Entity)
    • 定义:具有唯一标识(Identity),且在生命周期中状态可变化的对象。其核心是“是谁”而非“是什么”。
    • 特点:
      • 唯一标识贯穿生命周期(如用户ID、订单号),即使属性全变,标识不变则实体不变;
      • 状态可变(如用户的地址、订单的状态);
      • 通常包含业务行为(如订单的“取消”操作)。
    • 示例:电商中的“用户”“订单”,金融中的“账户”。
  • 值对象(Value Object)
    • 定义:通过属性值来定义自身的对象,无唯一标识,用于描述实体的特征。
    • 特点:
      • 无唯一标识,属性完全相同则视为同一个对象;
      • 不可变(创建后属性不可修改,修改需创建新对象),避免副作用;
      • 通常用于封装一组相关属性(如“地址”包含省、市、街道;“金额”包含数值和币种)。
    • 示例:“地址”“金额”“时间区间”“商品规格(颜色、尺寸)”。
  • 聚合(Aggregate)与聚合根(Aggregate Root)
    • 聚合:一组紧密关联的实体和值对象的集合,作为一个整体保证数据一致性(事务边界)。
    • 聚合根:聚合中唯一可被外部直接访问的实体,负责管理聚合内的其他对象,维护聚合的 invariants(不变规则)。
    • 特点:
      • 聚合内的对象可直接关联(如订单包含订单项);
      • 聚合外的对象只能通过聚合根的标识引用聚合内的对象(避免跨聚合直接关联,降低耦合);
      • 聚合根是事务的最小单元(修改聚合内的对象需通过聚合根,保证原子性)。
    • 示例:电商中“订单聚合”包含订单(聚合根)、订单项(实体)、收货地址(值对象);订单聚合外的“库存”只能通过订单ID关联订单,不能直接关联订单项。

2. 行为封装模式

当业务逻辑无法归属到单个实体或值对象时,需通过专门的模式封装行为。
  • 领域服务(Domain Service)
    • 定义:封装跨多个实体/聚合的业务逻辑,无状态(或仅含临时状态),以动词为核心命名(如“转账”“计算税费”)。
    • 特点:
      • 依赖实体/聚合根完成业务操作(而非自身存储数据);
      • 输出是对实体状态的修改或领域事件的发布;
      • 仅包含领域层的纯业务逻辑(不涉及技术细节,如数据库操作)。
    • 示例:金融领域的“转账服务”(需操作转出账户和转入账户两个聚合根);电商的“订单结算服务”(需关联订单、支付、库存三个聚合)。
  • 领域事件(Domain Event)
    • 定义:记录领域中发生的重要事件,用于解耦跨聚合的业务协作(基于事件的通信)。
    • 特点:
      • 包含事件发生的时间、触发者(如哪个实体)、关键数据(如订单ID、状态);
      • 不可变(事件一旦发生,内容不可修改);
      • 由实体/聚合根的操作触发(如订单“创建成功”“支付完成”),被其他领域组件(如领域服务、外部系统)订阅。
    • 示例:“订单创建事件”(触发库存扣减)、“支付成功事件”(触发订单状态更新)。
  • 领域工厂(Domain Factory)
    • 定义:封装复杂实体/聚合的创建逻辑,尤其是当创建过程涉及多个步骤、依赖多个对象或需保证 invariants 时。
    • 特点:
      • 隐藏创建细节(如聚合根的初始化需关联多个子对象);
      • 确保创建的对象符合领域规则(如“订单必须至少包含一个订单项”)。
    • 示例:“订单工厂”负责创建订单聚合,确保订单创建时订单项非空、收货地址有效。

3. 边界与抽象模式

用于划分领域的边界,隔离不同的业务上下文。
  • 限界上下文(Bounded Context)
    • 定义:领域中一个语义一致的边界,边界内的领域模型(实体、服务等)有统一的术语和规则,边界外可能有不同的模型映射(如“用户”在电商上下文和支付上下文可能有不同属性)。
    • 作用:解决大型系统中“同一术语在不同业务场景下含义不同”的问题,避免模型混乱。
    • 示例:电商系统中的“订单上下文”“库存上下文”“支付上下文”;同一“商品”在订单上下文关注价格,在库存上下文关注库存数量。

4.1 模型特征与血缘关系

领域模型的血缘关系指各设计模式之间的关联、依赖和协作关系,体现了领域模型的结构和行为逻辑。血缘关系可分为结构血缘(静态关联)和行为血缘(动态依赖)。 领域模型的设计涉及三种典型模式,各有其适用场景:
  1. 失血模型
      • 特征:模型仅包含属性和getter/setter,无任何业务逻辑
      • 业务逻辑全部放在Service层实现
      • 优势:简单直观,易于理解
      • 局限:Service层负担过重,不符合面向对象设计
      • 适用场景:简单CRUD应用
  1. 贫血模型
      • 特征:模型包含简单的原子服务(如获取关联ID),核心业务逻辑仍在Service层
      • 优势:一定程度上减轻Service层负担
      • 局限:数据与行为分离,仍不完全符合面向对象思想
      • 适用场景:中小型应用,大多数Spring项目采用此模式
贫血模型(Anemic Domain Model)是指领域对象(如OrderDO)仅包含属性和getter/setter,无业务逻辑,所有逻辑都放在Service层。
  1. 充血模型
      • 特征:模型包含大部分业务逻辑,Service层仅做事务控制与协调
      • 优势:符合面向对象设计,数据与行为封装在一起
      • 局限:实现复杂,可能导致模型依赖DAO层
      • 适用场景:领域驱动设计(DDD),复杂业务场景
充血模型(Rich Domain Model)是指领域对象包含属性和业务逻辑(如订单状态转换、金额计算)数据与行为合一,Service层仅负责事务和流程编排。
Spring创始人Rod Johnson曾承认,Spring框架采用的贫血模型本质上是"事务脚本"模式(面向过程编程)。但这并不意味着贫血模型应该被摒弃——在实际项目中,应根据业务复杂度选择合适的模型模式。

1. 结构血缘(静态关联)

指模型元素在结构上的包含、引用关系,体现在代码或设计图中。
  • 实体与值对象的关系:实体通常“包含”值对象(值对象是实体的属性)。
    • 示例:用户(实体)包含“联系地址”(值对象);订单项(实体)包含“商品规格”(值对象)。
  • 实体之间的关系
    • 聚合内:实体可直接关联(如订单(实体)包含多个订单项(实体),一对多关系);
    • 聚合外:实体通过“聚合根标识”间接引用(如库存实体通过“商品ID”引用商品聚合根,而非直接关联商品实体)。
  • 聚合与聚合根的关系:聚合根是聚合的“核心”,聚合包含聚合根、其他实体和值对象(聚合根→实体→值对象,形成层级包含)。
    • 示例:订单聚合 = 订单(聚合根)→ 订单项(实体)→ 商品规格(值对象);同时订单聚合根包含收货地址(值对象)。
  • 领域服务与实体/聚合的关系:领域服务“依赖”实体/聚合根完成业务逻辑(服务调用实体的方法,或修改实体的状态)。
    • 示例:转账服务依赖“转出账户”和“转入账户”两个聚合根,调用账户的“扣款”和“收款”方法。
  • 领域事件与实体/聚合的关系:领域事件由实体/聚合根的操作“触发”(实体是事件的“源”),事件中包含实体的标识或关键属性。
    • 示例:订单实体执行“支付”操作后,触发“订单支付成功事件”,事件包含订单ID、支付金额、支付时间。

2. 行为血缘(动态协作)

指模型元素在业务流程中的交互关系,体现“谁触发谁、谁依赖谁”的动态逻辑。
  • 触发链:实体操作→领域事件→其他实体/服务响应。
    • 示例:用户提交订单(实体操作)→ 触发“订单创建事件”→ 库存服务订阅事件,执行“扣减库存”(调用库存聚合根的“扣减”方法)。
  • 依赖链:领域服务→实体/聚合根→值对象。
    • 示例:结算服务(领域服务)→ 依赖订单聚合根(获取订单金额)和税率值对象(获取税率)→ 计算税费(返回税费值对象)→ 更新订单聚合根的“税费”属性。
  • 聚合内的协作:聚合根协调内部实体/值对象完成操作,保证不变规则。
    • 示例:订单(聚合根)执行“添加订单项”操作时,需检查订单项的商品是否已存在(通过订单项实体的商品ID),若存在则合并数量(调用订单项的“合并”方法),最终保证“同一商品在订单中不重复”的不变规则。

4.2 领域驱动设计(DDD)核心概念

充血模型是DDD的基础,理解DDD概念有助于更好地设计领域模型:
概念
定义
示例
实体(Entity)
有唯一标识,状态可变的领域对象
Order(订单有唯一ID,状态会变)
值对象(Value Object)
无唯一标识,状态不可变的对象
Money、Address
聚合(Aggregate)
实体和值对象的集合,有聚合根
Order(根)+ OrderItem + Money
领域服务
处理跨实体逻辑,无状态
PaymentService(协调订单和支付)
仓储(Repository)
封装实体持久化,模拟内存集合
OrderRepository
限界上下文
领域模型的边界,上下文内模型语义一致
订单上下文、库存上下文
领域模型是数据在各层流转的载体,混淆模型职责是代码混乱的主要原因。阿里《Java开发手册》明确规定:“禁止跨层使用模型对象”。

5 命名规范:让代码自解释

命名是代码可读性的核心要素,良好的命名能使代码"自解释",减少注释需求。《Clean Code》中强调:"好的代码本身就是注释",而规范的命名是实现这一目标的基础。

5.1 命名的基本原则

  1. 明确性原则:命名应准确反映对象的功能或含义,避免模糊词汇
      • 正例:calculateTotalPrice()isEligibleForDiscount()
      • 反例:doSomething()processData()
  1. 一致性原则:同一概念在系统中应使用同一名称
      • 正例:统一使用"userId"而非混用"userID"、"user_Id"
      • 反例:同时存在getUser()fetchUser()queryUser()表达相同含义
  1. 简洁性原则:在明确性基础上保持简洁,避免不必要的冗余
      • 正例:OrderDAO.findById()而非OrderDAO.findOrderById()
      • 反例:theUserWhoLoggedInAtTheBeginningOfTheSession
  1. 可读性原则:使用完整英文单词,避免不规范缩写
      • 正例:messageconfiguration
      • 反例:msg(除非团队达成共识)、conf
  1. 专业性原则:使用领域术语和技术术语
      • 正例:withdraw()(银行业务)、commit()(数据库操作)
      • 反例:用removeMoney()代替withdraw()

5.2 类名命名规范

类名应采用名词或名词短语,首字母大写,遵循驼峰命名法。不同类型的类有特定的命名规则:
  1. 业务类命名
      • Service接口:XxxService(如OrderService
      • Service实现类:XxxServiceImpl(如OrderServiceImpl
      • DAO接口:XxxDAOXxxMapper(如UserDAO
      • 控制器:XxxController(如OrderController
  1. 领域模型类命名
      • DO类:XxxDOXxxEntity(如UserDO
      • DTO类:XxxDTO(如OrderQueryDTO
      • VO类:XxxVO(如UserProfileVO
  1. 工具类与组件命名
      • 工具类:XxxUtils(如DateUtils
      • 管理器:XxxManager(如CacheManager
      • 工厂类:XxxFactory(如HttpClientFactory
      • 监听器:XxxListener(如OrderStatusListener
  1. 设计模式相关类命名
      • 建造者模式:XxxBuilder(如QueryBuilder
      • 适配器模式:XxxAdapter(如LogAdapter
      • 装饰器模式:XxxDecorator(如ResponseDecorator
      • 代理模式:XxxProxy(如RpcProxy

5.3 方法名命名规范

方法名应采用动词或动词短语,首字母小写,遵循驼峰命名法。不同功能的方法有特定的命名规则:
  1. 查询方法
      • 获取单个对象:getXxx()(如getUserById()
      • 获取多个对象:listXxx()(如listOrdersByUserId()
      • 统计查询:countXxx()(如countValidOrders()
      • 条件查询:findXxx()(如findActiveUsers()
  1. 修改方法
      • 插入数据:saveXxx()insertXxx()(如saveOrder()
      • 更新数据:updateXxx()(如updateUserPassword()
      • 删除数据:deleteXxx()removeXxx()(如deleteExpiredOrders()
  1. 布尔值方法
      • 判断状态:isXxx()(如isValid()isExpired()
      • 判断能力:canXxx()(如canSubmit()canCancel()
      • 判断存在:hasXxx()(如hasPermission()hasStock()
  1. 业务方法
      • 业务操作:doXxx()(如doCheckout()doRefund()
      • 计算操作:calculateXxx()(如calculateTotalPrice()
      • 转换操作:convertXxx()toXxx()(如toOrderDTO()
  1. 工厂方法
      • 实例创建:createXxx()(如createHttpClient()
      • 实例获取:getInstance()(如getInstance(options)
      • 类型转换:fromXxx()(如fromString()fromJson()
方法命名应避免"名不副实",例如一个名为isExpired()的方法不应包含删除过期数据的逻辑:

5.4 变量与常量命名规范

  1. 变量命名
      • 采用名词或名词短语,首字母小写,驼峰命名
      • 布尔变量建议以iscanhas等前缀开头
      • 集合变量建议使用复数形式或添加List/Map后缀
  1. 常量命名
      • 全部大写,单词间用下划线分隔
      • 应包含完整含义,避免过度缩写
      • 按业务模块分组定义
  1. 枚举命名
      • 枚举类名:XxxEnum(如OrderStatusEnum
      • 枚举值:全部大写,下划线分隔

6 项目结构与代码组织

合理的项目结构是工程规范的物理载体,它决定了代码的组织方式和依赖关系,直接影响开发效率和维护成本。一个清晰的项目结构应当使开发者能够快速定位到所需代码。

6.1 顶层目录结构

标准的Java项目采用模块化结构,顶层目录遵循以下规范:
各模块职责明确:
  • application:仅包含启动类和核心配置
  • api:定义对外接口和数据模型
  • core:实现核心业务逻辑
  • infrastructure:提供通用技术组件
  • test:包含所有测试代码

6.2 业务模块内部结构

每个业务模块内部采用分层目录结构,典型的模块结构如下:
这种结构的优势在于:
  • 按功能分层,职责清晰
  • 相关类集中存放,便于查找
  • 符合多数开发者的认知习惯

6.3 通用组件组织

项目中的通用组件应集中管理,避免分散在各业务模块中。典型的通用组件结构如下:
通用组件的设计原则:
  • 无状态:工具类应设计为无状态,仅包含静态方法
  • 单一职责:每个工具类专注于特定功能领域
  • 可测试:工具类应易于单元测试
  • 避免过度设计:不要为了"通用"而实现极少使用的功能

6.4 分层架构反模式地图

反模式
现象描述
危害
解决方案
Service膨胀
一个Service类3000行,包含所有业务逻辑
改一行需通读全文,风险高
按业务拆分Service,引入领域对象承载逻辑
跨层调用
Controller直接调用DAO,跳过Service
事务失控,字段修改影响多层
用ArchUnit检测跨层调用,添加Service代理
模型混用
DO直接返回给前端,DTO在DAO层使用
数据库字段变更影响前端,安全隐患
严格按层使用模型,用MapStruct强制转换
依赖倒置错误
领域层依赖基础设施层(如Domain用Redis)
更换技术框架需修改领域层
领域层定义接口,基础设施层实现接口
事务滥用
所有方法加@Transactional,包括查询
事务开销大,并发性能差
仅对写操作加事务,查询方法不加
工具类侵入
领域层调用DateUtils等工具类
领域层依赖技术细节,难以测试
工具类逻辑封装到值对象(如LocalDateWrapper

7 分层架构的演进

随着业务复杂度增长,分层架构也在不断演进,主要经历了三个阶段:
  1. 单体架构
      • 特征:所有模块打包为单一应用部署
      • 优势:开发简单,部署方便
      • 局限:代码耦合度高,扩展性差
      • 适用场景:小型项目或初创阶段
  1. 分布式架构
      • 特征:按业务模块拆分为独立服务,通过RPC/HTTP通信
      • 优势:降低耦合度,可独立扩展
      • 挑战:分布式事务、服务依赖管理
      • 适用场景:中大型项目,业务模块边界清晰
  1. 微服务架构
      • 特征:服务粒度更细,可独立部署与伸缩
      • 优势:技术栈灵活,容错性强
      • 挑战:服务治理复杂,运维成本高
      • 适用场景:超大型项目,高并发场景
架构演进的核心驱动力是业务复杂度的增长,而非技术潮流。京东、阿里等大型电商平台的架构演进历程表明,合理的架构应当与业务规模相匹配,避免过度设计。

8 微服务拆分:从单体到分布式

微服务拆分是架构演进的必然,但90%的团队因“拍脑袋”拆分导致服务间依赖混乱。

8.1 微服务拆分三原则

原则1:业务边界优先(高内聚)

  • 按“领域边界”拆分,而非技术分层(如“订单服务”包含订单相关的所有功能,而非“订单API服务”“订单DAO服务”);
  • 参考DDD的限界上下文,上下文内业务紧密相关,上下文间松耦合。
美团外卖案例
  • 订单服务:包含下单、支付、取消等订单全生命周期;
  • 库存服务:包含库存扣减、库存查询;
  • 两者通过“订单创建→扣库存”的RPC调用协作。

原则2:数据隔离(低耦合)

  • 每个微服务独占数据库(或独立schema),禁止跨库联表查询;
  • 服务间数据交互通过API,而非直接访问数据库。
反例
订单服务直接查询库存库的stock表,导致库存库改表后订单服务报错。

原则3:接口契约稳定

  • 定义清晰的API契约(如OpenAPI规范),包含参数、返回值、错误码;
  • 接口变更遵循“向后兼容”原则(新增字段而非删除);
  • 通过契约测试(如Spring Cloud Contract)确保调用方兼容。

8.2 微服务拆分公式

拆分公式 = 领域边界 × 团队结构 × 业务复杂度
  1. 领域边界:通过事件风暴(Event Storming)识别聚合和限界上下文;
  1. 团队结构:遵循“康威定律”(系统设计反映团队结构),一个团队负责一个或多个相关服务;
  1. 业务复杂度:复杂度低的领域可合并(如“评价服务”可并入“订单服务”),高的拆分为独立服务。

拆分步骤(以电商为例)

  1. 识别核心领域:订单、商品、用户、支付、库存;
  1. 梳理领域关系:订单依赖商品(查价格)、用户(查地址)、库存(扣减)、支付(回调);
  1. 确定服务粒度
      • 订单服务:核心,独立;
      • 商品服务:核心,独立;
      • 用户服务:核心,独立;
      • 库存服务:依赖商品,可独立;
      • 支付服务:对接第三方,独立;
      • 评价服务:依赖订单,复杂度低,可暂并入订单服务。

8.3 微服务反模式与解决方案

反模式
现象
解决方案
按技术分层拆分
拆分为“订单API服务”“订单DAO服务”
按业务功能拆分,一个服务包含完整的分层逻辑
服务粒度太细
10人团队维护20个服务,每个服务代码少
合并相关服务,避免“分布式单体”
同步调用链太长
下单需调用5个服务,响应时间500ms+
引入事件驱动(如MQ),缩短同步链
共享数据库
多个服务访问同一数据库
按服务拆分数据库,通过API交互
无状态设计失败
服务依赖本地缓存,导致集群数据不一致
改用分布式缓存(如Redis),确保无状态

9 架构设计中的常见误区与最佳实践

即使理解了架构设计的理论,在实际项目中仍可能陷入各种误区。了解这些常见问题及其解决方案,能帮助开发者少走弯路。

9.1 常见架构误区

  1. 过度设计
      • 症状:为尚未存在的需求提前设计复杂架构
      • 危害:增加开发成本,降低开发效率
      • 解决方案:遵循YAGNI原则(You Aren't Gonna Need It),仅设计当前必需的功能
  1. 技术驱动架构
      • 症状:为使用某项新技术而设计架构,而非基于业务需求
      • 危害:架构与业务脱节,增加维护难度
      • 解决方案:始终以业务需求为导向,技术仅作为实现手段
  1. 忽视非功能性需求
      • 症状:只关注功能实现,忽视性能、安全性、可扩展性等
      • 危害:系统上线后出现性能瓶颈或安全漏洞
      • 解决方案:在架构设计阶段明确非功能性需求指标,并进行针对性设计
  1. 架构师独断专行
      • 症状:架构设计由架构师单独决定,不征求团队意见
      • 危害:架构难以落地,团队执行力低
      • 解决方案:采用协作式设计,鼓励团队成员参与架构讨论
  1. 架构一成不变
      • 症状:架构设计完成后不再调整,不随业务发展演进
      • 危害:架构逐渐不适应业务需求,最终被迫重构
      • 解决方案:定期回顾架构设计,根据业务变化进行调整

9.2 最佳实践案例

  1. 分层架构的正确依赖 控制器层不应直接调用DAO层,而应通过Service层间接访问,即使业务逻辑简单:
    1. Service层解耦 当多个Service之间存在依赖关系时,应通过事件驱动或中介者模式解耦,避免直接依赖:
      1. 领域模型转换 不同层之间的模型转换应通过专门的转换器完成,避免在业务代码中散落转换逻辑:
        1. 事务管理 事务应在Service层声明,且明确指定回滚异常类型:

          10 主流框架中的规范实践

          主流开源框架的代码规范和架构设计为我们提供了良好的参考范例。学习这些框架的设计思想,能帮助我们更好地理解和应用工程规范。

          10.1 Spring框架的分层实践

          Spring框架的分层设计清晰,各模块职责明确:
          • Spring MVC:处理Web层请求,对应Controller层
          • Spring Core:提供依赖注入等核心功能
          • Spring Transaction:处理事务管理,对应Service层事务控制
          • Spring Data:简化数据访问,对应DAO层
          Spring的命名规范具有很强的参考价值:
          • 接口命名:XxxServiceXxxRepository
          • 实现类命名:XxxServiceImplXxxTemplate
          • 注解命名:@Service@Repository@Transactional

          10.2 MyBatis-Plus的DAO层规范

          MyBatis-Plus的ServiceImpl定义了一套标准的数据操作方法,遵循一致的命名规范(tip: 我十分反感IService的存在):
          这种规范的命名使开发者能快速理解方法功能,降低学习成本。

          总结

          规范是地基,架构是楼房写代码就像盖楼:
          • 规范是地基,决定了代码的稳定性(地基不稳,楼越高越危险);
          • 架构是设计图,决定了系统的扩展性(设计不合理,加层就塌);
          • 领域模型是承重墙,承载业务逻辑(设计不好,改一点就裂)。
          从规范到架构的演进,本质是“从约束到自由”的过程:初期靠规范约束保证一致性,后期靠架构设计支撑业务增长。阿里、美团等团队的实践证明,这套体系能让新人3天上手,线上故障下降,重构从“大手术”变为“日常小优化”。 优秀的工程规范不是一朝一夕形成的,而是通过团队成员的共同努力和持续改进逐步完善的。只有将规范内化为团队的开发习惯,才能真正发挥其价值,构建出高质量、可维护的系统。
          最后,写给读文章的你
          “代码是写给人看的,只是恰好机器能运行。”
          —— Honesty
          当你写出的代码既符合规范,又体现架构思想时,维护它的人(包括未来的你)会真心感谢现在的你。

          参考资料

          • 《阿里巴巴Java开发手册(嵩山版)》
          • 《Clean Code》- Robert C. Martin
          • 《Clean Architecture》- Robert C. Martin
          • 《大型网站技术架构:核心原理与案例分析》- 李智慧
          • 《架构即未来》- Marty Cagan
          • 《从0到1构建电商平台》- 京东技术白皮书
          • Spring 官方文档
          • Mybatis 官方稳定
          • MyBatis-Plus 官方文档
          • 阿里巴巴开源项目代码规范
          Keycloak 客户端授权服务博客突发崩溃?我的紧急排查与解决全记录
          Loading...
          目录
          0%
          Honesty
          Honesty
          花には咲く日があり、人には少年はいない
          统计
          文章数:
          81
          目录
          0%