Java后端架构设计:外卖霸王餐接口对接中的分层解耦实践思路

在外卖平台对接“霸王餐”营销能力的场景中,业务逻辑复杂、外部依赖多、变更频繁,若不进行合理分层与解耦,极易导致代码腐化和维护成本飙升。本文基于baodanbao.com.cn项目实践,展示如何通过清晰的分层架构实现高内聚、低耦合的接口对接系统。

四层架构划分

项目采用标准四层结构,每层职责单一:

  • Controller层:接收HTTP请求,参数校验,调用Service。
  • Service层:核心业务编排,事务控制。
  • Domain层:领域模型与业务规则(可选独立模块)。
  • Infrastructure层:外部服务调用、数据库访问等技术实现。

各层包结构如下:

baodanbao.com.cn.controller     // 控制器
baodanbao.com.cn.service        // 业务接口
baodanbao.com.cn.service.impl   // 业务实现
baodanbao.com.cn.domain.model   // 领域实体
baodanbao.com.cn.infra.client   // 外部API客户端
baodanbao.com.cn.infra.mapper   // 数据库映射

Controller层:协议转换与校验

仅处理Web相关逻辑,不包含业务判断:

// baodanbao.com.cn.controller.QualifyController.java
@RestController
@RequestMapping("/api/v1")
public class QualifyController {

    @Autowired
    private UserQualifyService userQualifyService;

    @PostMapping("/qualify")
    public ResponseEntity<QualifyResult> checkQualify(@Valid @RequestBody QualifyRequest req) {
        QualifyResult result = userQualifyService.check(req.getUserId(), req.getMerchantId());
        return ResponseEntity.ok(result);
    }
}

在这里插入图片描述

Service层:业务编排与事务边界

定义接口并实现,隔离外部依赖:

// baodanbao.com.cn.service.UserQualifyService.java
public interface UserQualifyService {
    QualifyResult check(String userId, String merchantId);
}
// baodanbao.com.cn.service.impl.UserQualifyServiceImpl.java
@Service
@Transactional(readOnly = true)
public class UserQualifyServiceImpl implements UserQualifyService {

    @Autowired
    private BaoDanBaoApiClient baoDanBaoClient; // 外部API

    @Autowired
    private QualifyRecordMapper recordMapper;  // 本地DB

    @Override
    public QualifyResult check(String userId, String merchantId) {
        // 1. 查询本地缓存或记录(Infrastructure)
        QualifyRecord local = recordMapper.findByUserIdAndMerchant(userId, merchantId);
        if (local != null && !isExpired(local)) {
            return new QualifyResult(local.isQualified(), local.getReason());
        }

        // 2. 调用霸王餐远程API(Infrastructure)
        RemoteQualifyResponse remoteResp = baoDanBaoClient.checkRemote(
            new RemoteQualifyRequest(userId, merchantId)
        );

        // 3. 持久化结果(Infrastructure)
        QualifyRecord newRecord = new QualifyRecord(userId, merchantId, remoteResp.isQualified(), remoteResp.getReason());
        recordMapper.insert(newRecord);

        return new QualifyResult(remoteResp.isQualified(), remoteResp.getReason());
    }

    private boolean isExpired(QualifyRecord record) {
        return System.currentTimeMillis() - record.getCreateTime().getTime() > 3600_000; // 1小时过期
    }
}

Infrastructure层:封装外部依赖

将第三方API调用抽象为接口,便于Mock和替换:

// baodanbao.com.cn.infra.client.BaoDanBaoApiClient.java
@Component
public class BaoDanBaoApiClient {

    private final RestTemplate restTemplate;
    private final BaoDanBaoProperties properties;

    public BaoDanBaoApiClient(RestTemplate restTemplate, BaoDanBaoProperties props) {
        this.restTemplate = restTemplate;
        this.properties = props;
    }

    public RemoteQualifyResponse checkRemote(RemoteQualifyRequest request) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("X-App-Id", properties.getAppId());
        headers.set("X-Sign", generateSign(request));

        HttpEntity<RemoteQualifyRequest> entity = new HttpEntity<>(request, headers);
        return restTemplate.postForObject(
            properties.getBaseUrl() + "/v1/qualify/check",
            entity,
            RemoteQualifyResponse.class
        );
    }

    private String generateSign(RemoteQualifyRequest req) {
        // 签名逻辑略
        return "SIGNATURE";
    }
}

数据库操作通过MyBatis Mapper实现:

// baodanbao.com.cn.infra.mapper.QualifyRecordMapper.java
@Mapper
public interface QualifyRecordMapper {
    QualifyRecord findByUserIdAndMerchant(@Param("userId") String userId, @Param("merchantId") String merchantId);
    void insert(QualifyRecord record);
}

依赖方向控制:禁止跨层调用

通过ArchUnit单元测试强制约束调用方向:

// src/test/java/baodanbao/com/cn/arch/ArchitectureTest.java
@Test
void services_should_not_depend_on_controller() {
    ArchRule rule = noClasses()
        .that().resideInAPackage("..service..")
        .should().dependOnClassesThat().resideInAPackage("..controller..");
    rule.check(JavaClassImporter.importPackages("baodanbao.com.cn"));
}

确保依赖流向为:Controller → Service → Infrastructure,不可逆向。

领域模型隔离

关键业务对象定义在domain.model中,不暴露技术细节:

// baodanbao.com.cn.domain.model.QualifyResult.java
public class QualifyResult {
    private final boolean qualified;
    private final String reason;

    public QualifyResult(boolean qualified, String reason) {
        this.qualified = qualified;
        this.reason = reason;
    }

    // only getters
}

该类被Controller、Service、Infrastructure共同使用,但不包含JPA注解或JSON注解,保持纯净。

通过上述分层与解耦设计,外卖系统在接入霸王餐能力时,可独立演进各层逻辑,降低变更风险,提升测试覆盖率与系统稳定性。

本文著作权归 俱美开放平台 ,转载请注明出处!

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