xfg-ddd-skills

Polar Sponsor
爱发电 赞助
.NET 9.0

DDD 六边形架构设计与部署技能包:提供 Domain/Case/Infrastructure 层的设计模式、代码模板及 Docker 部署脚本,用于解答 DDD 架构、设计模式及项目部署需求。

DDD 六边形建筑

功能概述

DDD 六边形建筑是一项面向实际任务的技能,主要用于用六边形建筑设计和实施域设计软件;此技能为主干线建筑提供了模式、模板和最佳做法。

核心要点

  • 它将相关步骤、工具调用和结果整理方式集中到统一流程中,帮助使用者更快完成目标并减少重复操作。
  • 使用时应结合输入条件选择合适的执行方式,核对必要参数、依赖环境与输出内容,并按原始要求处理异常情况。
  • 该技能适合需要稳定复用相关能力的场景,可作为自动化工作流的一部分,也便于后续检查、调整和扩展。

使用与执行

从功能定位来看,该技能强调把分散的操作要求整理成清晰、可复用的处理流程,使用户能够围绕既定目标快速准备输入、选择执行方式并获得结构化结果。实际使用前应先确认任务范围、数据来源、运行环境、必要权限和关键参数,再依据技能说明逐步执行;

结果检查与注意事项

若输入条件不完整,应先补齐信息或采用保守配置,避免因错误假设导致结果偏离需求。执行过程中需要关注工具调用是否成功、接口或依赖是否可用、输出格式是否符合预期,并对异常提示、缺失字段和边界情况进行处理;涉及批量任务时,还应保存进度,避免中断后重复操作。

DDD Hexagonal Architecture

Design and implement software using Domain-Driven Design with Hexagonal Architecture. This skill provides patterns, templates, and best practices for building maintainable domain-centric applications.

Scripts

创建 DDD 项目

当用户说"创建 DDD 项目"、"新建项目"、"创建项目"、"创建ddd项目"时,必须使用 scripts/create-ddd-project.sh 脚本。

脚本支持系统: Windows (Git Bash/MSYS2)、Mac (macOS)、Linux,自动检测并适配。

⚠️ 环境提醒: 建议提前安装 JDK 17+ 和 Maven 3.8.*,脚本启动时会自动检测并给出各平台安装指引,未安装也可继续但可能导致生成失败。

⚠️ 重要提醒:必须询问用户项目创建地址

在创建项目前,如果用户没有明确给出工程创建地址,必须询问用户在哪里创建项目。 不能随意创建到默认目录,必须获得用户确认。

示例对话:

用户:帮我创建一个 DDD 项目
AI:好的,我来帮您创建 DDD 项目。请问您希望将项目创建在哪个目录?
     例如:
     1) /Users/xxx/projects
     2) /Users/xxx/Documents
     3) /home/xxx/workspace
     4) 其他路径(请直接输入)

用户:创建在 /Users/xxx/projects 下
AI:确认在 /Users/xxx/projects 下创建项目,开始执行...

流程:

  1. 第一步:确认项目创建目录

    必须询问用户,如果用户未指定,列出可选项供用户选择。

    示例:

    📂 选择项目生成目录
    ──────────────────────────────
    1) /Users/xxx/projects
    2) /Users/xxx/Documents
    3) /home/xxx/workspace
    4) 自定义路径(直接输入路径)
    
    直接回车 = 选择 [1]
    
  2. 第二步:填写项目配置(逐一询问,直接回车使用默认值)

    参数 说明 默认值 示例
    GroupId Maven 坐标 groupId,标识组织或公司 com.yourcompany cn.bugstack
    ArtifactId 项目模块唯一标识名称 your-project-name order-system
    Version 项目版本号 1.0.0-SNAPSHOT 1.0.0-RELEASE
    Package Java 代码根包名 自动从 GroupId + ArtifactId 推导 cn.bugstack.order
    Archetype 版本 脚手架模板版本 1.8 -
  3. 第三步:确认并生成

    显示所有配置,确认后执行 Maven Archetype 生成项目。

脚本执行方式(在 xfg-ddd-skills 项目根目录下运行):

bash scripts/create-ddd-project.sh

⚠️ 必须先 cd 到 xfg-ddd-skills 项目目录下再执行,脚本会自动定位自身路径。 AI 负责引导用户选择目录、填写参数,无需手动拼凑 Maven 命令。 ⚠️ 再次强调:创建项目前必须询问用户项目创建地址,不能随意创建!

Quick Reference

Task Reference
Architecture overview references/architecture.md
Entity design references/entity.md
Aggregate design references/aggregate.md
Value Object design references/value-object.md
Repository pattern references/repository.md
Port & Adapter references/port-adapter.md
Domain Service references/domain-service.md
Case layer orchestration references/case-layer.md
Trigger layer references/trigger-layer.md
Infrastructure layer references/infrastructure-layer.md
Domain 层设计指南(避免常见错误) references/domain-design-guide.md
Domain 层核心模式 references/domain-patterns.md
Infrastructure 层核心模式 references/infrastructure-patterns.md
DevOps 部署 references/devops-deployment.md
Project structure references/project-structure.md
Naming conventions references/naming.md
Docker Images references/docker-images.md

Architecture Layers

┌─────────────────────────────────────────────────────────────┐
│                     Trigger Layer                            │
│         (HTTP Controller / MQ Listener / Job)               │
└─────────────────────────┬───────────────────────────────────┘
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                       API Layer                              │
│              (DTO / Request / Response)                     │
└─────────────────────────┬───────────────────────────────────┘
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                      Case Layer                              │
│            (Orchestration / Business Flow)                   │
└─────────────────────────┬───────────────────────────────────┘
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                     Domain Layer                             │
│        (Entity / Aggregate / VO / Domain Service)           │
└─────────────────────────┬───────────────────────────────────┘
                          ▲
┌─────────────────────────────────────────────────────────────┐
│                  Infrastructure Layer                        │
│      (Repository Impl / Port Adapter / DAO / PO)            │
└─────────────────────────────────────────────────────────────┘

Dependency Rule: Trigger → API → Case → Domain ← Infrastructure

