1. 三层架构的本质与价值
第一次接触三层架构是在2013年接手一个电商系统重构项目时。当时老系统所有SQL都直接写在JSP里,每次修改业务逻辑都要在几百个页面中"海底捞针"。这种经历让我深刻理解了分层架构的必要性——它不仅是技术规范,更是保障项目长期可维护性的工程实践。
三层架构(Presentation Layer/Business Layer/Data Access Layer)的核心在于关注点分离。就像餐厅后厨的分工:服务员(Controller)负责接待顾客和传菜,厨师(Service)专注烹饪逻辑,配菜员(Mapper)只管食材准备。这种分工让每个角色只需专注自己的职责范围。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 标准三层结构解析
2.1 表现层(Controller)
表现层是与外界交互的"门面",我习惯将其视为系统的API网关。在Spring Boot项目中,一个设计良好的Controller应该:
java复制@RestController
@RequestMapping("/api/products")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping("/{id}")
public ResponseEntity<ProductDTO> getProduct(@PathVariable Long id) {
return ResponseEntity.ok(productService.getProductById(id));
}
@PostMapping
public ResponseEntity<Void> createProduct(@Valid @RequestBody ProductCreateRequest request) {
productService.createProduct(request);
return ResponseEntity.created(URI.create("/api/products")).build();
}
}
关键经验:Controller应该保持"瘦身",只做三件事:
- 参数校验(可用@Valid自动触发)
- 调用Service方法
- 组装响应格式
2.2 业务逻辑层(Service)
这是系统的"大脑",我在实际开发中会特别注意:
java复制@Service
@Transactional
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductMapper productMapper;
@Override
public ProductDTO getProductById(Long id) {
Product product = productMapper.selectById(id);
if(product == null) {
throw new BusinessException(ErrorCode.PRODUCT_NOT_FOUND);
}
return convertToDTO(product);
}
@Override
public void createProduct(ProductCreateRequest request) {
if(productMapper.existsByName(request.getName())) {
throw new BusinessException(ErrorCode.PRODUCT_NAME_DUPLICATE);
}
Product product = convertToEntity(request);
productMapper.insert(product);
eventPublisher.publishEvent(new ProductCreatedEvent(product.getId()));
}
}
常见陷阱:
- 事务边界不清晰(建议在Service方法上加@Transactional)
- 业务逻辑泄露到Controller或Mapper
- 过度依赖数据库事务(复杂业务应考虑领域事件)
2.3 数据访问层(Mapper)
我的团队曾因Mapper设计不当导致严重性能问题,总结出以下规范:
java复制@Mapper
public interface ProductMapper {
@Select("SELECT * FROM product WHERE id = #{id}")
Product selectById(Long id);
@Select("SELECT COUNT(1) FROM product WHERE name = #{name}")
boolean existsByName(String name);
@Insert("INSERT INTO product(name,price,stock) VALUES(#{name},#{price},#{stock})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insert(Product product);
}
性能优化要点:
- 避免在循环中调用Mapper方法(改用批量操作)
- 复杂查询使用XML配置而非注解SQL
- 结果集处理考虑流式查询(大数据量场景)
3. 现代架构的演进趋势
随着云原生技术普及,传统三层架构正在进化:
3.1 服务网格(Service Mesh)的影响
在K8s环境中,Istio等Service Mesh技术接管了部分跨服务通信职责。这时Service层更应聚焦核心业务逻辑,将熔断、降级等能力下沉到基础设施层。
3.2 DDD分层架构实践
在复杂领域系统中,我会采用六边形架构:
code复制- 接口层(REST/RPC)
- 应用层(用例编排)
- 领域层(核心业务逻辑)
- 基础设施层(持久化实现)
这种架构保持了分层理念,但更强调领域模型的核心地位。
4. 典型问题排查指南
4.1 事务失效场景
java复制// 错误示例:自调用导致事务失效
public void createProduct(ProductCreateRequest request) {
validateProduct(request); // 内部调用不会走代理
...
}
private void validateProduct(ProductCreateRequest request) {
// 事务注解无效
@Transactional
checkInventory(request.getSku());
}
解决方案:
- 将方法拆分到不同类
- 使用AopContext.currentProxy()
- 改为声明式事务
4.2 N+1查询问题
java复制// 错误示例:在循环中查询
List<Order> orders = orderMapper.listAll();
orders.forEach(order -> {
User user = userMapper.selectById(order.getUserId()); // 循环查询
order.setUser(user);
});
优化方案:
- 使用JOIN一次性查询
- 使用MyBatis的@Many注解
- 内存中组装关联数据
5. 架构扩展实践
在物联网项目中,我们扩展了标准三层架构:
code复制- DeviceController (处理设备协议)
- MessageService (业务逻辑)
- DeviceStateMapper (持久化)
- ProtocolAdapter (新增协议适配层)
- RuleEngine (新增规则引擎层)
这种扩展保持了核心分层理念,同时适应了物联网领域的特殊需求。关键在于:新增层级必须有明确的职责边界,不能沦为"大杂烩"。
实际开发中,我建议先用标准三层实现MVP,再根据业务复杂度逐步演进架构。过早引入复杂分层反而会增加维护成本。记住:架构是手段而非目的,适合业务现状的才是最好的。
