Cursor AI 编程实战(篇三):Domain、Infrastructure 与策略模式

Cursor AI 编程实战(篇三):Domain、Infrastructure 与策略模式

系列 :共三篇。本篇为 附录 F 整段并入:Domain 层、Infrastructure 层、策略模式三份 .mdc 完整正文(已脱敏),适合作为项目规则库主体单独收藏。
上一篇篇二:Rules 与 Adapter/App
合并完整版Cursor-AI编程最佳实践-CSDN发表版.md


系列说明:本文为《Cursor AI 编程实战》连载之一,脱敏整理自团队《最佳实战》。三篇可独立阅读,也可按篇一 → 篇二 → 篇三顺序配合使用。


附录 F:Domain / Infrastructure / 策略模式 .mdc 全文(脱敏)

以下为《最佳实战》原文三份规则的正文级复制,可按需拆成 .cursor/rules/domain.mdcinfrastructure.mdcstrategy-pattern.mdc。文中领域事务示例统一使用 @PlatformTransactional 作为占位注解名,请替换为贵司真实事务 API。@TService@Action 等为 RPC/门面层示例注解,请按实际框架调整。

F.1 Domain 层开发规范(.mdc

markdown 复制代码
---
description: Domain层开发规范
alwaysApply: false
---
# Domain层开发规范

## Domain层职责定义

Domain层是核心业务逻辑层,负责处理复杂的业务规则和领域模型抽象,提供可复用的业务能力。

## 包结构规范

```
domain/
└── 按功能模块划分
├── service/     # 领域服务
├── model/       # 领域模型
├── repository/  # 仓储接口
└── factory/     # 工厂类
```

## DomainService开发规范

### 基础结构模板
```java
@Service
@RequiredArgsConstructor
@Slf4j
public class ModuleDomainService {
    
    private final ModuleRepo moduleRepo;
    private final RelatedDomainService relatedDomainService;
    
    /**
     * 提供可复用的业务能力
     * 包含复杂的业务逻辑和规则
     */
    public BusinessResultDTO processComplexBusiness(BusinessRequestDTO request) {
        // 复杂业务逻辑处理
        return result;
    }
}
```

### 业务逻辑封装原则
- **封装复杂业务规则**,避免在App层重复实现
- **提供可复用的业务能力**,供多个App层服务调用
- **处理领域模型之间的交互**,维护业务完整性
- **隔离外部依赖**,通过仓储接口访问数据

## 缓存使用规范

### 缓存注解使用
```java
@Service
@RequiredArgsConstructor
public class ConfigDomainService {
    
    private final ConfigRepo configRepo;
    
    /**
     * 查询配置信息,使用缓存
     */
    @Cacheable(key = "#id")
    public SlsSoTypeCfDTO getById(Long id) {
        SlsSoTypeCfPO configPO = configRepo.selectById(id);
        return convertToDTO(configPO);
    }
    
    /**
     * 更新配置后清除缓存
     */
    @CacheEvict(key = "#id")
    public void updateConfig(Long id, ConfigUpdateDTO updateDTO) {
        SlsSoTypeCfPO configPO = buildUpdatePO(id, updateDTO);
        configRepo.updateById(configPO);
    }
    
    /**
     * 批量清除缓存
     */
    @CacheEvict(allEntries = true)
    public void clearAllCache() {
        log.info("清除所有配置缓存");
    }
}
```

### 缓存使用原则
- **查询频繁且变更较少的数据使用缓存**
- **缓存key要具有唯一性**,避免冲突
- **数据变更后要及时清除缓存**,保持数据一致性
- **设置合适的过期时间**,避免缓存雪崩


## 领域模型规范

### 领域对象设计原则
- **充血模型**:业务逻辑封装在领域对象内部
- **不变性**:重要的领域对象设计为不可变对象
- **封装性**:隐藏内部实现细节,提供清晰的业务接口

### 领域对象示例
```java
/**
 * 订单领域对象
 */
public class OrderDomain {
    
    private final Long orderId;
    private final String orderNo;
    private OrderStatusEnum status;
    private final List<OrderItemDomain> orderItems;
    private BigDecimal totalAmount;
    
    public OrderDomain(Long orderId, String orderNo, OrderStatusEnum status, 
                      List<OrderItemDomain> orderItems) {
        this.orderId = Objects.requireNonNull(orderId, "订单ID不能为空");
        this.orderNo = Objects.requireNonNull(orderNo, "订单号不能为空");
        this.status = Objects.requireNonNull(status, "订单状态不能为空");
        this.orderItems = Collections.unmodifiableList(
            Objects.requireNonNull(orderItems, "订单项不能为空"));
        this.totalAmount = calculateTotalAmount();
    }
    
    /**
     * 订单状态流转
     */
    public void changeStatus(OrderStatusEnum newStatus) {
        validateStatusTransition(this.status, newStatus);
        this.status = newStatus;
    }
    
    /**
     * 计算订单总金额
     */
    public BigDecimal calculateTotalAmount() {
        return orderItems.stream()
                .map(OrderItemDomain::getItemAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
    
    /**
     * 验证是否可以取消
     */
    public boolean canCancel() {
        return OrderStatusEnum.PENDING.equals(status) || 
               OrderStatusEnum.CONFIRMED.equals(status);
    }
    
    private void validateStatusTransition(OrderStatusEnum from, OrderStatusEnum to) {
        if (!isValidTransition(from, to)) {
            throw new BusinessException(OrderMsg.INVALID_STATUS_TRANSITION, from, to);
        }
    }
}
```

## 业务规则验证规范

### 业务规则封装
```java
@Service
@RequiredArgsConstructor
public class OrderBusinessRuleService {
    
    private final InventoryDomainService inventoryDomainService;
    private final CustomerDomainService customerDomainService;
    
    /**
     * 订单创建前的业务规则验证
     */
    public void validateOrderCreation(OrderCreationDTO orderDTO) {
        log.info("开始验证订单创建规则,订单号:{}", orderDTO.getOrderNo());
        
        // 客户状态验证
        validateCustomerStatus(orderDTO.getCustomerId());
        
        // 库存验证
        validateInventoryAvailability(orderDTO.getOrderItems());
        
        // 价格验证
        validatePricing(orderDTO);
        
        // 业务限制验证
        validateBusinessLimitations(orderDTO);
        
        log.info("订单创建规则验证通过,订单号:{}", orderDTO.getOrderNo());
    }
    
    private void validateCustomerStatus(Long customerId) {
        CustomerDTO customer = customerDomainService.getById(customerId);
        if (customer == null) {
            throw new BusinessException(CustomerMsg.CUSTOMER_NOT_FOUND, customerId);
        }
        
        if (!CustomerStatusDict.ACTIVE.equals(customer.getStatus())) {
            throw new BusinessException(CustomerMsg.CUSTOMER_INACTIVE, customerId);
        }
    }
    
    private void validateInventoryAvailability(List<OrderItemCreationDTO> orderItems) {
        for (OrderItemCreationDTO item : orderItems) {
            Integer availableStock = inventoryDomainService.getAvailableStock(
                item.getSkuId(), item.getWarehouseId());
            
            if (availableStock < item.getQuantity()) {
                throw new BusinessException(InventoryMsg.INSUFFICIENT_STOCK, 
                                          item.getSkuId(), availableStock, item.getQuantity());
            }
        }
    }
}
```

## 领域事件处理规范

### 领域事件定义
```java
/**
 * 订单创建事件
 */
public class OrderCreatedEvent {
    
    private final Long orderId;
    private final String orderNo;
    private final Long customerId;
    private final BigDecimal totalAmount;
    private final LocalDateTime createdTime;
    
    public OrderCreatedEvent(Long orderId, String orderNo, Long customerId, 
                           BigDecimal totalAmount, LocalDateTime createdTime) {
        this.orderId = orderId;
        this.orderNo = orderNo;
        this.customerId = customerId;
        this.totalAmount = totalAmount;
        this.createdTime = createdTime;
    }
    
    // getters...
}
```

### 领域事件发布
```java
@Service
@RequiredArgsConstructor
public class OrderDomainService {
    
    private final ApplicationEventPublisher eventPublisher;
    private final OrderRepo orderRepo;
    
    @PlatformTransactional
    public void createOrder(OrderCreationDTO orderDTO) {
        // 创建订单
        OrderDomain order = buildOrder(orderDTO);
        OrderPO orderPO = convertToPO(order);
        orderRepo.save(orderPO);
        
        // 发布领域事件
        OrderCreatedEvent event = new OrderCreatedEvent(
            order.getOrderId(),
            order.getOrderNo(),
            order.getCustomerId(),
            order.getTotalAmount(),
            LocalDateTime.now()
        );
        eventPublisher.publishEvent(event);
    }
}
```

## 复杂业务流程规范

### 业务流程编排
```java
@Service
@RequiredArgsConstructor
public class OrderProcessDomainService {
    
    private final OrderBusinessRuleService orderBusinessRuleService;
    private final InventoryDomainService inventoryDomainService;
    private final PaymentDomainService paymentDomainService;
    private final LogisticsDomainService logisticsDomainService;
    
    /**
     * 订单处理完整流程
     */
    @PlatformTransactional
    public OrderProcessResultDTO processOrder(OrderProcessDTO processDTO) {
        log.info("开始处理订单流程,订单ID:{}", processDTO.getOrderId());
        
        // 1. 业务规则验证
        orderBusinessRuleService.validateOrderProcessing(processDTO);
        
        // 2. 库存预占
        InventoryReservationDTO reservation = inventoryDomainService.reserveInventory(
            processDTO.getOrderId(), processDTO.getOrderItems());
        
        try {
            // 3. 支付处理
            PaymentResultDTO paymentResult = paymentDomainService.processPayment(
                processDTO.getPaymentInfo());
            
            // 4. 物流安排
            LogisticsArrangementDTO logistics = logisticsDomainService.arrangeLogistics(
                processDTO.getLogisticsInfo());
            
            // 5. 订单状态更新
            updateOrderStatus(processDTO.getOrderId(), OrderStatusEnum.PROCESSED);
            
            log.info("订单流程处理完成,订单ID:{}", processDTO.getOrderId());
            
            return OrderProcessResultDTO.success(reservation, paymentResult, logistics);
            
        } catch (Exception e) {
            // 回滚库存预占
            inventoryDomainService.rollbackReservation(reservation.getReservationId());
            log.error("订单处理失败,订单ID:{}", processDTO.getOrderId(), e);
            throw e;
        }
    }
}
```

F.2 Infrastructure 层开发规范(.mdc

markdown 复制代码
---
description: Infrastructure层开发规范
alwaysApply: false
---
# Infrastructure层开发规范

## Infrastructure层职责定义

Infrastructure层负责数据库CRUD、搜索引擎、文件系统、外部RPC请求等基础设施相关的实现。

## 包结构规范

```
infrastructure/
├── repo/       # 数据访问仓储
└── gateway/    # 外部系统防腐层
```

## Repository开发规范

### 基础结构模板
```java
@Repository
public interface ModuleRepo extends BaseRepository<ModulePO> {
    
    // 基础CRUD方法继承自BaseRepository
    // 自定义查询方法使用default实现
    
    default List<ModulePO> listByCondition(QueryConditionDTO condition) {
        // 实现自定义查询逻辑
    }
}
```

### 查询方法规范
- **避免全表查询**,必须有具体的where条件
- **只查询必要字段**,使用QueryWrapper.select()指定字段
- **批量操作避免循环查询**,使用in条件或批量方法
- **空集合检查**,参数为空时直接返回空列表

### 查询优化原则
```java
@Repository
public interface OptimizedQueryRepo extends BaseRepository<DataPO> {
    
    /**
     * ✅ 正确:指定查询字段,减少数据传输
     */
    default List<DataPO> listBasicInfo(Collection<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return Lists.newArrayList();
        }
        
        QueryWrapper<DataPO> wrapper = new QueryWrapper<>();
        wrapper.select("id", "name", "status", "updated_at");  // 只查询必要字段
        wrapper.lambda().in(DataPO::getId, ids);
        return selectList(wrapper);
    }
    
    /**
     * ✅ 正确:使用IfPresent避免无效查询条件
     */
    default Paging<DataPO> pageQuery(QueryRequestDTO request) {
        LambdaQueryWrapperX<DataPO> wrapper = new LambdaQueryWrapperX<>();
        wrapper.eqIfPresent(DataPO::getShopId, request.getShopId());
        wrapper.likeIfPresent(DataPO::getName, request.getName());
        wrapper.geIfPresent(DataPO::getCreatedAt, request.getStartTime());
        wrapper.leIfPresent(DataPO::getCreatedAt, request.getEndTime());
        wrapper.orderByDesc(DataPO::getUpdatedAt);
        return selectPage(request, wrapper);
    }
    
    /**
     * ✅ 正确:批量操作减少数据库交互
     */
    default void batchUpdateStatus(Map<Long, String> idStatusMap) {
        if (MapUtils.isEmpty(idStatusMap)) {
            return;
        }
        
        List<DataPO> updateList = idStatusMap.entrySet().stream()
                .map(entry -> {
                    DataPO po = new DataPO();
                    po.setId(entry.getKey());
                    po.setStatus(entry.getValue());
                    po.setUpdatedAt(LocalDateTime.now());
                    return po;
                })
                .collect(Collectors.toList());
                
        updateBatchById(updateList);
    }
}
```

### 避免的错误模式
```java
// ❌ 错误:循环查询数据库
for (Long id : ids) {
    DataPO data = repo.selectById(id);  // 禁止在循环中查询
    // 处理逻辑
}

// ❌ 错误:全表查询
List<DataPO> allData = repo.selectList(null);  // 禁止无条件查询

// ❌ 错误:SELECT *
wrapper.select("*");  // 禁止查询所有字段

// ❌ 错误:使用${}参数
@Select("SELECT * FROM table WHERE id = ${id}")  // 存在SQL注入风险
```

## 外部系统防腐层(Gateway)规范

### 基础结构模板
```java
@TService("TARGET_SYSTEM")
public interface ModuleGateway {
    
    @Action(name = "业务操作描述", value = "ACTION_CODE")
    ResultDTO businessOperation(RequestDTO request);
}
```

### Gateway开发要点
- 使用@TService注解标明目标系统
- 使用@Action注解定义具体操作
- 不写Response会自动解包,简化接口定义
- 支持批量操作,提高效率

### 完整示例
```java
/**
 * 销售系统防腐层
 */
@TService("SLS")
public interface OmsSlsGateway {
    
    /**
     * 同步销售订单到OMS
     */
    @Action(name = "销售单-OMS同步", value = "SLS_SO_HEAD_TR_OMS_SYNC_ACTION")
    SlsSoHeadTrDTO syncSalesOrder(SlsSoHeadTrDTO request);
    
}

/**
 * 商品系统防腐层
 */
@TService("GEN")
public interface OmsGenGateway {
    
    /**
     * 批量提交商品信息
     */
    @Action(name = "商品-批量提交", value = "GEN_SKU_BATCH_SUBMIT_ACTION")
    List<GenSkuMdDTO> batchSubmitSku(List<GenSkuMdDTO> skuList);
}
```

## 文件存储(OSS)服务规范

### 文件上传下载封装
```java
@Service
@RequiredArgsConstructor
@Slf4j
public class FileStorageService {
    
    private final OssClient ossClient;
    
    @Value("${oss.bucket.name}")
    private String bucketName;
    
    /**
     * 上传文件
     */
    public String uploadFile(String fileName, InputStream inputStream, String contentType) {
        try {
            String objectKey = generateObjectKey(fileName);
            
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentType(contentType);
            metadata.setCacheControl("max-age=31536000");
            
            PutObjectRequest request = new PutObjectRequest(bucketName, objectKey, inputStream, metadata);
            ossClient.putObject(request);
            
            String fileUrl = getFileUrl(objectKey);
            log.info("文件上传成功,文件名:{},URL:{}", fileName, fileUrl);
            
            return fileUrl;
            
        } catch (Exception e) {
            log.error("文件上传失败,文件名:{}", fileName, e);
            throw new FileStorageException("文件上传失败", e);
        }
    }
    
    /**
     * 批量上传文件
     */
    public List<FileUploadResultDTO> batchUploadFiles(List<FileUploadDTO> files) {
        if (CollectionUtils.isEmpty(files)) {
            return Lists.newArrayList();
        }
        
        return files.stream()
                .map(file -> {
                    try {
                        String url = uploadFile(file.getFileName(), file.getInputStream(), file.getContentType());
                        return FileUploadResultDTO.success(file.getFileName(), url);
                    } catch (Exception e) {
                        log.error("批量上传中单个文件失败:{}", file.getFileName(), e);
                        return FileUploadResultDTO.failure(file.getFileName(), e.getMessage());
                    }
                })
                .collect(Collectors.toList());
    }
    
    /**
     * 下载文件
     */
    public InputStream downloadFile(String objectKey) {
        try {
            OSSObject ossObject = ossClient.getObject(bucketName, objectKey);
            return ossObject.getObjectContent();
        } catch (Exception e) {
            log.error("文件下载失败,对象键:{}", objectKey, e);
            throw new FileStorageException("文件下载失败", e);
        }
    }
    
    private String generateObjectKey(String fileName) {
        String datePath = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
        String uuid = UUID.randomUUID().toString().replace("-", "");
        String extension = getFileExtension(fileName);
        return String.format("%s/%s%s", datePath, uuid, extension);
    }
}
```

F.3 策略模式开发规则(.mdc

markdown 复制代码
---
description: 策略模式开发规则
alwaysApply: false
---
# 策略模式开发规则

## 架构规范

### 代码分层
- 复杂的业务逻辑(如促销优惠计算)应放在 domain 层
- 需要对同一功能进行多种逻辑扩展时,使用策略模式

### 策略模式实现规范

#### 策略接口设计
- 策略接口必须包含 `isMatch()` 方法用于策略匹配
- 策略接口必须包含具体的业务执行方法
- 接口方法需要明确的 JavaDoc 注释

#### 策略实现类规范
- 所有策略实现类必须使用 `@Service` 注解
- 必须使用 `@Strategy` 注解,指定策略接口和实现键
- 实现类命名格式:`{具体策略名}StrategyImpl`

```java
@Service
@Strategy(value = {策略接口}.class, implKey = "{实现类名}")
public class {具体策略名}StrategyImpl implements {策略接口} {
    // 实现代码
}
```

#### 策略加载规范
- 使用 `StrategyLoader.load()` 方法加载匹配的策略
- 策略加载代码模式:
```java
{策略接口} strategy = StrategyLoader.load({策略接口}.class, 
    strategy -> strategy.isMatch({匹配参数}));
```

## 业务逻辑规范

### 促销优惠计算
- 优惠计算必须处理金额溢出情况
- 扣减类优惠需要检查优惠金额是否超过目标金额
- 折扣类优惠使用乘法计算,无需处理溢出

#### 扣减优惠实现要点
```java
// 必须处理溢出逻辑
if (discountAmount.compareTo(targetAmount) > 0) {
    discountAmount = targetAmount;
}
```

#### 折扣优惠实现要点
```java
// 计算折扣率:(10 - 打折数) / 10
BigDecimal cutOffRatio = new BigDecimal(10)
    .subtract(discountRatio)
    .divide(BigDecimal.TEN, MathContext.DECIMAL32);
```

### 数据传输对象(DTO)
- 计算结果使用 Builder 模式构建
- DTO 必须包含必要的业务字段:折扣内容类型、折扣金额等

## 代码质量要求

### 命名规范
- 策略接口命名:`{业务领域}Strategy`
- 策略实现类命名:`{具体策略}StrategyImpl`
- 方法命名要清晰表达业务含义

### 注释规范
- 策略接口必须有完整的类级别注释
- 方法参数和返回值必须有 JavaDoc 注释
- 复杂业务逻辑需要行内注释说明

### 异常处理
- 策略匹配失败时要有明确的处理逻辑
- 金额计算要考虑精度问题,使用 `MathContext.DECIMAL32`

## 扩展性原则

### 开闭原则
- 新增优惠方式时,只需要添加新的策略实现类
- 调用方代码无需修改
- 策略加载机制保持不变

### 单一职责
- 每个策略实现类只负责一种具体的计算逻辑
- 策略接口职责单一,只定义一种业务行为

## 代码示例模板

### 策略接口模板
```java
/**
 * {业务描述}策略接口
 */
public interface {业务名}Strategy {
    
    /**
     * 执行{业务操作}
     * @param param 参数描述
     * @return 返回值描述
     */
    ResultType execute(ParamType param);
    
    /**
     * 是否匹配该策略
     * @param matchParam 匹配参数
     * @return true-匹配,false-不匹配
     */
    boolean isMatch(String matchParam);
}
```

### 策略实现类模板
```java
/**
 * {具体策略描述}
 */
@Service
@Strategy(value = {策略接口}.class, implKey = "{实现类名}")
public class {具体策略}StrategyImpl implements {策略接口} {
    
    @Override
    public ResultType execute(ParamType param) {
        // 具体业务逻辑实现
        return result;
    }
    
    @Override
    public boolean isMatch(String matchParam) {
        return {匹配条件};
    }
}
```

相关推荐
西凉的悲伤1 小时前
java通过url获取 jpg、png、pdf 文件格式
java
SunnyDays10111 小时前
Java 实现 PDF 附件的添加与删除:四种实用方法
java·pdf·附件
phltxy1 小时前
Redis 持久化机制
java·redis·git
键盘飞行员1 小时前
Windsurf + Claude 4.7 前端开发:用 ui-ux-pro-max 根治 “AI 味”、实现全站 UI 统一
前端·ui·ai编程
Gerardisite1 小时前
企业微信客户管理系统实战:标签、分层与自动化流程搭建
java·python·机器人·自动化·企业微信
ch.ju1 小时前
Java程序设计(第3版)第三章——数组的定义方式
java·开发语言
Chloeis Syntax2 小时前
JavaEE学习日记(2)---文件操作和IO
java·笔记·学习·java-ee
无风听海2 小时前
OAuth 2.0 response_type完全指南
java·开发语言·oauth
Cyan_RA92 小时前
SpringMVC 数据格式化处理 详解
java·开发语言·spring·mvc·ssm·springmvc·数据格式化