⚠️ Domain 层设计自检清单

在生成 Domain 层代码前,必须逐项检查:

1. 是否有多种处理方式(if-else 判断类型)? → 是:使用策略模式(IXxxStrategy 接口 + 实现类 + Map 注入)

2. 是否有多个独立的校验/过滤步骤(3步以上)? → 是:使用责任链模式(IXxxFilter 接口 + Factory 组装链)

3. Service 方法是否超过 60 行? → 是:拆分为过滤器(校验)+ 策略(执行)+ 私有方法(保存)

4. Infrastructure 层是否包含业务判断逻辑? → 是:将业务校验移到 Domain 层的过滤器中,Infrastructure 只做数据读写

5. 是否跨域直接依赖另一个 Domain 的 Repository? → 是:通过 Case 层编排,或在本域 Repository 接口中聚合所需数据

6. Infrastructure 包名是否正确? → Repository 实现:adapter/repository/(❌ 不是 persistent/repository/) → DAO 操作:dao/(❌ 不是 scenario/dao/ 或其他包) → Redis 操作:redis/(❌ 不是 config/)

Domain Layer 目录结构

model/
├── aggregate/              # 聚合对象
│   └── XxxAggregate.java
├── entity/               # 实体对象
│   ├── XxxEntity.java          # 普通实体
│   └── XxxCommandEntity.java  # 命令实体
└── valobj/               # 值对象
    ├── XxxVO.java             # 普通值对象
    └── XxxEnumVO.java         # 枚举值对象

⚠️ 注意:model/ 下没有单独的 command/ 包,命令实体统一放在 entity/ 包下。

🔄 新功能开发完整流程

当用户需要实现一个新功能时,必须按照以下分层调用流程进行开发:

调用链路图

┌─────────────────────────────────────────────────────────────────────────┐
│                           新功能开发流程                                 │
└─────────────────────────────────────────────────────────────────────────┘

  外部请求
      │
      ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 1. Trigger 层(触发层)                                                  │
│    职责:接收外部请求,路由转发,参数校验,不含业务逻辑                     │
│                                                                         │
│    • HTTP Controller  →  接收 HTTP 请求                                  │
│    • MQ Listener      →  监听消息队列                                    │
│    • Job/Task         →  定时任务/异步任务                               │
│                                                                         │
│    输出:调用 Case 层接口,或轻量场景直接调用 Domain 层                    │
└─────────────────────────┬───────────────────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 2. Case 层(编排层)- 可选,复杂业务需要                                  │
│    职责:跨领域业务编排,流程串联,事务管理                                │
│                                                                         │
│    • 接收 Trigger 调用                                                   │
│    • 编排多个 Domain Service 调用顺序                                     │
│    • 处理跨领域数据转换                                                   │
│    • 管理分布式事务(如需要)                                             │
│                                                                         │
│    输出:调用 Domain 层 Service 接口                                      │
└─────────────────────────┬───────────────────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 3. Domain 层(领域层)                                                   │
│    职责:核心业务逻辑,业务规则校验,领域模型操作                           │
│                                                                         │
│    • Service 服务实现业务逻辑                                             │
│    • Entity/Aggregate 封装业务行为                                       │
│    • 通过 Adapter 接口(Port/Repository)与外部交互                        │
│                                                                         │
│    注意:Domain 层不直接依赖 Infrastructure,只依赖接口                    │
│                                                                         │
│    输出:调用 Adapter 接口(定义在 domain/adapter/ 下)                    │
└─────────────────────────┬───────────────────────────────────────────────┘
                          │ 依赖倒置:Domain 定义接口,Infrastructure 实现
                          ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 4. Infrastructure 层(基础设施层)                                        │
│    职责:技术实现,数据持久化,外部服务调用                                │
│                                                                         │
│    • adapter/repository/  →  实现 Repository 接口,操作数据库              │
│    • adapter/port/        →  实现 Port 接口,调用外部 HTTP/RPC 服务        │
│    • dao/                 →  MyBatis DAO 接口和 PO 对象                   │
│    • gateway/             →  HTTP/RPC 客户端,远程服务调用                 │
│    • redis/               →  Redis 操作                                  │
│                                                                         │
│    输出:返回数据给 Domain 层,或执行外部调用                              │
└─────────────────────────────────────────────────────────────────────────┘

分层职责速查表

层级 职责 禁止做的事 依赖方向
Trigger 接收请求、参数校验、路由转发 业务逻辑、直接操作数据库 → API/Case/Domain
Case 跨域编排、流程串联、事务管理 直接操作数据库、外部 HTTP 调用 → Domain
Domain 业务规则、领域模型、逻辑编排 直接依赖 MyBatis/Redis/HTTP → 只依赖接口
Infrastructure 数据持久化、外部调用、技术实现 业务判断、业务规则 实现 Domain 接口

开发流程检查清单

当用户说"帮我实现一个 XXX 功能"时,按以下顺序检查:

Step 1: 确定入口方式(Trigger)

询问用户或根据需求判断:

□ HTTP API 接口?  →  创建 Controller
□ MQ 消息监听?    →  创建 MQ Listener
□ 定时任务?       →  创建 Job
□ 异步任务?       →  创建 Task/Worker

Step 2: 判断是否需要 Case 层

□ 涉及多个领域协作?     →  需要 Case 层
□ 业务流程超过 3 步?    →  需要 Case 层
□ 需要分布式事务?       →  需要 Case 层
□ 单领域、简单业务?     →  Trigger 直接调用 Domain

Step 3: Domain 层设计

□ 定义 Entity/Aggregate/VO
□ 定义 Service 接口和实现
□ 定义 Repository 接口(数据访问)
□ 定义 Port 接口(外部调用,如需要)

Step 4: Infrastructure 层实现

□ 实现 Repository 接口(adapter/repository/)
□ 创建 DAO 接口和 PO 对象(dao/)
□ 实现 Port 接口(adapter/port/,如需要)
□ 创建 Gateway 客户端(gateway/,如需要)
□ 配置 Redis 操作(redis/,如需要)

代码示例:完整调用链

