🎁 Get the FREE AI Skills Starter GuideSubscribe →
BytesAgainBytesAgain
🦀 ClawHub

xfg-ddd-skills

by @fuzhengwei

DDD 六边形架构设计与部署技能包。提供 Domain/Case/Infrastructure 层设计模式与代码模板,以及 Docker 环境部署脚本。当用户询问 DDD 架构、设计模式或需要部署项目时调用。

Versionv2.2.3
Downloads734
Stars4
TERMINAL
clawhub install xfg-ddd-skills

📖 About This Skill


name: "xfg-ddd-skills" description: "DDD 六边形架构设计与部署技能包。提供 Domain/Case/Infrastructure 层设计模式与代码模板,以及 Docker 环境部署脚本。当用户询问 DDD 架构、设计模式或需要部署项目时调用。" version: 2.2.3 author: xiaofuge license: MIT triggers: # DDD 架构设计 - "DDD" - "六边形架构" - "domain-driven design" - "领域驱动设计" - "ports and adapters" - "创建 Entity" - "创建聚合根" - "创建 DDD 项目" - "新建项目" - "Aggregate" - "ValueObject" - "值对象" - "聚合根" # DevOps 部署相关 - "部署" - "deploy" - "Docker" - "docker-compose" - "容器化" - "发布" - "上线" - "打包" - "build" - "运维部署" - "启动服务" - "停止服务" - "重启服务" - "环境配置" - "生产环境" - "测试环境" - "数据库部署" - "MySQL" - "Redis" - "RabbitMQ" metadata: openclaw: emoji: "🏗️"

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/

    > 📦 镜像来源:docker-image-pusher > 添加新镜像:在 images.txt 添加镜像名,等待1分钟同步

    常用镜像速查表

    | 原始镜像 | 阿里云加速地址 | 用途 | |---------|--------------|------| | JDK/Java | | | | openjdk:8-jre-slim | registry.cn-hangzhou.aliyuncs.com/xfg-studio/openjdk:8-jre-slim | Java 8 运行环境 | | openjdk:8-jdk | registry.cn-hangzhou.aliyuncs.com/xfg-studio/openjdk:8-jdk | Java 8 开发镜像 | | openjdk:17-jdk-slim | registry.cn-hangzhou.aliyuncs.com/xfg-studio/openjdk:17-jdk-slim | Java 17 运行环境 | | openjdk:17-ea-17-jdk-slim-buster | registry.cn-hangzhou.aliyuncs.com/xfg-studio/openjdk:17-ea-17-jdk-slim-buster | Java 17 EA 版本 | | 数据库 | | | | mysql:8.0.32 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/mysql:8.0.32 | MySQL 8.0 | | mysql:8.4.4 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/mysql:8.4.4 | MySQL 8.4 | | postgres:14.18 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/postgres:14.18 | PostgreSQL 14 | | pgvector/pgvector:pg17 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/pgvector:pg17 | PostgreSQL 向量库 | | 缓存 | | | | redis:6.2 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/redis:6.2 | Redis 6.2 | | redis:7.2 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/redis:7.2 | Redis 7.2 | | redis:7.4.13 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/redis:7.2/7.4.13 | Redis 7.4 | | 数据库管理 | | | | phpmyadmin:5.2.1 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/phpmyadmin:5.2.1 | MySQL Web 管理 | | redis-commander:0.8.0 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/redis-commander:0.8.0 | Redis Web 管理 | | dpage/pgadmin4:9.1.0 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/pgadmin4:9.1.0 | PostgreSQL Web 管理 | | 消息队列 | | | | rabbitmq:3.12.9 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/rabbitmq:3.12.9 | RabbitMQ | | rocketmq:5.1.0 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/rocketmq:5.1.0 | RocketMQ | | kafka:3.7.0 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/kafka:3.7.0 | Kafka | | kafka-eagle:3.0.2 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/kafka-eagle:3.0.2 | Kafka Eagle | | 注册中心/配置中心 | | | | nacos-server:v2.2.3-slim | registry.cn-hangzhou.aliyuncs.com/xfg-studio/nacos-server:v2.2.3-slim | Nacos 2.2.3 | | nacos-server:v3.1.1 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/nacos-server:v3.1.1 | Nacos 3.1.1 | | Web 服务器 | | | | nginx:1.25.1 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/nginx:1.25.1 | Nginx 1.25 | | nginx:1.28.0-alpine | registry.cn-hangzhou.aliyuncs.com/xfg-studio/nginx:1.28.0-alpine | Nginx 1.28 Alpine | | 任务调度 | | | | xxl-job-admin:2.4.0 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/xxl-job-admin:2.4.0 | XXL-Job 管理端 | | xxl-job-aarch64:2.4.0 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/xxl-job-aarch64:2.4.0 | XXL-Job ARM 版本 | | 监控 | | | | prometheus:2.47.2 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/prometheus:2.47.2 | Prometheus | | grafana:10.2.0 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/grafana:10.2.0 | Grafana | | skywalking-oap-server:9.3.0 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/skywalking-oap-server:9.3.0 | SkyWalking OAP | | skywalking-ui:9.3.0 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/skywalking-ui:9.3.0 | SkyWalking UI | | 搜索引擎 | | | | elasticsearch:7.17.14 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/elasticsearch:7.17.14 | Elasticsearch | | kibana:7.17.14 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/kibana:7.17.14 | Kibana | | Node | | | | node:18-alpine | registry.cn-hangzhou.aliyuncs.com/xfg-studio/node:18-alpine | Node 18 | | node:20-alpine | registry.cn-hangzhou.aliyuncs.com/xfg-studio/node:20-alpine | Node 20 | | AI/工具 | | | | ollama/ollama:0.5.10 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/ollama:0.5.10 | Ollama | | n8nio/n8n:1.88.0 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/n8n:1.88.0 | N8N 工作流 | | 其他 | | | | alpine:3.20.1 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/alpine:3.20.1 | Alpine Linux | | portainer:latest | registry.cn-hangzhou.aliyuncs.com/xfg-studio/portainer:latest | Docker 可视化管理 | | jenkins:2.439 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/jenkins:2.439 | Jenkins | | sentinel-dashboard:1.8.7 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/sentinel-dashboard:1.8.7 | Sentinel 流量控制 | | canal-server:v1.1.6 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/canal-server:v1.1.6 | Canal | | zookeeper:3.9.0 | registry.cn-hangzhou.aliyuncs.com/xfg-studio/zookeeper:3.9.0 | Zookeeper |

    拉取镜像示例

    # 拉取 MySQL
    docker pull registry.cn-hangzhou.aliyuncs.com/xfg-studio/mysql:8.0.32

    拉取 Redis

    docker pull registry.cn-hangzhou.aliyuncs.com/xfg-studio/redis:6.2

    拉取 Java 17

    docker pull registry.cn-hangzhou.aliyuncs.com/xfg-studio/openjdk:17-jdk-slim


    ⚠️ 常见问题处理

    1. MySQL 8.0 认证问题

    docker exec mysql mysql -uroot -p123456 -e "ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456'; FLUSH PRIVILEGES;"
    

    2. 容器网络不通

    确保所有容器在同一个网络:
    networks:
      - my-network
    

    3. 端口冲突

    修改 docker-compose.yml 中的端口映射:
    ports:
      - "13306:3306"  # 改为非标准端口
    

    4. 应用无法连接数据库

    检查环境变量配置和健康检查依赖:
    depends_on:
      mysql:
        condition: service_healthy
    


    📝 部署操作流程示例

    当用户说"帮我部署 ai-mcp-gateway"时,执行:

    1. 确认项目信息 - 项目路径:/Users/fuzhengwei/Documents/project/ddd-demo/ai-mcp-gateway - 端口:8091 - 镜像:system/ai-mcp-gateway:1.0.0

    2. 执行部署

    # 进入项目目录
    cd /Users/fuzhengwei/Documents/project/ddd-demo/ai-mcp-gateway

    打包

    mvn clean package -Dmaven.test.skip=true

    构建 Docker 镜像

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

    部署基础环境

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

    等待 MySQL 就绪

    sleep 30

    初始化数据库

    docker exec -i mysql mysql -uroot -p123456 < mysql/sql/ai_mcp_gateway_v2.sql

    启动应用

    docker-compose -f docker-compose-app.yml up -d

    验证

    docker ps | grep ai-mcp-gateway curl http://localhost:8091/api/gateway/list

    3. 部署完成检查 - 容器状态正常 - 日志无报错 - 健康检查通过