1. CPS/SPS系统模块化工程概述
在电商和供应链领域,佣金分成系统(CPS)和供应商平台系统(SPS)的复杂度随着业务发展呈指数级增长。去年我们重构某跨境电商平台时,原始单体架构的代码库已经膨胀到80万行,新功能开发平均需要3周才能上线。通过模块化改造后,核心交易模块的部署频率提升至每天2次,这正是合理的包结构设计带来的直接价值。
典型的CPS/SPS系统包含以下核心模块:
- 订单跟踪模块(Tracking Core)
- 佣金计算引擎(Commission Engine)
- 供应商管理门户(Vendor Portal)
- 数据报表服务(Reporting Service)
- 结算对账系统(Settlement System)
2. 分层架构设计原则
2.1 垂直分层与水平切割
我们采用"三纵四横"的立体分割策略:
纵向业务层: ├── 订单域(Order) ├── 商品域(Product) └── 结算域(Settlement) 横向技术层: ├── API适配层(Adapter) ├── 业务逻辑层(Service) ├── 领域模型层(Domain) └── 基础设施层(Infra)在Java工程中,这对应着如下的包命名规范:
com.company.sps ├── order │ ├── adapter │ ├── service │ └── domain └── product ├── adapter ├── service └── domain2.2 依赖流向控制
通过ArchUnit测试确保依赖关系合规:
@ArchTest static final ArchRule layer_dependencies = layeredArchitecture() .layer("Adapter").definedBy("..adapter..") .layer("Service").definedBy("..service..") .layer("Domain").definedBy("..domain..") .whereLayer("Adapter").mayOnlyBeAccessedByLayers("Adapter") .whereLayer("Service").mayOnlyBeAccessedByLayers("Adapter", "Service") .whereLayer("Domain").mayOnlyBeAccessedByLayers("Service", "Domain");3. 模块间通信设计
3.1 跨模块调用规范
对于强依赖场景,使用清晰的DTO定义:
// 在sps-commission模块中 package com.company.sps.commission.api; public record CommissionCalculateRequest( @NotBlank String orderId, @NotNull BigDecimal amount, @Valid List<CommissionRule> rules ) {} // 在sps-order模块调用方 @FeignClient(name = "commission-service") public interface CommissionClient { @PostMapping("/api/commission/calculate") CommissionResult calculate(@RequestBody CommissionCalculateRequest request); }3.2 事件驱动架构实现
采用Spring Cloud Stream实现领域事件:
// 在领域层定义事件 package com.company.sps.order.domain.event; public class OrderCompletedEvent { private String orderId; private LocalDateTime completedAt; // 其他领域属性 } // 在服务层发布事件 @Service @RequiredArgsConstructor public class OrderService { private final OrderEventSource eventSource; public void completeOrder(String orderId) { // 业务逻辑... eventSource.publish(new OrderCompletedEvent(orderId, LocalDateTime.now())); } }4. 配置管理策略
4.1 多环境配置隔离
使用Spring Profile实现环境隔离:
src/main/resources ├── application.yml ├── application-dev.yml ├── application-uat.yml └── application-prod.yml模块特有配置采用独立前缀:
# 在sps-settlement模块中 sps: settlement: cron: "0 0 2 * * ?" retryTimes: 3 alertEmails: "finance@company.com"4.2 共享配置管理
公共配置放在core模块:
sps-core └── src/main/resources ├── META-INF/spring │ └── common-beans.xml └── application-common.yml通过@ImportResource引入:
@SpringBootApplication @ImportResource("classpath:META-INF/spring/common-beans.xml") public class SettlementApplication { public static void main(String[] args) { SpringApplication.run(SettlementApplication.class, args); } }5. 异常处理体系
5.1 错误码规范
采用分段式错误码设计:
public enum ErrorCode { // 格式: 系统码(2位) + 模块码(2位) + 错误类型(2位) + 具体错误(2位) ORDER_NOT_FOUND("010101", "订单不存在"), PRODUCT_INVENTORY_SHORTAGE("020201", "库存不足"); private final String code; private final String message; // ... }5.2 全局异常处理
在core模块实现统一拦截:
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) public ResponseEntity<ErrorResponse> handleBusinessException( BusinessException ex) { return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(new ErrorResponse(ex.getErrorCode(), ex.getMessage())); } @ExceptionHandler(TechnicalException.class) public ResponseEntity<ErrorResponse> handleTechnicalException( TechnicalException ex) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(new ErrorResponse(ex.getErrorCode(), "系统繁忙")); } }6. 构建与部署优化
6.1 多模块构建配置
父pom关键配置示例:
<modules> <module>sps-core</module> <module>sps-order</module> <module>sps-commission</module> </modules> <dependencyManagement> <dependencies> <dependency> <groupId>com.company</groupId> <artifactId>sps-core</artifactId> <version>${project.version}</version> </dependency> </dependencies> </dependencyManagement>6.2 容器化部署策略
Docker多阶段构建示例:
# 构建阶段 FROM maven:3.8.6-openjdk-11 as builder WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn package -DskipTests # 运行阶段 FROM openjdk:11-jre-slim COPY --from=builder /app/target/sps-order.jar /app.jar ENTRYPOINT ["java","-jar","/app.jar"]7. 监控与可观测性
7.1 日志规范设计
使用MDC实现请求追踪:
@Aspect @Component @RequiredArgsConstructor public class LoggingAspect { private final Tracer tracer; @Before("execution(* com.company.sps..*Service.*(..))") public void logServiceAccess(JoinPoint joinPoint) { MDC.put("traceId", tracer.currentSpan().context().traceIdString()); log.info("Executing: " + joinPoint.getSignature()); } }日志配置文件示例:
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%X{traceId}] %-5p %c{1}:%L - %m%n"/>7.2 指标监控埋点
使用Micrometer暴露指标:
@RestController @RequiredArgsConstructor public class OrderController { private final MeterRegistry registry; @PostMapping("/orders") public Order createOrder(@RequestBody OrderRequest request) { registry.counter("order.create.requests").increment(); long start = System.currentTimeMillis(); Order order = orderService.create(request); registry.timer("order.create.latency") .record(System.currentTimeMillis() - start, TimeUnit.MILLISECONDS); return order; } }8. 开发效率提升技巧
8.1 代码生成策略
使用Freemarker模板生成重复代码:
public class CodeGenerator { public void generateRepository(EntityClass entity) { Configuration cfg = new Configuration(Configuration.VERSION_2_3_31); cfg.setClassForTemplateLoading(getClass(), "/templates"); Template template = cfg.getTemplate("Repository.ftl"); StringWriter writer = new StringWriter(); template.process(Map.of("entity", entity), writer); Files.writeString( Paths.get("src/main/java/com/company/sps/" + entity.getModuleName() + "/repository/" + entity.getName() + "Repository.java"), writer.toString() ); } }8.2 本地开发配置
在application-dev.yml中配置开发工具:
spring: devtools: restart: enabled: true datasource: url: jdbc:h2:mem:testdb username: sa password: jpa: show-sql: true hibernate: ddl-auto: update9. 安全防护方案
9.1 接口权限控制
基于Spring Security的模块化配置:
@Configuration @EnableWebSecurity @RequiredArgsConstructor public class ModuleSecurityConfig { private final ModuleProperties properties; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers(properties.getPublicApis()).permitAll() .antMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated() .and() .oauth2ResourceServer() .jwt(); return http.build(); } }9.2 敏感数据保护
使用Jasypt加密配置:
@Bean public StringEncryptor encryptor() { PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor(); encryptor.setPoolSize(4); encryptor.setPassword(System.getenv("JASYPT_PASSWORD")); encryptor.setAlgorithm("PBEWithMD5AndTripleDES"); return encryptor; }10. 持续演进策略
10.1 模块解耦路线
渐进式拆分方案:
阶段1:功能分组 - 按业务领域划分package - 共享数据库 阶段2:物理分离 - 独立模块工程 - 数据库schema分离 阶段3:服务化 - 独立部署单元 - 领域事件驱动10.2 技术债管理
在TODO注释中标记技术债:
// TODO [技术债] 2023-12前需重构 // 当前使用同步调用影响性能 // 应改为异步事件处理 public void processOrder(Order order) { commissionService.calculate(order); // 同步调用 }通过SonarQube配置技术债看板:
sonar: issuesReport: html: enable: true technicalDebt: estimation: man_days rating: A