以"订单支付"功能为例,展示完整分层调用:

1. Trigger 层(HTTP Controller)

@RestController
@RequestMapping("/api/order")
public class OrderController {
    
    @Resource
    private IOrderPayCase orderPayCase;  // 复杂业务,调用 Case 层
    
    @PostMapping("/pay")
    public Response pay(@RequestBody OrderPayRequest request) {
        // 1. 参数校验
        if (request.getOrderId() == null || request.getPayAmount() == null) {
            return Response.fail("参数不完整");
        }
        
        // 2. 调用 Case 层(复杂业务)
        // 如果是简单业务,可直接调用 Domain Service
        try {
            OrderPayResult result = orderPayCase.execute(request);
            return Response.success(convertToResponse(result));
        } catch (Exception e) {
            return Response.fail(e.getMessage());
        }
    }
}

2. Case 层(业务编排)

public interface IOrderPayCase {
    OrderPayResult execute(OrderPayRequest request) throws Exception;
}

@Service
public class OrderPayCaseImpl implements IOrderPayCase {
    
    @Resource
    private IOrderService orderService;      // 订单领域服务
    @Resource
    private IPaymentService paymentService;  // 支付领域服务
    @Resource
    private IInventoryService inventoryService; // 库存领域服务
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderPayResult execute(OrderPayRequest request) throws Exception {
        log.info("执行订单支付流程,订单号:{}", request.getOrderId());
        
        // 1. 查询订单
        OrderEntity order = orderService.queryOrder(request.getOrderId());
        
        // 2. 扣减库存(调用库存领域服务)
        inventoryService.deduct(order.getProductId(), order.getQuantity());
        
        // 3. 执行支付(调用支付领域服务)
        PaymentResult payment = paymentService.pay(order, request.getPayAmount());
        
        // 4. 更新订单状态(调用订单领域服务)
        orderService.markPaid(order.getOrderId(), payment.getTransactionId());
        
        // 5. 返回结果
        return OrderPayResult.builder()
            .orderId(order.getOrderId())
            .status("PAID")
            .transactionId(payment.getTransactionId())
            .build();
    }
}

3. Domain 层(领域服务)

// 订单领域服务接口
public interface IOrderService {
    OrderEntity queryOrder(String orderId);
    void markPaid(String orderId, String transactionId);
}

// 订单领域服务实现
@Service
public class OrderServiceImpl implements IOrderService {
    
    @Resource
    private IOrderRepository orderRepository;  // 依赖 Repository 接口,非实现
    
    @Override
    public OrderEntity queryOrder(String orderId) {
        return orderRepository.queryById(orderId);
    }
    
    @Override
    public void markPaid(String orderId, String transactionId) {
        OrderEntity order = orderRepository.queryById(orderId);
        // 业务规则校验
        if (order.getStatus() != OrderStatus.PENDING_PAY) {
            throw new BusinessException("订单状态不正确,无法支付");
        }
        // 执行业务逻辑
        order.pay(transactionId);  // Entity 封装业务行为
        orderRepository.save(order);
    }
}

// 支付领域服务
public interface IPaymentService {
    PaymentResult pay(OrderEntity order, BigDecimal amount);
}

@Service
public class PaymentServiceImpl implements IPaymentService {
    
    @Resource
    private IPaymentPort paymentPort;  // 依赖 Port 接口,调用外部支付网关
    
    @Override
    public PaymentResult pay(OrderEntity order, BigDecimal amount) {
        // 构建支付请求
        PaymentRequest request = PaymentRequest.builder()
            .orderId(order.getOrderId())
            .amount(amount)
            .build();
        
        // 调用外部支付服务(通过 Port 接口)
        return paymentPort.executePayment(request);
    }
}

4. Infrastructure 层(技术实现)

// Repository 实现 - 订单数据访问
@Repository
public class OrderRepositoryImpl implements IOrderRepository {
    
    @Resource
    private IOrderDao orderDao;  // MyBatis DAO
    @Resource
    private StringRedisTemplate redisTemplate;
    
    @Override
    public OrderEntity queryById(String orderId) {
        // 先查缓存
        String cacheKey = "order:" + orderId;
        String cached = redisTemplate.opsForValue().get(cacheKey);
        if (cached != null) {
            return JSON.parseObject(cached, OrderEntity.class);
        }
        
        // 再查数据库
        OrderPO po = orderDao.queryById(orderId);
        OrderEntity entity = convertToEntity(po);
        
        // 写入缓存
        redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(entity), 30, TimeUnit.MINUTES);
        
        return entity;
    }
    
    @Override
    public void save(OrderEntity entity) {
        OrderPO po = convertToPO(entity);
        orderDao.update(po);
        
        // 更新缓存
        redisTemplate.opsForValue().set("order:" + entity.getOrderId(), 
            JSON.toJSONString(entity), 30, TimeUnit.MINUTES);
    }
}

// Port 实现 - 外部支付网关调用
@Component
public class PaymentPortImpl implements IPaymentPort {
    
    @Resource
    private PaymentGateway paymentGateway;  // HTTP 客户端
    
    @Override
    public PaymentResult executePayment(PaymentRequest request) {
        // 调用外部支付服务
        PaymentGatewayRequest gatewayRequest = convertToGatewayRequest(request);
        PaymentGatewayResponse response = paymentGateway.pay(gatewayRequest);
        
        if (!response.isSuccess()) {
            throw new PaymentException("支付失败:" + response.getErrorMsg());
        }
        
        return PaymentResult.builder()
            .transactionId(response.getTransactionId())
            .status("SUCCESS")
            .build();
    }
}

常见问题与纠正

❌ 错误1:Trigger 层直接调用 Repository

// 错误示例
@RestController
public class OrderController {
    @Resource
    private IOrderRepository orderRepository;  // ❌ 直接依赖 Repository
    
    @PostMapping("/order")
    public Response create(@RequestBody OrderRequest request) {
        // 业务逻辑散落在 Controller
        OrderEntity order = new OrderEntity();
        order.setStatus("CREATED");
        orderRepository.save(order);  // ❌ 直接操作数据库
        return Response.success();
    }
}

