Skip to content

Java 后端分层架构与跨微服务 RPC 调用实战指南

归类:Java 架构 / 分层设计与微服务 RPC
更新时间:2026-09-09
状态:✅ 已沉淀 | 📝 持续维护


一、架构分层概念与前端生态对照表

为了帮助全栈开发者快速建立对 Java 后端与 SCF 微服务架构的直观理解,下表将 Java 后端的核心分层组件与前端 Node.js/TypeScript 生态进行了 1:1 具象化类比:

Java 后端组件核心职责与设计原则前端 (Node.js/TS) 具象化类比真实项目节点 (fi_scf_fifac & pay_scf_expense)
DTO / VO纯数据载体,无业务逻辑。跨层/跨服务传递入参与结果。TypeScript Interface / type 类型定义ExpenseClosingDetailVO / TeamBuildingVerifyResult
BFF Controller对外 HTTP API 路由入口。负责参数校验、解析 DTO、调用业务服务、组装 HTTP 响应。Express / NestJS Router / ControllerTeamBuildingCheckController.java
SCF 契约层 (Contract)微服务接口定义与数据结构约束(即 RPC 协议)。纯规范,不跑服务@types 共享包 / gRPC proto / tRPC Router DefinitionIPayExpenseQueryService.java (位于 contract 模块)
SCF 服务层 (Service Impl)核心业务逻辑实现(流程编排、校验、算法、状态匹配)。NestJS Service / Koa 业务处理函数PayExpenseQueryServiceImpl.java (实现 IPayExpenseQueryService)
DAO (Data Access Object)封装数据库 CRUD 操作,防 SQL 注入,严禁暴露给 Controller 直连。Prisma Client / TypeORM Repository / Knex 查询函数FillDao.java / FillTbDetailDao.java
Maven 编译构建依赖管理与项目编译打包(编译 .java 源码生成 .class / .jar)。非运行时npm run build / pnpm / Webpack 打包阶段pom.xml 配置定义

二、真实项目实战案例:团建费计提核对 (TeamBuilding Provision Check)

在 58 业财系统中,fi_scf_fifac(财务总账与凭证核算中心)需要向 pay_scf_expense(费用业务中心)抓取当期(如 202608)指定公司编码下的团建费计提提单数据,并与财务凭证状态进行双向核对。

1. 运行时全链路调流流转

text
[前端 Vue/React 页面]

    │  1. HTTP POST /skill/teamBuilding/verify (period=202608, companyCode=58)

[BFF 接入层] TeamBuildingCheckController.java  (fi_scf_fifac)

    │  2. 参数规范化与 RPC 组装,调用 SCF 客户端

[RPC 契约门面] IPayExpenseQueryService.java  (pay_scf_expense contract 规范)

    │  3. SCF (TCP/RPC 协议传输),传递请求参数 DTO

[服务逻辑层] PayExpenseQueryServiceImpl.java  (pay_scf_expense service)

    │  4. 执行团建费计提业务编排,调用 DAO 读库

[数据访问层] FillDao.java / FillTbDetailDao.java  (pay_scf_expense)

    │  5. SQL: SELECT * FROM tb_fill WHERE fill_type = 'TEAM_BUILDING_PROVISION'

[MySQL 数据库] 存储提单主表 `tb_fill` 与 团建费明细表 `tb_fill_tb_detail`

    ├─► 6. DAO 返回数据库实体 DO (FillDb / FillTbDetailDb)
    ├─► 7. Service 转换组装为传输对象 List<ExpenseClosingDetailVO>
    ├─► 8. 通过 SCF 网络 RPC 将 DTO 结果原路回传给 fi_scf_fifac
    └─► 9. BFF 组装最终结果 TeamBuildingVerifyResult,以 JSON 格式响应前端

三、各层核心代码与职责拆解

1. BFF 接入层 (Controller)

  • 源码物理节点fi_scf_fifac/fi-fac-service/.../TeamBuildingCheckController.java
  • 前端类比:相当于 Express/NestJS 中的 API 路由。
  • 核心代码逻辑
    java
    @RestController
    @RequestMapping("/skill/teamBuilding")
    public class TeamBuildingCheckController {
        @Autowired
        private IExpenseClosingCheckService expenseClosingCheckService;
    
        @PostMapping("/verify")
        public TeamBuildingVerifyResult verifyTeamBuilding(@RequestParam String period, @RequestParam String companyCode) {
            // 1. 参数校验与格式标准化 (如 2026-08 -> 202608)
            String normalizedPeriod = DateUtil.normalizePeriod(period);
            
            // 2. 调用服务层做团建费计提抓取与核对
            Map<String, List<ExpenseClosingDetailVO>> detailVoMap = 
                expenseClosingCheckService.queryExpenseDetails(normalizedPeriod, companyCode);
            
            // 3. 组装响应 DTO 返回给前端
            return TeamBuildingVerifyResult.builder().data(detailVoMap).build();
        }
    }

