xfg-ddd-skills
# 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
bash scripts/create-ddd-project.sh
```
> ⚠️ **必须先 cd 到 `xfg-ddd-skills` 项目目录下再执行**,脚本会自动定位自身路径。
> AI 负责引导用户选择目录、填写参数,无需手动拼凑 Maven 命令。
> **⚠️ 再次强调:创建项目前必须询问用户项目创建地址,不能随意创建!**
---
## Quick Reference
| Task | Reference |
|------|-----------|
| Architecture overview | [references/architecture.md](references/architecture.md) |
| Entity design | [references/entity.md](references/entity.md) |
| Aggregate design | [references/aggregate.md](references/aggregate.md) |
| Value Object design | [references/value-object.md](references/value-object.md) |
| Repository pattern | [references/repository.md](references/repository.md) |
| Port & Adapter | [references/port-adapter.md](references/port-adapter.md) |
| Domain Service | [references/domain-service.md](references/domain-service.md) |
| Case layer orchestration | [references/case-layer.md](references/case-layer.md) |
| Trigger layer | [references/trigger-layer.md](references/trigger-layer.md) |
| Infrastructure layer | [references/infrastructure-layer.md](references/infrastructure-layer.md) |
| **Domain 层设计指南(避免常见错误)** | **[references/domain-design-guide.md](references/domain-design-guide.md)** |
| **Domain 层核心模式** | **[references/domain-patterns.md](references/domain-patterns.md)** |
| **Infrastructure 层核心模式** | **[references/infrastructure-patterns.md](references/infrastructure-patterns.md)** |
| **DevOps 部署** | **[references/devops-deployment.md](references/devops-deployment.md)** |
| Project structure | [references/project-structure.md](references/project-structure.md) |
| Naming conventions | [references/naming.md](references/naming.md) |
| Docker Images | [references/docker-images.md](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<String, IXxxStrategy>` 注入)
**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)
```java
@RestController
@RequestMapping("/api/order")
public class OrderController {
@Resource
private IOrderPayCase orderPayCase; // 复杂业务,调用 Case 层
@PostMapping("/pay")
public Response<OrderPayResponse> 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 层(业务编排)
```java
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 层(领域服务)
```java
// 订单领域服务接口
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 层(技术实现)
```java
// 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
```java
// 错误示例
@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
```java
// 错误示例
@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 层包含业务逻辑
```java
// 错误示例
@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
```java
// 错误示例
@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**
- 在现有接口中添加新方法
- 在实现类中实现新方法
**示例**:在交易域添加一个"查询订单列表"功能
```java
// 1. 在现有接口中添加方法
public interface ITradeRepository {
// 现有方法...
// 新增:查询订单列表
List<MarketPayOrderEntity> queryOrderList(QueryOrderRequest request);
}
// 2. 在实现类中实现
@Repository
public class TradeRepository implements ITradeRepository {
@Resource
private IMcpGatewayDao mcpGatewayDao;
@Override
public List<MarketPayOrderEntity> 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 接口**
```java
// 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**
```java
// 实体
@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**
```java
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 接口**
```java
/**
* XXX 业务编排接口
*
* 职责:编排多个领域服务,完成复杂业务场景
*/
public interface I{Xxx}Case {
/**
* 执行 XXX 业务
*/
void execute(XxxRequest request) throws Exception;
}
```
3. **实现 Case 编排**
```java
/**
* 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**
```java
@RestController
@RequestMapping("/api/xxx")
public class XxxController {
@Resource
private I{Xxx}Service xxxService;
@PostMapping("/process")
public Response<XxxResponse> 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 层**
```java
// 重构前
@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:实现**
```java
// 1. 扩展 ITradeRepository
public interface ITradeRepository {
// 现有方法...
// 新增:查询超时未支付订单
List<MarketPayOrderEntity> 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<MarketPayOrderEntity> orders = repository.queryTimeoutUnpaidOrders();
// 2. 遍历取消
for (MarketPayOrderEntity order : orders) {
repository.cancelOrder(order.getOrderId());
}
}
}
```
**步骤 4:添加 Trigger**
```java
@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 聚合对象
```java
@Data @Builder @AllArgsConstructor @NoArgsConstructor
public class GroupBuyOrderAggregate {
/** 用户实体对象 */
private UserEntity userEntity;
/** 支付活动实体对象 */
private PayActivityEntity payActivityEntity;
/** 支付优惠实体对象 */
private PayDiscountEntity payDiscountEntity;
/** 已参与拼团量 */
private Integer userTakeOrderCount;
}
```
### Entity 普通实体
```java
@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 包)
```java
/** 命令实体放在 entity 包,使用 CommandEntity 后缀 */
@Data @Builder @AllArgsConstructor @NoArgsConstructor
public class TradeLockRuleCommandEntity {
private String userId;
private Long activityId;
private String teamId;
}
```
### Value Object 值对象
```java
@Getter @Builder @AllArgsConstructor @NoArgsConstructor
public class NotifyConfigVO {
private NotifyTypeEnumVO notifyType;
private String notifyMQ;
private String notifyUrl;
}
```
### EnumVO 枚举值对象(可包含策略逻辑)
```java
@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 完整编码
```java
/** 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<TradeLockRuleCommandEntity, TradeLockRuleFilterFactory.DynamicContext, TradeLockRuleFilterBackEntity> 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);
}
}
```
### 策略模式实现
```java
/** 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](file:///Users/fuzhengwei/Documents/project/ddd-demo/group-buy-market) - E-commerce domain
- [ai-mcp-gateway](file:///Users/fuzhengwei/Documents/project/ddd-demo/ai-mcp-gateway) - API gateway domain
---
# 🚀 DevOps 部署完整流程
## 📋 部署检查清单
当用户需要部署 DDD 项目时,按照以下流程执行:
### 1. 确认项目信息
- [ ] 项目名称(artifactId)
- [ ] 项目路径(代码根目录)
- [ ] 部署环境(开发/测试/生产)
- [ ] 基础依赖(MySQL/Redis/RabbitMQ)
### 2. 打包构建
```bash
cd /path/to/project
mvn clean package -Dmaven.test.skip=true
```
### 3. 基础镜像拉取(加速)
```bash
# 使用阿里云加速镜像
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. 数据库部署
```bash
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. 应用容器构建
```bash
cd ai-mcp-gateway-app
docker build -t system/{artifactId}:1.0.0 .
```
### 6. 应用启动
```bash
cd docs/dev-ops
docker-compose -f docker-compose-app.yml up -d
```
### 7. 验证部署
```bash
# 查看容器状态
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 标准模板
```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 标准模板
```yaml
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 标准模板
```yaml
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
```bash
#!/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
```bash
#!/bin/bash
CONTAINER_NAME={artifactId}
echo "停止容器 ${CONTAINER_NAME}"
docker stop ${CONTAINER_NAME}
docker rm ${CONTAINER_NAME}
echo "容器已停止"
```
---
## 🔧 application-prod.yml 标准配置
```yaml
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](https://github.com/fuzhengwei/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 |
### 拉取镜像示例
```bash
# 拉取 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 认证问题
```bash
docker exec mysql mysql -uroot -p123456 -e "ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456'; FLUSH PRIVILEGES;"
```
### 2. 容器网络不通
确保所有容器在同一个网络:
```yaml
networks:
- my-network
```
### 3. 端口冲突
修改 docker-compose.yml 中的端口映射:
```yaml
ports:
- "13306:3306" # 改为非标准端口
```
### 4. 应用无法连接数据库
检查环境变量配置和健康检查依赖:
```yaml
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. **执行部署**
```bash
# 进入项目目录
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. **部署完成检查**
- 容器状态正常
- 日志无报错
- 健康检查通过
标签
skill
ai