纠正:Trigger 层只负责接收请求和路由,业务逻辑应下沉到 Domain 层。

❌ 错误2:Domain Service 直接依赖 DAO

// 错误示例
@Service
public class OrderServiceImpl implements IOrderService {
    @Resource
    private IOrderDao orderDao;  // ❌ 直接依赖 DAO,违反分层
    
    public OrderEntity queryOrder(String orderId) {
        OrderPO po = orderDao.queryById(orderId);  // ❌ 直接操作数据库
        return convert(po);
    }
}

纠正:Domain 层应依赖 Repository 接口,由 Infrastructure 层实现。

❌ 错误3:Infrastructure 层包含业务逻辑

// 错误示例
@Repository
public class OrderRepositoryImpl implements IOrderRepository {
    
    public void save(OrderEntity entity) {
        // ❌ 在 Repository 中做业务判断
        if (entity.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new BusinessException("金额必须大于0");  // 业务异常不应在这里抛
        }
        orderDao.update(convertToPO(entity));
    }
}

纠正:业务判断应在 Domain 层完成,Infrastructure 层只负责数据读写。

❌ 错误4:跨域直接调用 Repository

// 错误示例
@Service
public class OrderServiceImpl implements IOrderService {
    @Resource
    private IInventoryRepository inventoryRepository;  // ❌ 跨域依赖 Repository
    
    public void createOrder(OrderEntity order) {
        // 直接操作库存领域的数据
        InventoryPO inventory = inventoryRepository.queryByProductId(order.getProductId());
        // ...
    }
}

纠正:跨域操作应通过 Case 层编排,或调用目标领域的 Service 接口。

总结口诀

Trigger 只路由,Case 做编排
Domain 管业务,Infra 做实现
接口定义在 Domain,实现放在 Infra 层
依赖永远向内指,Domain 是核心

📦 新功能开发规范

当用户需要增加新功能时,按照以下决策流程进行开发:

决策流程图

用户需要新功能
        │
        ▼
┌───────────────────┐
│ 检查现有领域服务   │ ──是──→ 扩展现有 Service
│ domain/xxx/service│
│ 是否有支持?      │
└─────────┬─────────┘
          │否
          ▼
┌───────────────────┐
│ 创建新的领域?     │ ──是──→ 创建新领域(完整结构)
│ domain/xxx/       │
│                   │
└─────────┬─────────┘
          │
          ▼
┌───────────────────┐
│ Case 层是否需要?  │ ──是──→ 创建 Case 层编排
│ 业务复杂?多领域?  │
└─────────┬─────────┘
          │否(轻量工程)
          ▼
┌───────────────────┐
│ Trigger 直接调用   │ ←── Trigger → Domain
│ Domain 领域层     │
└───────────────────┘

决策指南

问题 答案 处理方式
现有领域能否支持新功能? ✅ 是 在现有 Service 中添加方法
是否需要跨多个领域? ✅ 是 创建 Case 层编排
业务逻辑是否复杂? ✅ 是 创建 Case 层编排
是否是轻量工程? ✅ 是 Trigger 直接调用 Domain
Trigger 是否越来越复杂? ✅ 是 询问用户是否创建 Case 层

场景一:扩展现有领域服务

判断条件:新功能属于现有领域的业务范围。

开发步骤:

  1. 检查现有领域服务

    • 查看 domain/{domain}/service/ 目录
    • 确认是否有相关的 Service 接口
  2. 扩展现有 Service

    • 在现有接口中添加新方法
    • 在实现类中实现新方法

示例:在交易域添加一个"查询订单列表"功能

// 1. 在现有接口中添加方法
public interface ITradeRepository {
    // 现有方法...
    
    // 新增:查询订单列表
    List queryOrderList(QueryOrderRequest request);
}

// 2. 在实现类中实现
@Repository
public class TradeRepository implements ITradeRepository {
    
    @Resource
    private IMcpGatewayDao mcpGatewayDao;
    
    @Override
    public List queryOrderList(QueryOrderRequest request) {
        // 实现查询逻辑
    }
}

场景二:创建新的领域

判断条件:新功能涉及全新的业务领域,与现有领域无关。

开发步骤:

  1. 创建完整的领域结构

    domain/
    └── {new-domain}/                    # 新领域
        ├── adapter/                    # 适配器接口
        │   ├── port/                  # 端口接口
        │   │   └── I{Xxx}Port.java
        │   └── repository/            # 仓储接口
        │       └── I{Xxx}Repository.java
        ├── model/                     # 领域模型
        │   ├── aggregate/            # 聚合根
        │   ├── entity/               # 实体
        │   └── valobj/               # 值对象
        └── service/                   # 领域服务
            ├── I{Xxx}Service.java     # 服务接口
            └── {能力}/
                └── {Xxx}ServiceImpl.java
    
  2. 定义 Adapter 接口

    // Repository 接口
    public interface I{Xxx}Repository {
        XxxEntity queryById(Long id);
        void save(XxxEntity entity);
    }
    
    // Port 接口
    public interface I{Xxx}Port {
        void notify(XxxEntity entity) throws Exception;
    }
    
  3. 定义 Model

    // 实体
    @Data
    public class XxxEntity {
        private Long id;
        private String name;
    }
    
    // 值对象
    @Getter
    public enum XxxStatusEnumVO {
        CREATED("created", "已创建"),
        PROCESSING("processing", "处理中"),
        COMPLETED("completed", "已完成");
        private String code;
        private String info;
    }
    
  4. 实现 Service

    public interface I{Xxx}Service {
        void process(XxxEntity entity) throws Exception;
    }
    
    @Slf4j
    @Service
    public class XxxServiceImpl implements I{Xxx}Service {
        
        @Resource
        private I{Xxx}Repository repository;
        
        @Resource
        private I{Xxx}Port port;
        
        @Override
        public void process(XxxEntity entity) throws Exception {
            log.info("处理业务:{}", entity.getId());
            // 业务逻辑
            repository.save(entity);
            port.notify(entity);
        }
    }
    

场景三:创建 Case 层

