架构风格
单体、微服务与 Serverless。
30 分钟
架构风格:分层、六边形、CQRS 与事件驱动
这一课你将能做到
- 说出分层、六边形、CQRS、事件驱动四种架构风格的核心结构和适用场景
- 在判题平台里画出四层分层架构图,明确每层能调谁、不能调谁
- 判断"读写分离"和"事件总线"什么时候适合用,什么时候不适合
为什么要学
一个判题平台刚开始可能只是个单体应用,但随着用户量增长,读多写少、判题异步、多终端接入,架构风格的选择决定了扩展路径。分层架构是入门必备,六边形让核心业务不依赖框架,CQRS 让查询性能飞起,事件驱动让异步流程解耦。本课给你一张架构地图,知道什么时候走哪条路。
先建立图像
分层架构 六边形架构(端口适配器)
────────── ──────────────────────
Controller 外部 HTTP/API 适配器
↓ 核心业务(领域)
Service 外部 DB/MQ 适配器
↓
Repository
↓
Database
CQRS 事件驱动
────── ────────
写模型: Submission SubmissionSubmitted → EventBus
↓保存 ↓
读模型: SubmissionView JudgeEngine 消费 → 判题
↓查询 ↓
Dashboard JudgeResultEvent → 通知/更新
跟着做
步骤 1:四层分层架构——判题平台版
┌──────────────┐
│ 用户界面层 │ Controller、Filter、WebSocketHandler
├──────────────┤
│ 应用层 │ Service、CommandHandler、事件发布
├──────────────┤
│ 领域层 │ Entity、ValueObject、DomainService
├──────────────┤
│ 基础设施层 │ RepositoryImpl、MessageQueue、外部API
└──────────────┘
规则:上层可调用下层,同层可调用,下层不能调上层。领域层是核心,不依赖 Spring、不依赖 MyBatis。
// 领域层:纯 POJO,不依赖框架
public class Submission {
private SubmissionId id;
private UserId userId;
private LessonId lessonId;
private CodeSnippet code;
private SubmissionStatus status;
public void judge(JudgeResult result) {
this.status = result.isAccepted() ? ACCEPTED : REJECTED;
}
}
// 基础设施层:依赖领域层,实现 Repository 接口
@Repository
public class MybatisSubmissionRepository implements SubmissionRepository {
private final SubmissionMapper mapper;
@Override
public void save(Submission submission) { /* 转换并写入 DB */ }
}
步骤 2:六边形架构——端口与适配器
六边形也叫"端口适配器",核心思想:业务逻辑不依赖外部世界,外部世界通过"端口"接入。
// 输入端口(驱动端口)
public interface SubmitUseCase {
SubmissionId handle(SubmitCommand command);
}
// 输出端口(被驱动端口)
public interface SubmissionRepository {
void save(Submission submission);
Optional<Submission> findById(SubmissionId id);
}
// 核心业务在六边形内部
@Service
public class SubmitService implements SubmitUseCase {
private final SubmissionRepository repository;
public SubmitService(SubmissionRepository repository) {
this.repository = repository;
}
public SubmissionId handle(SubmitCommand command) {
Submission submission = Submission.create(command);
repository.save(submission);
return submission.getId();
}
}
// HTTP 适配器(六边形外部)
@RestController
public class SubmissionController {
private final SubmitUseCase submitUseCase; // 依赖端口,不依赖实现
@PostMapping("/submissions")
public ResponseEntity<SubmissionId> submit(@RequestBody SubmitRequest req) {
return ResponseEntity.ok(submitUseCase.handle(req.toCommand()));
}
}