很多Java项目在起步时都是一个“大包大揽”的单体——所有代码堆在同一个模块里,业务逻辑、数据访问、工具类混杂在一起。起初开发确实爽:改一个功能不用跨模块跳转,IDE秒开。但当代码量突破几万行、团队扩大到十几人时,噩梦开始了——改一个工具类要排查几十个地方的调用,一次发布牵动上百个文件,代码冲突成了家常便饭。
可维护性不是锦上添花,而是项目长期演进的生存底线。模块化与分层架构,正是守住这条底线的两把利器。
模块化:先拆“包”,再拆“模块”
模块化的核心目标很简单:高内聚、低耦合。每个模块专注于单一的业务领域或技术功能,通过清晰的接口对外交互。在Maven多模块项目中,常见的划分方式是按功能垂直拆分——用户模块、订单模块、商品模块各自独立。
一个典型的多模块Spring Boot项目通常包含四个核心模块:
common模块:存放工具类、统一返回格式、公共异常、常量定义等跨模块共享的代码,被所有其他模块依赖。
api模块:对外暴露的服务接口定义和DTO,用于模块间解耦或RPC调用。
service模块:实现核心业务逻辑,包含Service接口和实现类,负责事务控制和流程编排。
web模块:处理HTTP请求,接收参数、调用Service、返回JSON响应。
依赖关系遵循一个铁律:web → api → common,service → common,common不依赖任何模块。这个依赖方向一旦被打破,循环依赖和架构腐败就会随之而来。
分层:让每一层只做一件事
如果说模块化是“横向切分”业务领域,那么分层就是“纵向切分”技术职责。经典的三层架构将系统划分为表现层、业务逻辑层和数据访问层。
Controller层(表现层):接收请求、参数校验、调用Service、返回响应。它只关心“怎么接”和“怎么回”,不写任何业务逻辑。
Service层(业务逻辑层):承载核心业务规则——流程编排、事务管理、权限判断。它是系统的“大脑”,也是分层架构中最关键的一层。
DAO/Mapper层(数据访问层):负责与数据库交互,封装CRUD操作。
每一层的职责边界必须清晰。Controller不能绕过Service直接调DAO——这看似省事,实则在业务逻辑分散到各处之后,日后的维护成本会以指数级增长。Service层通常先定义接口再写实现类,通过接口隔离具体实现。
当模块化遇上分层:1+1>2
模块化和分层不是二选一,而是互为补充。模块化解决“按什么切”的问题,分层解决“怎么组织”的问题。
在业务模块内部(比如用户模块),仍然遵循Controller-Service-DAO的分层结构。跨模块的调用则通过api模块暴露的接口进行,而不是直接依赖对方的实现类。这样既保证了模块间的松耦合,又保持了模块内部的高内聚。
容易踩的坑
把“模块”当“微服务”拆。模块化是代码组织层面的解耦,不是部署层面的拆分。在项目早期,一个多模块的单体应用比拆成十几个微服务更容易维护和演进。
common模块变成“垃圾桶”。什么东西都往common里扔,最终它会膨胀成所有模块都依赖的“大泥球”。common应该只放真正跨模块共享的、稳定的基础代码。
分层变成“假分层”。有人在Controller里写了大量业务判断,有人在Service里直接拼接SQL——分层一旦被打破,架构就开始腐烂。代码审查时要像守护底线一样守护分层边界。
写在最后
模块化与分层架构的价值,在项目的前三个月是看不出来的——甚至会觉得“多建几个模块好麻烦”。但到了第三年、第五年,当项目从几万行长到几十万行,当团队从两个人扩张到二十个人,当初那些看似“多余”的架构设计,会成为支撑项目继续健康演进的根基。
好的架构不是设计出来的,是演进出来的。先搭好模块化和分层的骨架,然后在每一次迭代中持续守护边界、优化依赖。这比任何花哨的技术选型都更值得投入精力。