判断条件:业务涉及多个领域协作,或需要编排多个领域服务。

开发步骤:

  1. 创建 Case 模块结构

    case/
    └── {domain}/
        └── {capability}/
            ├── I{Xxx}Case.java           # Case 接口
            └── impl/
                └── {Xxx}CaseImpl.java    # Case 实现
    
  2. 定义 Case 接口

    /**
     * XXX 业务编排接口
     * 
     * 职责:编排多个领域服务,完成复杂业务场景
     */
    public interface I{Xxx}Case {
        
        /**
         * 执行 XXX 业务
         */
        void execute(XxxRequest request) throws Exception;
    }
    
  3. 实现 Case 编排

    /**
     * XXX 业务编排实现
     * 
     * @author xiaofuge
     */
    @Slf4j
    @Service
    public class XxxCaseImpl implements I{Xxx}Case {
        
        @Resource
        private IDomain1Service domain1Service;
        
        @Resource
        private IDomain2Service domain2Service;
        
        @Override
        public void execute(XxxRequest request) throws Exception {
            log.info("执行 XXX 业务");
            
            // 1. 调用领域服务1
            Domain1Result r1 = domain1Service.method1(request.getParam1());
            
            // 2. 调用领域服务2
            domain2Service.method2(r1.getData());
            
            // 3. 组装结果
            // ...
        }
    }
    

Case 层命名规范:

  • 接口命名:I{Xxx}Case
  • 实现类命名:{Xxx}CaseImpl

场景四:Trigger 直接调用 Domain

判断条件:轻量工程,业务简单,不需要 Case 层编排。

开发步骤:

  1. 在 Trigger 层直接调用 Domain

    @RestController
    @RequestMapping("/api/xxx")
    public class XxxController {
        
        @Resource
        private I{Xxx}Service xxxService;
        
        @PostMapping("/process")
        public Response process(@RequestBody XxxRequest request) {
            try {
                xxxService.process(request.toEntity());
                return Response.success();
            } catch (Exception e) {
                return Response.fail(e.getMessage());
            }
        }
    }
    
  2. Trigger 层职责

    • 接收请求参数
    • 参数校验
    • 调用 Domain 层
    • 处理异常
    • 返回响应

场景五:Trigger 复杂化后重构为 Case 层

判断条件:Trigger 层代码越来越复杂,包含大量业务逻辑。

警告信号:

  • Controller 代码超过 100 行
  • Controller 中有大量 if-else 判断
  • Controller 依赖多个 Domain Service
  • 业务逻辑难以测试

重构步骤:

  1. 询问用户

    AI:检测到 Trigger 层代码比较复杂,是否需要创建 Case 层来分摊业务逻辑?
        这样可以:
        1. 将业务逻辑从 Controller 移到 Case 层
        2. 提高代码可测试性
        3. 更好的职责分离
    
  2. 创建 Case 层

    • 按照场景三的方式创建 Case 模块
    • 将 Controller 中的业务逻辑移到 Case 层
  3. 简化 Trigger 层

    // 重构前
    @RestController
    public class XxxController {
        @Resource private IDomain1Service d1;
        @Resource private IDomain2Service d2;
        @Resource private IDomain3Service d3;
        
        public Response process(Request req) {
            // 100+ 行业务逻辑...
        }
    }
    
    // 重构后
    @RestController
    public class XxxController {
        @Resource private I{Xxx}Case xxxCase;
        
        public Response process(Request req) {
            xxxCase.execute(req);
            return Response.success();
        }
    }
    

完整开发流程示例

需求:在拼团系统中添加"订单超时取消"功能

步骤 1:检查现有领域

trade/
├── adapter/repository/ITradeRepository.java  ← 可以复用
├── model/entity/TradeLockRuleCommandEntity.java
└── service/
    ├── lock/TradeLockOrderService.java     ← 部分相关
    └── refund/TradeRefundOrderService.java  ← 退单逻辑可参考

步骤 2:决策

  • 现有领域(trade)已有相关服务
  • 需要扩展现有 Service
  • 不需要创建新领域

步骤 3:实现

// 1. 扩展 ITradeRepository
public interface ITradeRepository {
    // 现有方法...
    
    // 新增:查询超时未支付订单
    List queryTimeoutUnpaidOrders();
    
    // 新增:取消订单
    void cancelOrder(String orderId);
}

// 2. 扩展 TradeLockOrderService
public interface ITradeLockOrderService {
    // 现有方法...
    
    // 新增:处理超时订单
    void handleTimeoutOrders();
}

@Slf4j
@Service
public class TradeLockOrderService implements ITradeLockOrderService {
    
    @Resource
    private ITradeRepository repository;
    
    @Override
    public void handleTimeoutOrders() {
        log.info("处理超时未支付订单");
        
        // 1. 查询超时订单
        List orders = repository.queryTimeoutUnpaidOrders();
        
        // 2. 遍历取消
        for (MarketPayOrderEntity order : orders) {
            repository.cancelOrder(order.getOrderId());
        }
    }
}

步骤 4:添加 Trigger

@Component
public class OrderTimeoutJob {
    
    @Resource
    private ITradeLockOrderService tradeLockOrderService;
    
    @Scheduled(cron = "0 */5 * * * ?")  // 每5分钟执行
    public void execute() {
        try {
            tradeLockOrderService.handleTimeoutOrders();
        } catch (Exception e) {
            log.error("订单超时处理异常", e);
        }
    }
}

规范速查表

场景 判断条件 实现位置
扩展现有服务 功能属于现有领域 domain/{domain}/service/
创建新领域 全新业务领域 创建完整的 domain/{new}/ 结构
创建 Case 层 多领域协作、复杂业务 case/{domain}/{capability}/
Trigger 调用 轻量工程、简单业务 trigger/{domain}/controller/
重构为 Case Trigger 越来越复杂 询问用户后重构

核心原则:

  1. 优先复用:先检查现有领域是否能支持
  2. 单一职责:新功能归到对应领域,不随意扩张
  3. 适度分层:简单场景直接调用,复杂场景创建 Case
  4. 持续演进:Trigger 复杂化时,询问用户是否重构

