Cursor AI 编程实战(篇三):Domain、Infrastructure 与策略模式
系列 :共三篇。本篇为 附录 F 整段并入:Domain 层、Infrastructure 层、策略模式三份
.mdc完整正文(已脱敏),适合作为项目规则库主体单独收藏。
上一篇 :篇二:Rules 与 Adapter/App
合并完整版 :Cursor-AI编程最佳实践-CSDN发表版.md
系列说明:本文为《Cursor AI 编程实战》连载之一,脱敏整理自团队《最佳实战》。三篇可独立阅读,也可按篇一 → 篇二 → 篇三顺序配合使用。
附录 F:Domain / Infrastructure / 策略模式 .mdc 全文(脱敏)
以下为《最佳实战》原文三份规则的正文级复制,可按需拆成 .cursor/rules/domain.mdc、infrastructure.mdc、strategy-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 {匹配条件};
}
}
```