跳到主要内容

分层架构

分层架构(Layered Architecture)是最常见、最经典的软件架构模式,也是企业级应用的事实标准。它将系统划分为多个水平层次,每层都有明确的职责和边界。

什么是分层架构?

分层架构将软件系统组织为多个水平层,每一层都有特定的职责,上层依赖下层提供的服务,下层对上层隐藏实现细节。这种架构模式强制实现关注点分离,使得系统更易于理解、开发和维护。

┌─────────────────────────────────────────────────────────────┐
│ 表现层(Presentation Layer) │
│ 处理用户界面、API 接口、输入输出 │
├─────────────────────────────────────────────────────────────┤
│ 业务逻辑层(Business Layer) │
│ 核心业务规则、业务流程编排、领域逻辑 │
├─────────────────────────────────────────────────────────────┤
│ 数据访问层(Data Access Layer) │
│ 数据库操作、外部服务调用、数据转换 │
├─────────────────────────────────────────────────────────────┤
│ 数据库层(Database Layer) │
│ 数据持久化存储 │
└─────────────────────────────────────────────────────────────┘

分层架构的核心原则

1. 单向依赖原则

上层可以依赖下层,下层不能依赖上层。这种单向依赖保证了架构的清晰性。

表现层 ──→ 业务逻辑层 ──→ 数据访问层 ──→ 数据库层

2. 隔离原则

每一层只暴露必要的接口给上层,隐藏内部实现细节。上层不应该知道下层的具体实现。

3. 替换原则

只要保持接口契约不变,每一层都可以被独立替换。例如,可以将关系型数据库替换为 NoSQL,而不影响上层代码。

经典四层架构详解

第一层:表现层(Presentation Layer)

职责

  • 处理用户输入和输出
  • 提供 RESTful API 或 Web 界面
  • 输入数据校验和格式化
  • 用户会话管理

示例代码

@RestController
@RequestMapping("/api/users")
public class UserController {

private final UserService userService;

public UserController(UserService userService) {
this.userService = userService;
}

@PostMapping
public ResponseEntity<UserDTO> createUser(@Valid @RequestBody CreateUserRequest request) {
// 表现层只负责接收请求和返回响应
UserDTO user = userService.createUser(request);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}

@GetMapping("/{id}")
public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {
UserDTO user = userService.getUserById(id);
return ResponseEntity.ok(user);
}
}

// DTO - 数据传输对象,用于层间数据传递
public class UserDTO {
private Long id;
private String username;
private String email;
private LocalDateTime createdAt;
// getters and setters
}

// Request - 请求对象,包含输入校验
public class CreateUserRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 50, message = "用户名长度必须在3-50之间")
private String username;

@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;

@NotBlank(message = "密码不能为空")
@Size(min = 6, message = "密码长度至少6位")
private String password;
// getters and setters
}

第二层:业务逻辑层(Business Layer)

职责

  • 实现核心业务规则
  • 业务流程编排
  • 事务管理
  • 权限检查

示例代码

@Service
@Transactional
public class UserServiceImpl implements UserService {

private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final EmailService emailService;

public UserServiceImpl(UserRepository userRepository,
PasswordEncoder passwordEncoder,
EmailService emailService) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.emailService = emailService;
}

@Override
public UserDTO createUser(CreateUserRequest request) {
// 业务规则:检查用户名是否已存在
if (userRepository.existsByUsername(request.getUsername())) {
throw new BusinessException("用户名已存在");
}

// 业务规则:检查邮箱是否已存在
if (userRepository.existsByEmail(request.getEmail())) {
throw new BusinessException("邮箱已被注册");
}

// 创建用户实体
User user = new User();
user.setUsername(request.getUsername());
user.setEmail(request.getEmail());
user.setPasswordHash(passwordEncoder.encode(request.getPassword()));
user.setStatus(UserStatus.ACTIVE);
user.setCreatedAt(LocalDateTime.now());

// 保存用户
User savedUser = userRepository.save(user);

// 发送欢迎邮件(异步操作)
emailService.sendWelcomeEmail(savedUser.getEmail(), savedUser.getUsername());

// 转换为 DTO 返回
return convertToDTO(savedUser);
}