Quick Templates

Aggregate 聚合对象

@Data @Builder @AllArgsConstructor @NoArgsConstructor
public class GroupBuyOrderAggregate {
    /** 用户实体对象 */
    private UserEntity userEntity;
    /** 支付活动实体对象 */
    private PayActivityEntity payActivityEntity;
    /** 支付优惠实体对象 */
    private PayDiscountEntity payDiscountEntity;
    /** 已参与拼团量 */
    private Integer userTakeOrderCount;
}

Entity 普通实体

@Data @Builder @AllArgsConstructor @NoArgsConstructor
public class MarketPayOrderEntity {
    private String teamId;
    private String orderId;
    private BigDecimal originalPrice;
    private BigDecimal deductionPrice;
    private BigDecimal payPrice;
    private TradeOrderStatusEnumVO tradeOrderStatusEnumVO;
}

Entity 命令实体(放在 entity 包)

/** 命令实体放在 entity 包,使用 CommandEntity 后缀 */
@Data @Builder @AllArgsConstructor @NoArgsConstructor
public class TradeLockRuleCommandEntity {
    private String userId;
    private Long activityId;
    private String teamId;
}

Value Object 值对象

@Getter @Builder @AllArgsConstructor @NoArgsConstructor
public class NotifyConfigVO {
    private NotifyTypeEnumVO notifyType;
    private String notifyMQ;
    private String notifyUrl;
}

EnumVO 枚举值对象(可包含策略逻辑)

@Getter @AllArgsConstructor
public enum RefundTypeEnumVO {

    UNPAID_UNLOCK("unpaid_unlock", "Unpaid2RefundStrategy", "未支付,未成团") {
        @Override
        public boolean matches(GroupBuyOrderEnumVO groupBuyOrderEnumVO, TradeOrderStatusEnumVO tradeOrderStatusEnumVO) {
            return GroupBuyOrderEnumVO.PROGRESS.equals(groupBuyOrderEnumVO) 
                && TradeOrderStatusEnumVO.CREATE.equals(tradeOrderStatusEnumVO);
        }
    },
    
    PAID_UNFORMED("paid_unformed", "Paid2RefundStrategy", "已支付,未成团") {
        @Override
        public boolean matches(GroupBuyOrderEnumVO groupBuyOrderEnumVO, TradeOrderStatusEnumVO tradeOrderStatusEnumVO) {
            return GroupBuyOrderEnumVO.PROGRESS.equals(groupBuyOrderEnumVO) 
                && TradeOrderStatusEnumVO.COMPLETE.equals(tradeOrderStatusEnumVO);
        }
    };

    private String code;
    private String strategy;
    private String info;

    public abstract boolean matches(GroupBuyOrderEnumVO groupBuyOrderEnumVO, TradeOrderStatusEnumVO tradeOrderStatusEnumVO);

    public static RefundTypeEnumVO getRefundStrategy(GroupBuyOrderEnumVO g, TradeOrderStatusEnumVO t) {
        return Arrays.stream(values()).filter(v -> v.matches(g, t)).findFirst()
                .orElseThrow(() -> new RuntimeException("不支持的退款状态组合"));
    }
}

Domain Service 完整编码

/** 1. 定义服务接口 */
public interface ITradeLockOrderService {
    MarketPayOrderEntity lockMarketPayOrder(UserEntity user, PayActivityEntity activity, PayDiscountEntity discount) throws Exception;
}

/** 2. 实现服务(放在子包中) */
@Slf4j @Service
public class TradeLockOrderService implements ITradeLockOrderService {

    @Resource private ITradeRepository repository;
    @Resource private BusinessLinkedList tradeRuleFilter;

    @Override
    public MarketPayOrderEntity lockMarketPayOrder(UserEntity userEntity, PayActivityEntity payActivityEntity, PayDiscountEntity payDiscountEntity) throws Exception {
        log.info("锁定营销优惠支付订单:{} activityId:{}", userEntity.getUserId(), payActivityEntity.getActivityId());

        // 1. 交易规则过滤(责任链)
        TradeLockRuleFilterBackEntity back = tradeRuleFilter.apply(TradeLockRuleCommandEntity.builder()
                .activityId(payActivityEntity.getActivityId())
                .userId(userEntity.getUserId())
                .teamId(payActivityEntity.getTeamId()).build(),
                new TradeLockRuleFilterFactory.DynamicContext());

        // 2. 构建聚合对象
        GroupBuyOrderAggregate aggregate = GroupBuyOrderAggregate.builder()
                .userEntity(userEntity)
                .payActivityEntity(payActivityEntity)
                .payDiscountEntity(payDiscountEntity)
                .userTakeOrderCount(back.getUserTakeOrderCount())
                .build();

        // 3. 锁定聚合订单
        return repository.lockMarketPayOrder(aggregate);
    }
}

策略模式实现

/** 1. 策略接口 */
public interface IRefundOrderStrategy {
    void refundOrder(TradeRefundOrderEntity entity) throws Exception;
    void reverseStock(TeamRefundSuccess success) throws Exception;
}

/** 2. 抽象基类(模板方法) */
@Slf4j
public abstract class AbstractRefundOrderStrategy implements IRefundOrderStrategy {
    @Resource protected ITradeRepository repository;
    @Resource protected ThreadPoolExecutor threadPoolExecutor;

    protected void doReverseStock(TeamRefundSuccess s, String type) throws Exception {
        log.info("退单恢复锁单量 - {}", type);
        repository.refund2AddRecovery(s.getActivityId() + ":" + s.getTeamId(), s.getOrderId());
    }
}

/** 3. 具体策略 */
@Slf4j @Service("paid2RefundStrategy")
public class Paid2RefundStrategy extends AbstractRefundOrderStrategy {
    @Override
    public void refundOrder(TradeRefundOrderEntity e) throws Exception {
        log.info("退单-已支付,未成团 userId:{}", e.getUserId());
        NotifyTaskEntity n = repository.paid2Refund(GroupBuyRefundAggregate.buildPaid2RefundAggregate(e, -1, -1));
        if (n != null) threadPoolExecutor.execute(() -> tradeTaskService.execNotifyJob(n));
    }
    @Override
    public void reverseStock(TeamRefundSuccess s) throws Exception {
        doReverseStock(s, "已支付,但有锁单记录,恢复锁单库存");
    }
}