2. SCF 契约层与 DTO (Contract & Interface)

  • 源码物理节点pay_scf_expense/contract/.../IPayExpenseQueryService.java
  • 前端类比:相当于 Monorepo 中 @shared/types 的 TS Interface 定义与 tRPC 服务契约。
  • 核心代码逻辑
    java
    public interface IPayExpenseQueryService {
        /**
         * 查询指定期间与公司编码下的团建费计提明细列表
         */
        List<ExpenseClosingDetailVO> queryTeamBuildingProvisionDetails(String period, String companyCode);
    }
  • DTO 数据载体 (ExpenseClosingDetailVO.java)
    java
    public class ExpenseClosingDetailVO implements Serializable {
        private String billNo;       // 提单单号 (如 TB2026080001)
        private String companyCode;  // 公司编码 (如 58)
        private String period;       // 会计期间 (如 202608)
        private BigDecimal amount;   // 计提总金额
        private Integer status;      // 凭证状态
    }

3. SCF 服务层 (Service Implementation)

  • 源码物理节点pay_scf_expense/service/.../PayExpenseQueryServiceImpl.java
  • 前端类比:相当于 NestJS 的 @Injectable() class ExpenseService 业务服务。
  • 核心代码逻辑
    java
    @ServiceBehavior
    public class PayExpenseQueryServiceImpl implements IPayExpenseQueryService {
        @Inject
        private FillDao fillDao;
        @Inject
        private FillTbDetailDao fillTbDetailDao;
    
        @Override
        public List<ExpenseClosingDetailVO> queryTeamBuildingProvisionDetails(String period, String companyCode) {
            // 1. 调用 DAO 查询数据库
            List<FillDb> fillList = fillDao.queryByPeriodAndType(period, companyCode, FillTypeEnum.TEAM_BUILDING_PROVISION.getCode());
            
            // 2. 业务逻辑转换:将数据库 DO (FillDb) 转换为对外传输 DTO (ExpenseClosingDetailVO)
            List<ExpenseClosingDetailVO> result = new ArrayList<>();
            for (FillDb db : fillList) {
                ExpenseClosingDetailVO vo = new ExpenseClosingDetailVO();
                vo.setBillNo(db.getBillNo());
                vo.setAmount(db.getTotalMoney());
                vo.setPeriod(db.getPeriod());
                result.add(vo);
            }
            return result;
        }
    }

4. 数据访问层 (DAO)

  • 源码物理节点pay_scf_expense/service/.../FillDao.java
  • 前端类比:相当于 Prisma DB Client / TypeORM Repository。
  • 核心代码逻辑
    java
    public class FillDao {
        public List<FillDb> queryByPeriodAndType(String period, String companyCode, int fillType) {
            // 纯粹的数据库 SQL 查询逻辑,只负责与 MySQL 数据库交互,不掺杂任何业务规则
            return sqlSession.selectList("FillMapper.queryList", Map.of("period", period, "companyCode", companyCode, "fillType", fillType));
        }
    }

四、架构分层与依赖强约束原则

  1. 单向依赖原则(上层依赖下层,下层不感知上层)

    • Controller ➡️ 依赖 Service 接口与 DTO
    • Service ➡️ 依赖 DAO
    • DAO ➡️ 依赖数据库
    • 禁止倒挂:DAO 绝对不能反向调用 Service;Service 不能感知 Controller 的存在。
  2. 数据隔离原则(DO/PO 物理隔离 DTO/VO)

    • DAO 数据库实体 (DO/PO):只在 Service 和 DAO 内部流转,存储数据库的物理字段(如 is_deleted, create_time)。
    • 对外传输对象 (DTO/VO):由 Service 组装完成后,向 Controller 或 RPC 外部回传,绝对禁止将 DAO 的 DO 对象直接回传给前端或 RPC 外部客户端!
  3. 契约解耦原则 (Contract Separation)

    • fi_scf_fifac 并不直接依赖 pay_scf_expense 的实现代码,只依赖其 contract jar 包。
    • 这与前端 npm i @types/api-spec 解耦的思想完全一致。

五、全景 Mermaid 架构调用图


六、记忆口诀(全栈极速复盘)

  1. HTTP 进 Controller:前端请求第一站,校验参数解析 JSON。
  2. RPC 靠 Contract:跨服务调用不硬连,全凭契约接口做约定。
  3. 业务在 Service:算法、流程、对账逻辑全在 Service 里实现。
  4. 存取找 DAO:数据库 SQL 封装在 DAO,DO 对象防泄漏。
  5. 传输用 DTO:数据结构只当载体,不写逻辑只传参。
  6. Maven 打包用:上线编译 npm build,运行时不参与调用。