Java 后端开发:新架构趋势解析(DDD、六边形、模块化单体、事件驱动、平台工程)
文章目录
Java 后端开发:新架构趋势详解(DDD、六边形、模块化单体、事件驱动、平台工程)
引言
当业务复杂度、团队规模、上线频率不断上升,传统的三层架构(Controller/Service/DAO)很容易变成“文件夹分类”,最终带来:
- 服务层越来越大,变更难以定位
- 模块之间通过共享数据库实体/公共包耦合
- 技术细节(DB/MQ/第三方接口)侵入业务代码
- 跨模块协同依赖链很长,上线风险高
这篇文章用清晰的方式解释这些架构趋势:它们各自解决什么问题、怎么落地、适合哪些场景。
1. 从三层模式到领域边界
1.1 三层模式在复杂业务下的常见问题
在简单系统中,三层模式很高效;但业务一变复杂,就容易出现:
- “业务规则”散落在多个类里:Service/DAO/查询逻辑都写着规则
- 同一个业务概念在不同模块被重复建模(“订单”在多个模块有不同含义)
- 团队为了复用,把 Entity 在多个模块之间传来传去,导致对象随处可改
- 改一条流程需要改多个 Service,甚至改 SQL,影响范围不可控
1.2 领域边界(Bounded Context)的意义
领域边界的核心是:在系统内部划清“职责范围”和“术语范围”。
- 每个边界只定义自己理解的业务概念
- 对外只暴露必要的接口(API/DTO/事件)
- 内部实现(数据库表、具体状态机、具体流程)不泄露给外部
这样做的好处是:模块间协作更清晰、代码变化集中、测试和上线风险更小。
1.3 划分边界的实践步骤(建议)
- 列出主要业务能力:订单、库存、结算、会员、风控、支付等
- 逐项回答:这个能力由哪个团队负责?数据归谁维护?规则属于谁?
- 确定上下游关系:谁是来源(上游),谁依赖(下游)
- 定义对外契约:API/DTO/事件(不要在跨边界时传数据库实体)
1.4 示例:电商业务
- 订单负责订单生命周期(创建、支付、取消、完成)
- 库存负责库存锁定/扣减,但不需要理解订单所有字段
- 会员/营销只需要订单事件做优惠发放,并不直接改订单系统
- 支付只负责完成支付流程并把支付结果通知订单
2. DDD:把业务规则放到核心
DDD 的目标不是“用很多名词”,而是让业务规则变得可读、可维护、可演进。
2.1 战略设计:先确定边界,再谈实现
- Bounded Context:确定哪些功能属于同一个边界
- Context Map:确定边界与边界的关系
- 上游/下游(Upstream/Downstream)
- 反腐层(ACL):当接入上游的模型与本边界不一致时,做转换
- 共享内核:少量稳定的共享模型(谨慎使用)
2.2 战术设计:把规则和模型写“对”
- 实体(Entity):有唯一身份(ID),状态可变化
- 值对象(Value Object):描述属性、可比较、不可变
- 聚合/聚合根(Aggregate/Aggregate Root):把一致性规则放在边界内,外部只能通过聚合根修改
- 领域服务(Domain Service):当规则跨多个对象且不属于某一个对象时使用
- 应用服务(Application Service):负责用例编排、事务边界、调用外部端口
- 领域事件(Domain Event):表达“发生了某件业务上的变化”,用于内部解耦
- 仓储(Repository):聚合的持久化接口
2.3 示例:订单聚合与领域事件
// 领域层
class Order {
private final OrderId id;
private OrderStatus status;
private Money total;
void pay(String paymentId) {
if (status != OrderStatus.CREATED) {
throw new IllegalStateException("Only CREATED order can be paid");
}
status = OrderStatus.PAID;
DomainEvents.raise(new OrderPaid(id, paymentId));
}
}
// 应用层
@Service
class OrderCommandService {
private final OrderRepository orderRepository;
private final PaymentPort paymentPort;
@Transactional
void payOrder(OrderId id, PaymentRequest request) {
Order order = orderRepository.get(id);
String paymentId = paymentPort.pay(order, request);
order.pay(paymentId);
orderRepository.save(order);
}
}
领域层负责“规则”和“状态变化”,应用层负责“流程编排”和“事务”。
3. 模块化单体(Modular Monolith):单体部署,模块化治理
模块化单体强调:部署仍是一个应用,但代码按模块严格划边界,依赖方向受控。它是很多团队从小系统成长到复杂系统时非常实用的形态。
3.1 适用场景
- 业务已经变复杂,但团队/资源不适合马上做微服务治理
- 希望先把边界定义清楚,后续再按边界拆分
- 需要快速迭代,但又不想系统越写越耦合
3.2 目录与依赖约束示例
app
├── order-domain
├── order-app
├── order-adapter
├── order-api
└── shared-kernel (谨慎、少量、稳定)
依赖方向建议:
domain不依赖adapteradapter实现port,不要反向侵入domain- 控制
shared-kernel的内容和体积,避免变成“大杂烩”
3.3 渐进式拆分策略
当某个模块:团队独立、部署频繁、资源隔离需求明确时,才考虑拆成微服务;拆分时尽量沿着之前定义好的边界切割。
4. 六边形架构:端口与适配器
六边形架构(Ports & Adapters)强调:业务逻辑与外部技术细节解耦。
4.1 核心思想
- Domain:纯业务模型(尽量不依赖框架)
- Application:用例编排
- Ports:抽象外部依赖(例如支付、仓储、消息发布)
- Adapters:实现 ports,把外部世界接进来(DB/MQ/HTTP/第三方)
4.2 示例:支付端口
// port
public interface PaymentPort {
String pay(Order order, PaymentRequest request);
}
// adapter(支付渠道A)
@Component
class PaymentAdapterA implements PaymentPort {
public String pay(Order order, PaymentRequest request) {
// 调用渠道A
return channelA.pay(...);
}
}
// adapter(支付渠道B)
@Component
class PaymentAdapterB implements PaymentPort {
public String pay(Order order, PaymentRequest request) {
// 调用渠道B
return channelB.pay(...);
}
}
当支付渠道发生变化,只需要替换 adapter,而不是改业务逻辑。
4.3 适用场景
- 经常需要替换 DB/MQ/缓存/第三方接口
- 希望写更容易测试的业务代码(domain 可单元测试)
- 想降低“技术选型”对业务的影响
5. 事件驱动架构(EDA):用事件解耦跨模块协同
事件驱动的目标是:让一个模块完成自己的主要流程后,用事件通知其他模块做后续处理,从而降低耦合。
5.1 不要把所有事情都变成事件
- 命令(Command):用于“做事”——创建订单、提交支付等
- 事件(Event):用于“通知发生过什么”——订单创建完成、支付成功
主流程还是用命令驱动,事件更适合用来触发边侧动作(补充、增强,而不是主流程的关键链路)。
5.2 领域事件 vs 集成事件
- 领域事件(Domain Event):在边界内用来表达状态变化
- 集成事件(Integration Event):对外系统使用的事件格式,用于跨系统或跨边界通知
很多团队会在内部用领域事件,在发往消息系统时转换成集成事件(DTO)。
5.3 落地要点
- Outbox 模式:把事件与业务更新放在同一事务写入 Outbox 表,再异步发布到 MQ,避免“业务成功但事件丢失”
- 幂等处理:消费端按照 eventId 或业务主键去重,允许重复投递
- 错误重试与死信:明确失败后的重试、死信队列策略,并做好告警
- 可观测性:事件链路需要 Trace/Log/Metrics,方便定位问题
5.4 示例:订单创建事件
订单创建完成后发布事件:
- 库存模块订阅事件:锁定或扣减库存
- 会员/营销模块订阅事件:发放优惠券/积分
- 风控模块订阅事件:做规则检查、记录日志
各模块只需要关心自己的“动作”,不用互相调用复杂接口。
6. 平台工程:把工程能力标准化
平台工程(Platform Engineering)目标是:把研发过程中重复的工作(建项目、部署、监控、日志、告警、流程、规范)统一成“平台能力”,让业务团队更专注业务。
6.1 平台常见能力
- 服务模板/项目脚手架:生成统一的项目结构与治理能力
- CI/CD:构建、测试、发布流水线标准化
- Observability:日志、指标、链路追踪统一
- 配置与密钥管理:安全、可审计、可回滚
- 数据库变更管理:对 schema 变更进行版本控制与回放
- 自动化检测:安全扫描、依赖更新、基础门禁(单测、代码质量)
6.2 平台团队与业务团队的协作方式
平台团队像“产品团队”一样运营平台:有路线图、版本、文档、灰度与支持;业务团队像“平台用户”,通过自助服务完成环境、发布、监控等工作。
7. 如何组合落地(建议)
- 如果刚开始变复杂:优先定义领域边界 + 模块化单体
- 在模块内部:用六边形(端口/适配器)保护业务逻辑
- 跨模块协同:用事件驱动做通知与补充动作
- 工程效率:用平台工程把模板、流程、监控、发布标准化
- 注意防止过度设计:只在“需要”时引入新的机制,并持续复盘成本
总结
这些架构趋势有一个共同目标:边界清晰、职责清晰、变更可控。
它们并不是要你一次做完所有,而是帮你用更小的风险、更清晰的结构,让系统从“可用”走向“可维护”。
更多推荐
所有评论(0)