Core Principles

Principle Description
Dependency Inversion Domain defines interfaces, Infrastructure implements
Rich Domain Model Entity contains both data and behavior
Aggregate Boundary Transaction consistency inside, eventual consistency outside
Anti-Corruption Layer Use Port to isolate external systems
Lightweight Trigger Trigger layer only routes requests, no business logic

When to Use DDD

Use DDD when:

  • Complex business domain with rich rules
  • Need to capture domain knowledge in code
  • Long-lived project with evolving requirements
  • Team needs shared domain language

Don't use DDD when:

  • Simple CRUD operations
  • Prototype or throwaway code
  • Domain logic is trivial
  • Team unfamiliar with DDD concepts

Example Projects

  • group-buy-market - E-commerce domain
  • ai-mcp-gateway - API gateway domain

🚀 DevOps 部署完整流程

📋 部署检查清单

当用户需要部署 DDD 项目时,按照以下流程执行:

1. 确认项目信息

  • 项目名称(artifactId)
  • 项目路径(代码根目录)
  • 部署环境(开发/测试/生产)
  • 基础依赖(MySQL/Redis/RabbitMQ)

2. 打包构建

cd /path/to/project
mvn clean package -Dmaven.test.skip=true

3. 基础镜像拉取(加速)

# 使用阿里云加速镜像
docker pull registry.cn-hangzhou.aliyuncs.com/xfg-studio/openjdk:17-jdk-slim
docker pull registry.cn-hangzhou.aliyuncs.com/xfg-studio/mysql:8.0.32
docker pull registry.cn-hangzhou.aliyuncs.com/xfg-studio/redis:6.2

4. 数据库部署

cd docs/dev-ops
docker-compose -f docker-compose-environment-aliyun.yml up -d mysql

# 等待 MySQL 就绪后初始化数据库
docker exec -it mysql mysql -uroot -p123456 -e "source /docker-entrypoint-initdb.d/xxx.sql"

5. 应用容器构建

cd ai-mcp-gateway-app
docker build -t system/{artifactId}:1.0.0 .

6. 应用启动

cd docs/dev-ops
docker-compose -f docker-compose-app.yml up -d

7. 验证部署

# 查看容器状态
docker ps -a | grep {artifactId}

# 查看应用日志
docker logs -f {artifactId}

# 健康检查
curl http://localhost:{port}/actuator/health

📁 标准部署目录结构

{project}/
├── docs/
│   └── dev-ops/
│       ├── docker-compose-environment-aliyun.yml  # 基础环境(MySQL/Redis/RabbitMQ)
│       ├── docker-compose-app.yml                  # 应用服务
│       ├── mysql/
│       │   ├── my.cnf                              # MySQL 配置
│       │   └── sql/
│       │       └── {project}.sql                  # 数据库初始化脚本
│       ├── redis/
│       │   └── redis.conf                          # Redis 配置
│       ├── app/
│       │   ├── start.sh                            # 启动脚本
│       │   └── stop.sh                             # 停止脚本
│       └── README.md                               # 部署说明
├── {project}-app/
│   ├── Dockerfile                                   # 应用 Dockerfile
│   ├── pom.xml
│   └── src/main/resources/
│       ├── application.yml
│       ├── application-dev.yml
│       ├── application-test.yml
│       ├── application-prod.yml
│       └── logback-spring.xml

🐳 Dockerfile 标准模板

# 基础镜像
FROM registry.cn-hangzhou.aliyuncs.com/xfg-studio/openjdk:17-jdk-slim

# 作者
MAINTAINER xiaofuge

# 时区配置
ENV TZ=PRC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# 添加应用 JAR
ADD target/{artifactId}.jar /{artifactId}.jar

# 暴露端口
EXPOSE {port}

# 启动命令
ENTRYPOINT ["sh","-c","java -jar $JAVA_OPTS /{artifactId}.jar $PARAMS"]

📦 docker-compose-app.yml 标准模板

version: '3.8'

services:
  {artifactId}:
    image: system/{artifactId}:1.0.0
    container_name: {artifactId}
    restart: on-failure
    ports:
      - "{port}:{port}"
    environment:
      - TZ=PRC
      - SERVER_PORT={port}
      - SPRING_PROFILES_ACTIVE=prod
    volumes:
      - ./logs:/data/log
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    networks:
      - my-network
    depends_on:
      - mysql
      - redis

networks:
  my-network:
    driver: bridge

🗄️ docker-compose-environment-aliyun.yml 标准模板

version: '3.9'

services:
  # MySQL 8.0
  mysql:
    image: registry.cn-hangzhou.aliyuncs.com/xfg-studio/mysql:8.0.32
    container_name: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      TZ: Asia/Shanghai
      MYSQL_ROOT_PASSWORD: 123456
    ports:
      - "13306:3306"
    volumes:
      - ./mysql/my.cnf:/etc/mysql/conf.d/mysql.cnf:ro
      - ./mysql/sql:/docker-entrypoint-initdb.d
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 5s
      timeout: 10s
      retries: 10
      start_period: 15s
    networks:
      - my-network

  # phpMyAdmin(可选)
  phpmyadmin:
    image: registry.cn-hangzhou.aliyuncs.com/xfg-studio/phpmyadmin:5.2.1
    container_name: phpmyadmin
    ports:
      - "8899:80"
    environment:
      - PMA_HOST=mysql
      - PMA_PORT=3306
      - MYSQL_ROOT_PASSWORD=123456
    depends_on:
      mysql:
        condition: service_healthy
    networks:
      - my-network

  # Redis 6.2
  redis:
    image: registry.cn-hangzhou.aliyuncs.com/xfg-studio/redis:6.2
    container_name: redis
    restart: always
    ports:
      - "16379:6379"
    networks:
      - my-network
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 3

  # Redis Commander(可选)
  redis-admin:
    image: registry.cn-hangzhou.aliyuncs.com/xfg-studio/redis-commander:0.8.0
    container_name: redis-admin
    ports:
      - "8081:8081"
    environment:
      - REDIS_HOSTS=local:redis:6379
      - HTTP_USER=admin
      - HTTP_PASSWORD=admin
    depends_on:
      redis:
        condition: service_healthy
    networks:
      - my-network

