鲁班学院二期三期Java架构师VIP课程

1 阅读5分钟

一、架构的本质是管理依赖

很多人把架构等同于技术选型,其实架构的第一性问题只有一个:谁依赖谁。

一个健康的 Java 系统,依赖方向应该从外向内:

  • 外层:Web、消息、数据库、缓存;
  • 内层:应用服务、领域模型;
  • 核心:业务规则和领域知识。

内层不应该知道外层的存在。领域模型不应该 import Spring、MyBatis 或 Kafka。这样,当框架升级、数据库更换、协议调整时,业务核心不受影响。

稳定依赖原则、稳定抽象原则,这些听起来抽象,落地就是一句话:让容易变的东西依赖不容易变的东西,而不是反过来。


二、从分层到六边形

传统三层架构 Controller-Service-DAO 简单直接,但容易滑向“贫血模型 + 事务脚本”:业务逻辑散落在 Service,领域对象只剩 getter/setter,数据库表结构驱动代码结构。

六边形架构(端口与适配器)提供了另一种思路:领域在中心,外部通过端口接入。

public interface OrderRepository {
    Optional<Order> findById(OrderId id);
    void save(Order order);
}

这是端口。它属于领域层,只表达业务需要什么,不关心数据库是 MySQL 还是 MongoDB。

@Repository
public class JpaOrderRepository implements OrderRepository {
    private final JpaOrderDao dao;

    public Optional<Order> findById(OrderId id) {
        return dao.findById(id.value()).map(OrderMapper::toDomain);
    }

    public void save(Order order) {
        dao.save(OrderMapper.toEntity(order));
    }
}

这是适配器。它属于基础设施层,负责把 JPA 的细节翻译成领域语言。

好处很直接:领域层可以独立测试;换持久化技术只需新增适配器;业务规则不会被框架注解污染。


三、模块化:单体不是原罪

微服务流行之后,很多团队把“单体”当成贬义词。但一个边界清晰、模块化良好的单体,往往比一堆边界混乱的微服务更容易维护。

在 Java 中,模块化可以分三个层次:

  1. 包结构:按限界上下文划分顶层包,而不是按技术层划分;
  2. 构建模块:Maven/Gradle 多模块,用依赖关系强制边界;
  3. 运行时模块:JPMS 或 Spring Modulith,控制可见性。

模块之间通过事件解耦,而不是互相调用内部服务。例如订单模块发布 OrderPlaced,库存模块监听并扣减。发布者不需要知道订阅者是谁。

架构边界不能只靠自觉,最好写进测试。ArchUnit 可以用几行代码守住规则:

@AnalyzeClasses(packages = "com.example")
class ArchitectureTest {
    @ArchTest
    static final ArchRule domain_should_not_depend_on_infra =
        noClasses().that().resideInAPackage("..domain..")
            .should().dependOnClassesThat().resideInAPackage("..infra..");
}

这条规则进入 CI 后,任何违规依赖都会导致构建失败。架构从“口头约定”变成“可执行约束”。


四、微服务不是默认答案

微服务解决的是组织问题,不是技术问题。拆之前先问四个问题:

  • 团队是否已经大到需要独立部署?
  • 不同模块是否需要独立伸缩?
  • 数据边界是否清晰?
  • 是否愿意承担网络延迟、分布式事务、链路追踪、运维复杂度?

如果答案是否定的,模块化单体通常是更优起点。康威定律告诉我们,系统结构会映射组织沟通结构。强行拆成微服务,只会把代码耦合变成服务耦合。

一个务实的路径是:先模块化单体,再按压力拆分。  当某个模块确实需要独立团队、独立发布、独立扩容时,再把它抽出去。绞杀者模式、分支抽象,都是降低迁移风险的手段。


五、事件驱动与数据一致性

Java 架构中,跨模块、跨服务的一致性无法靠本地事务解决。常见方案是领域事件 + 最终一致性。

核心模式有三个:

  1. Outbox:业务数据和事件写入同一本地事务,再由后台任务投递,避免双写不一致;
  2. Saga:用一系列本地事务和补偿动作替代分布式事务;
  3. 幂等消费者:每个消费者都能安全重放消息,用业务键去重。

不要用 XA 或分布式事务硬扛高并发场景。大多数业务可以接受秒级、分钟级的最终一致,换取更高的可用性和更低的复杂度。


六、可观测性与性能

架构做得再漂亮,线上出问题无法定位就是灾难。Java 系统的可观测性建议统一到三个维度:

  • 日志:结构化 JSON,带 Trace ID;
  • 指标:Micrometer 采集,Prometheus 存储;
  • 追踪:OpenTelemetry 贯穿 HTTP、RPC、DB、消息。

JVM 层面,GC 选择、堆大小、线程模型都会影响架构。Java 21 的虚拟线程让阻塞式编程重新变得有吸引力,但它不会让数据库变快,也不会消除下游瓶颈。架构上仍要关注连接池、背压、超时和隔离。

性能不是事后优化,而是架构决策:同步还是异步、批处理还是流式、缓存放在哪一层、是否允许降级。


七、演进式架构

好的 Java 架构不是一次设计出来的,而是持续演进的。几个实践值得坚持:

  • ADR:记录每个重要架构决策的背景、选项和后果;
  • 架构测试:用 ArchUnit 等工具守住依赖规则;
  • 小步重构:不要一次重写,用绞杀者模式逐步替换;
  • 技术债可见:把债务变成待办事项,而不是口头抱怨。

总结

Java 架构的竞争力,不在于堆了多少框架,而在于:

边界清晰、依赖可控、变化被隔离、演进可持续。

六边形架构帮你分离领域与基础设施,模块化帮你控制单体复杂度,事件驱动帮你解耦一致性,可观测性帮你守住线上稳定。少量代码就能守住边界,但真正的功夫,在于每一次架构决策背后的取舍。