@Override
@Transactional(readOnly = true)
public UserDTO getUserById(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("用户不存在"));
return convertToDTO(user);
}

private UserDTO convertToDTO(User user) {
UserDTO dto = new UserDTO();
dto.setId(user.getId());
dto.setUsername(user.getUsername());
dto.setEmail(user.getEmail());
dto.setCreatedAt(user.getCreatedAt());
return dto;
}
}

第三层:数据访问层(Data Access Layer)

职责

  • 数据库 CRUD 操作
  • 数据映射和转换
  • 查询优化
  • 连接管理

示例代码

// Repository 接口 - 定义数据访问契约
public interface UserRepository {
User save(User user);
Optional<User> findById(Long id);
Optional<User> findByUsername(String username);
Optional<User> findByEmail(String email);
boolean existsByUsername(String username);
boolean existsByEmail(String email);
List<User> findByStatus(UserStatus status);
void deleteById(Long id);
}

// Repository 实现 - 使用 JPA
@Repository
public class UserRepositoryImpl implements UserRepository {

private final JpaUserRepository jpaRepository;

public UserRepositoryImpl(JpaUserRepository jpaRepository) {
this.jpaRepository = jpaRepository;
}

@Override
public User save(User user) {
UserEntity entity = convertToEntity(user);
UserEntity saved = jpaRepository.save(entity);
return convertToDomain(saved);
}

@Override
public Optional<User> findById(Long id) {
return jpaRepository.findById(id)
.map(this::convertToDomain);
}

@Override
public Optional<User> findByUsername(String username) {
return jpaRepository.findByUsername(username)
.map(this::convertToDomain);
}

@Override
public boolean existsByUsername(String username) {
return jpaRepository.existsByUsername(username);
}

// 实体转换方法
private UserEntity convertToEntity(User user) {
UserEntity entity = new UserEntity();
entity.setId(user.getId());
entity.setUsername(user.getUsername());
entity.setEmail(user.getEmail());
entity.setPasswordHash(user.getPasswordHash());
entity.setStatus(user.getStatus().name());
entity.setCreatedAt(user.getCreatedAt());
return entity;
}

private User convertToDomain(UserEntity entity) {
User user = new User();
user.setId(entity.getId());
user.setUsername(entity.getUsername());
user.setEmail(entity.getEmail());
user.setPasswordHash(entity.getPasswordHash());
user.setStatus(UserStatus.valueOf(entity.getStatus()));
user.setCreatedAt(entity.getCreatedAt());
return user;
}
}

// JPA 实体
@Entity
@Table(name = "users")
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(unique = true, nullable = false)
private String username;

@Column(unique = true, nullable = false)
private String email;

@Column(name = "password_hash", nullable = false)
private String passwordHash;

@Column(nullable = false)
private String status;

@Column(name = "created_at")
private LocalDateTime createdAt;

// getters and setters
}

// Spring Data JPA 接口
public interface JpaUserRepository extends JpaRepository<UserEntity, Long> {
Optional<UserEntity> findByUsername(String username);
Optional<UserEntity> findByEmail(String email);
boolean existsByUsername(String username);
boolean existsByEmail(String email);
List<UserEntity> findByStatus(String status);
}

第四层:领域层(Domain Layer)

在某些复杂的业务场景中,会在业务逻辑层和数据访问层之间增加领域层,用于封装核心业务逻辑。

// 领域实体 - 包含业务规则
public class User {
private Long id;
private String username;
private String email;
private String passwordHash;
private UserStatus status;
private LocalDateTime createdAt;

// 领域方法 - 封装业务规则
public void activate() {
if (this.status == UserStatus.ACTIVE) {
throw new IllegalStateException("用户已经是激活状态");
}
this.status = UserStatus.ACTIVE;
}

public void deactivate() {
if (this.status == UserStatus.INACTIVE) {
throw new IllegalStateException("用户已经是禁用状态");
}
this.status = UserStatus.INACTIVE;
}

public boolean canPlaceOrder() {
return this.status == UserStatus.ACTIVE;
}

// getters and setters
}

// 值对象
public class Email {
private final String value;

public Email(String value) {
if (!isValid(value)) {
throw new IllegalArgumentException("无效的邮箱格式");
}
this.value = value;
}

private boolean isValid(String email) {
return email != null && email.matches("^[A-Za-z0-9+_.-]+@(.+)$");
}

public String getValue() {
return value;
}
}