networks:
  my-network:
    driver: bridge

🚀 快速启动/停止脚本

start.sh

#!/bin/bash

CONTAINER_NAME={artifactId}
IMAGE_NAME=system/{artifactId}:1.0.0
PORT={port}

echo "容器部署开始 ${CONTAINER_NAME}"

# 停止容器
docker stop ${CONTAINER_NAME}

# 删除容器
docker rm ${CONTAINER_NAME}

# 启动容器
docker run --name ${CONTAINER_NAME} 
  --network my-network 
  -p ${PORT}:${PORT} 
  -e SPRING_PROFILES_ACTIVE=prod 
  -v $(pwd)/logs:/data/log 
  -d ${IMAGE_NAME}

echo "容器部署成功 ${CONTAINER_NAME}"

# 查看日志
docker logs -f ${CONTAINER_NAME}

stop.sh

#!/bin/bash

CONTAINER_NAME={artifactId}

echo "停止容器 ${CONTAINER_NAME}"
docker stop ${CONTAINER_NAME}
docker rm ${CONTAINER_NAME}

echo "容器已停止"

🔧 application-prod.yml 标准配置

server:
  port: {port}

spring:
  application:
    name: {artifactId}
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://${MYSQL_HOST:mysql}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:{database}}?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
    username: ${MYSQL_USER:root}
    password: ${MYSQL_PASSWORD:123456}
    hikari:
      pool-name: {artifactId}-hikari
      minimum-idle: 10
      maximum-pool-size: 50
      idle-timeout: 300000
      connection-timeout: 30000
      max-lifetime: 1800000
  redis:
    host: ${REDIS_HOST:redis}
    port: ${REDIS_PORT:6379}
  rabbitmq:
    host: ${RABBITMQ_HOST:rabbitmq}
    port: ${RABBITMQ_PORT:5672}
    username: ${RABBITMQ_USER:admin}
    password: ${RABBITMQ_PASSWORD:admin123}

logging:
  level:
    root: INFO
    cn.bugstack: INFO
  file:
    name: /data/log/{artifactId}.log

📊 阿里云镜像加速仓库

所有镜像已同步到阿里云,使用前缀 registry.cn-hangzhou.aliyuncs.com/xfg-studio/

相关专题

更多
PixTV官网入口地址合集
PixTV官网入口地址合集

本专题汇总了 PixTV AI 一站式视频创作平台的官方入口与使用教程。无需下载软件,浏览器直接访问即可使用。平台将剧本、图像、视频、声音与剪辑整合在“无限画布”中,接入 GPT Image 2.5、Seedance 2.5 等头部模型。本专题整理了从新建画布、角色锚定、分镜拆分到视频生成与导出的完整操作指南,助你快速上手 AI 短剧与漫剧创作。

2026.10.10

0

15

Kratos框架HTTP与gRPC服务开发教程
Kratos框架HTTP与gRPC服务开发教程

本专题围绕Kratos框架双协议服务开发,涵盖HTTP路由与处理器编写、参数获取、gRPC服务实现与客户端调用、metadata上下文传递、encoding编解码注册、统一响应封装、超时控制与流式响应实现方法。

2026.10.10

0

15

Kratos框架Protobuf接口定义与代码生成合集
Kratos框架Protobuf接口定义与代码生成合集

本专题讲解Kratos框架接口定义体系,涵盖proto编写规范、proto add/client/server生成命令、http注解路由、validate校验、OpenAPI文档生成、跨服务proto复用与兼容性设计。

2026.10.10

0

15

C++虚函数怎么定义和调用
C++虚函数怎么定义和调用

C++虚函数是实现运行时多态的重要机制。本专题从virtual关键字的基本用法入手,介绍基类与派生类之间的函数重写、基类指针调用派生类方法,以及动态绑定的执行过程,帮助初学者掌握虚函数的核心语法。

2026.10.10

0

26

C++类与对象的封装方法教程
C++类与对象的封装方法教程

C++封装是面向对象编程的核心特性之一,通过类将数据与操作数据的函数组织在一起,并利用访问权限控制外部访问。本专题介绍类的定义、成员变量、成员函数以及public、private和protected的使用方法,帮助初学者掌握封装的基本原理。

2026.10.10

0

32

C++构造函数定义与调用方法
C++构造函数定义与调用方法

C++构造函数用于初始化类对象,是面向对象编程的重要基础。本专题从构造函数的定义、声明和调用入手,介绍默认构造函数、带参数构造函数、拷贝构造函数及成员初始化列表,帮助初学者掌握对象创建与初始化的基本方法。

2026.10.10

0

16

Kratos框架零基础入门教程
Kratos框架零基础入门教程

本专题整理Kratos框架入门内容,涵盖Go环境准备、kratos CLI安装升级、new命令创建项目、目录结构分层说明、服务启动与双协议端口、依赖下载报错排查,帮助开发者快速跑通第一个Kratos框架微服务应用。

2026.10.10

0

15

C++条件判断语句怎么写
C++条件判断语句怎么写

C++条件判断是控制程序执行流程的重要基础。本专题介绍if、if-else、else if和switch等常见分支语句,结合条件表达式、比较运算符与代码示例,帮助初学者掌握不同场景下的判断逻辑。

2026.10.10

0

13

C++变量怎么声明和赋值
C++变量怎么声明和赋值

C++变量是编写程序和存储数据的基础。本专题围绕变量声明、定义、初始化、赋值和类型选择等内容展开,帮助初学者理解不同变量的用法,并掌握在实际代码中定义和使用变量的方法。

2026.10.10

0

20

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Codex官方文档
Codex官方文档

共0课时 | 0人学习

QClaw产品手册
QClaw产品手册

共0课时 | 0人学习