// 枚举
public enum UserStatus {
ACTIVE, // 激活
INACTIVE, // 禁用
PENDING // 待验证
}

分层架构的代码组织

project/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ ├── application/ # 应用层(可选)
│ │ │ │ ├── dto/
│ │ │ │ ├── service/
│ │ │ │ └── event/
│ │ │ ├── domain/ # 领域层
│ │ │ │ ├── model/ # 领域模型
│ │ │ │ ├── repository/ # 仓库接口
│ │ │ │ └── service/ # 领域服务
│ │ │ ├── infrastructure/ # 基础设施层
│ │ │ │ ├── persistence/ # 数据持久化
│ │ │ │ ├── messaging/ # 消息通信
│ │ │ │ └── external/ # 外部服务
│ │ │ └── interfaces/ # 接口层(表现层)
│ │ │ ├── rest/ # REST API
│ │ │ ├── web/ # Web 界面
│ │ │ └── dto/ # 请求/响应 DTO
│ │ └── resources/
│ └── test/
└── pom.xml / build.gradle

分层架构的优缺点

优点

优点说明
结构清晰职责分明,易于理解和维护
可测试性每层可以独立测试,便于单元测试
可替换性层内实现可以独立替换,不影响其他层
团队协作不同团队可以专注于不同层
技术栈灵活每层可以选择最适合的技术

缺点

缺点说明
性能开销层间转换带来额外的性能损耗
过度设计简单应用使用分层可能显得冗余
层间耦合如果设计不当,层间可能产生紧耦合
代码重复DTO 转换可能产生大量样板代码

分层架构的最佳实践

1. 避免跨层调用

// 不好的实践:表现层直接调用数据访问层
@RestController
public class BadUserController {
@Autowired
private UserRepository userRepository; // 直接依赖数据层

@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userRepository.findById(id); // 缺少业务逻辑处理
}
}

// 好的实践:严格分层调用
@RestController
public class GoodUserController {
private final UserService userService; // 只依赖业务层

public GoodUserController(UserService userService) {
this.userService = userService;
}

@GetMapping("/users/{id}")
public UserDTO getUser(@PathVariable Long id) {
return userService.getUserById(id); // 通过业务层获取数据
}
}

2. 使用依赖注入

@Configuration
public class ApplicationConfig {

@Bean
public UserRepository userRepository(JpaUserRepository jpaRepository) {
return new UserRepositoryImpl(jpaRepository);
}

@Bean
public UserService userService(UserRepository userRepository,
PasswordEncoder passwordEncoder,
EmailService emailService) {
return new UserServiceImpl(userRepository, passwordEncoder, emailService);
}
}

3. 异常处理分层

// 领域层异常
public class DomainException extends RuntimeException {
public DomainException(String message) {
super(message);
}
}

// 应用层异常
public class ApplicationException extends RuntimeException {
private final ErrorCode errorCode;

public ApplicationException(ErrorCode errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
}

// 全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException e) {
ErrorResponse error = new ErrorResponse(
HttpStatus.NOT_FOUND.value(),
e.getMessage(),
LocalDateTime.now()
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}

@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusiness(BusinessException e) {
ErrorResponse error = new ErrorResponse(
HttpStatus.BAD_REQUEST.value(),
e.getMessage(),
LocalDateTime.now()
);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
}
}

分层架构与其他架构的关系

分层架构是很多其他架构的基础:

  • MVC 架构:是分层架构在 Web 应用中的具体实现
  • 六边形架构:将分层架构中的层改为内部和外部
  • 整洁架构:强调依赖方向指向领域层
  • 微服务架构:每个服务内部可以使用分层架构

总结

分层架构是软件架构的基石,理解分层架构有助于掌握更复杂的架构模式。关键在于:

  1. 严格分层:遵守单向依赖原则
  2. 清晰职责:每层只做该层的事情
  3. 接口隔离:通过接口定义层间契约
  4. 适度设计:根据项目复杂度选择合适的层数

"好的架构应该让系统易于理解、易于修改、易于扩展。分层架构通过关注点分离实现了这一目标。" —— Robert C. Martin

延伸阅读