# 🐍 Python版整洁架构事件驱动微服务生成器
> **无需Go环境,纯Python一键生成** 整洁架构 + 事件溯源 + CQRS + Projection 完整项目
## 🎯 核心特性
| 特性 | 描述 |
| ------------ | ------------------------------ |
| **零依赖** | 只需Python3,无需Go环境 |
| **整洁架构** | 严格遵循Clean Architecture原则 |
| **事件溯源** | 完整的事件存储和重播机制 |
| **CQRS** | 读写分离,高性能查询 |
| **投影系统** | 毫秒级查询响应 |
| **一键启动** | 15秒内生成完整项目 |
## 🚀 快速开始
### 1. 生成图片管理系统示例
```bash
python3 python-generator.py --config examples/clean-arch-go-config.yaml --project image-system --quick-start
```
### 2. 15秒启动完整系统
```bash
cd image-system
./scripts/start.sh
# 访问服务
API: http://localhost:8080
NATS监控: http://localhost:8222
```
## 📁 生成的项目结构
```
project-name/
├── cmd/ # 应用入口
│ ├── api/ # HTTP API服务
│ ├── consumer/ # 事件消费者
│ ├── projection/ # 投影构建器
│ └── migration/ # 数据库迁移
├── internal/ # 核心业务逻辑
│ ├── domain/ # 领域层
│ │ ├── aggregate/ # 聚合根
│ │ ├── event/ # 领域事件
│ │ ├── repository/ # 仓储接口
│ │ └── projection/ # 投影接口
│ ├── usecase/ # 用例层
│ │ ├── command/ # 写用例
│ │ ├── query/ # 读用例
│ │ └── event/ # 事件处理
│ ├── adapter/ # 适配器层
│ │ ├── inbound/ # 输入适配器
│ │ └── outbound/ # 输出适配器
│ ├── infrastructure/ # 基础设施层
│ │ ├── config/ # 配置管理
│ │ ├── bootstrap/ # 服务引导程序
│ │ ├── database/ # 数据库连接
│ │ ├── eventstore/ # 事件存储
│ │ └── container/ # 依赖注入容器
├── configs/ # 配置文件
│ ├── app.yaml # 应用配置
│ └── .env.example # 环境变量模板
├── migrations/ # 数据库迁移
├── scripts/ # 开发脚本
└── docs/ # 项目文档
```
## 🏗️ 架构层次
### 领域层 (Domain Layer)
- **聚合根**: 核心业务逻辑和状态
- **领域事件**: 业务状态变化的记录
- **仓储接口**: 数据持久化抽象
### 用例层 (Use Case Layer)
- **命令用例**: 处理写操作
- **查询用例**: 处理读操作
- **事件处理**: 响应领域事件
### 适配器层 (Adapter Layer)
- **输入适配器**: HTTP API, gRPC, CLI
- **输出适配器**: 数据库, 消息队列, 缓存
### 基础设施层 (Infrastructure Layer)
- **事件存储**: PostgreSQL + NATS
- **投影存储**: PostgreSQL + Redis
- **缓存层**: Redis多级缓存
## 🎯 事件溯源实现
### 事件存储
```go
// 事件存储接口
type EventStore interface {
Save(ctx context.Context, aggregateID string, events []interface{}, version int) error
Load(ctx context.Context, aggregateID string) ([]interface{}, error)
Publish(ctx context.Context, event interface{}) error
// 快照支持
SaveSnapshot(ctx context.Context, aggregateID string, snapshot interface{}, version int) error
LoadSnapshot(ctx context.Context, aggregateID string) (interface{}, int, error)
GetSnapshotVersion(ctx context.Context, aggregateID string) (int, error)
}
// 快照接口
type Snapshot interface {
GetAggregateID() string
GetVersion() int
GetSnapshotData() interface{}
}
// 聚合根快照基类
type AggregateSnapshot struct {
AggregateID string
Version int
Data interface{}
}
func (s *AggregateSnapshot) GetAggregateID() string {
return s.AggregateID
}
func (s *AggregateSnapshot) GetVersion() int {
return s.Version
}
func (s *AggregateSnapshot) GetSnapshotData() interface{} {
return s.Data
}
```
### 投影系统
```go
// 投影构建器
type Projection interface {
Project(ctx context.Context, event interface{}) error
Get(ctx context.Context, id string) (interface{}, error)
}
```
## 📊 性能基准
| 操作类型 | 响应时间 | 并发能力 |
| -------- | ------------- | ---------- |
| 创建聚合 | < 50ms | 1000+ TPS |
| 查询投影 | < 5ms | 10000+ QPS |
| 事件重播 | < 1s/1000事件 | 可并行 |
| 投影更新 | < 100ms | 实时更新 |
## 🚀 Bootstrap系统
### 服务引导程序
```go
// 使用Bootstrap启动应用
func main() {
bootstrap.Run()
}
```
### 配置管理
```go
// 使用Viper加载配置
cfg, err := config.LoadConfig()
```
### 依赖注入容器
```go
// 容器管理所有依赖
container := container.NewContainer(cfg)
container.InitInfrastructure(ctx)
container.InitRepositories()
container.InitUseCases()
container.InitHTTPServer()
```
### 配置方式
#### 1. 配置文件 (configs/app.yaml)
```yaml
app:
name: my-service
version: 1.0.0
environment: development
debug: true
database:
host: localhost
port: 5432
user: user
password: password
dbname: mydb
```
#### 2. 环境变量
```bash
# 数据库配置
export DB_HOST=localhost
export DB_PORT=5432
export DB_USER=user
export DB_PASSWORD=password
# 启动应用
./scripts/start.sh
```
#### 3. .env文件
```bash
# 复制环境变量模板
cp configs/.env.example configs/.env
# 编辑配置文件
vim configs/.env
```
## 🛠️ 开发工具
### 一键启动
```bash
# 开发环境
./scripts/start.sh
# 生产环境
./scripts/build.sh
docker-compose up -d
```
### 数据库迁移
```bash
# 自动迁移
go run cmd/migration/main.go
# 手动迁移
psql -h localhost -U user -d image_system -f migrations/001_create_events.up.sql
```
### 测试命令
```bash
# 单元测试
go test ./...
# 集成测试
./scripts/test.sh
# 性能测试
./scripts/benchmark.sh
```
## 🎨 DSL配置指南
### 配置文件结构详解
Python生成器使用YAML格式的DSL(领域特定语言)来定义整个微服务的架构。配置文件支持以下顶级配置项:
#### 📋 项目配置 (project)
定义项目的基本信息:
```yaml
project:
name: my-service # 项目名称(必填)- 用于包名、模块名
description: 我的微服务 # 项目描述(可选)- 用于README文档
version: 1.0.0 # 版本号(可选)- 默认为1.0.0
author: Your Name # 作者信息(可选)
go_version: 1.21 # Go版本(可选)- 默认为1.21
```
#### 🏗️ 聚合根配置 (aggregates)
定义业务聚合根,每个聚合根对应一个核心业务实体:
```yaml
aggregates:
- name: User # 聚合根名称(必填)- 首字母大写
description: 用户聚合根 # 描述(可选)
fields: # 聚合根字段定义
- name: username # 字段名(必填)
type: string # Go类型(必填)- string/int/float64/bool/time.Time
json: username # JSON标签(可选)- 默认为字段名小写
gorm: uniqueIndex # GORM标签(可选)- 支持所有GORM标签
validate: required,min=3 # 验证标签(可选)- 支持go-playground/validator
description: 用户名 # 字段描述(可选)
- name: email
type: string
json: email
validate: required,email
gorm: uniqueIndex
- name: age
type: int
json: age
validate: min=0,max=150
- name: active
type: bool
json: active
default: true # 默认值(可选)
```
#### ⚡ 事件定义 (events)
每个聚合根可以定义多个领域事件:
```yaml
events:
- name: UserCreated # 事件名称(必填)- 必须以聚合根名开头
description: 用户创建事件 # 事件描述(可选)
fields: # 事件字段(可选)
- name: UserID
type: string
json: user_id
description: 用户ID
- name: Username
type: string
json: username
- name: UserUpdated
description: 用户更新事件
fields:
- name: UserID
type: string
json: user_id
- name: UpdatedFields
type: map[string]interface{} # 支持复杂类型
json: updated_fields
- name: UserDeleted
description: 用户删除事件
fields:
- name: UserID
type: string
json: user_id
- name: DeletedAt
type: time.Time
json: deleted_at
```
#### 📊 投影配置 (projections)
定义事件投影模型(可选配置):
```yaml
projections:
- name: UserProjection # 投影名称(必填)
aggregate: User # 关联的聚合根(必填)
description: 用户查询投影 # 描述(可选)
fields: # 投影字段定义
- name: ID
type: string
gorm: primaryKey
- name: Username
type: string
gorm: index
- name: Email
type: string
gorm: uniqueIndex
- name: Active
type: bool
gorm: index
```
#### 🔧 基础设施配置 (infrastructure)
定义基础设施依赖:
```yaml
infrastructure:
database: # 数据库配置
type: postgresql # 支持postgresql/mysql/sqlite
host: localhost
port: 5432
database: myservice
username: user
password: password
cache: # 缓存配置
type: redis # 支持redis/memcached
host: localhost
port: 6379
message_queue: # 消息队列
type: nats # 支持nats/rabbitmq/kafka
host: localhost
port: 4222
monitoring: # 监控配置
metrics: true # 启用Prometheus指标
tracing: true # 启用分布式追踪
logging: zap # 日志库选择
```
#### 🌐 API配置 (api)
定义API接口:
```yaml
api:
http: # HTTP配置
enabled: true
port: 8080
prefix: /api/v1
grpc: # gRPC配置
enabled: true
port: 50051
reflection: true # 启用服务反射
cors: # CORS配置
enabled: true
origins: ["*"]
methods: ["GET", "POST", "PUT", "DELETE"]
headers: ["Content-Type", "Authorization"]
```
#### 🔗 会话管理 (session)
分布式会话管理配置,用于长时任务和Saga事务:
```yaml
session:
enabled: true # 启用会话管理
saga: # Saga配置
enabled: true
timeout: 30m # Saga超时时间
compensation: true # 启用补偿机制
context: # 会话上下文
storage: redis # 存储后端: redis/postgresql
ttl: 24h # 会话TTL
compression: true # 启用压缩
orchestration: # 编排配置
pattern: choreography # 编排模式: choreography/orchestration
coordinator: enabled # 启用协调器(仅orchestration模式)
checkpoint: # 检查点机制
enabled: true
interval: 30s # 自动检查点间隔
storage: postgresql # 检查点存储
recovery: # 故障恢复
enabled: true
strategy: retry # 恢复策略: retry/rollback/compensate
max_retries: 3 # 最大重试次数
backoff: exponential # 退避策略
```
#### 🔄 长时任务 (long_running)
长时任务和批处理配置:
```yaml
long_running:
enabled: true
tasks:
- name: ImageProcessingTask
type: batch # 任务类型: batch/streaming
timeout: 2h
parallelism: 5 # 并行度
stages: # 处理阶段
- name: Download
timeout: 10m
retry_policy: 3
- name: Process
timeout: 1h
checkpoint: true # 启用检查点
- name: Upload
timeout: 30m
retry_policy: 5
context: # 会话上下文
required_fields:
- user_id
- image_urls
- processing_config
distributed_lock: true # 分布式锁
state_machine: true # 状态机
compensation: # 补偿机制
enabled: true
rollback_steps:
- DeleteTempFiles
- RevertDatabase
- SendNotification
```
### 完整配置示例
```yaml
# 完整项目配置示例
project:
name: ecommerce-order-service
description: 电商订单服务 - 支持订单创建、支付、发货等完整生命周期
version: 2.1.0
author: 架构团队
go_version: 1.21
aggregates:
- name: Order
description: 订单聚合根
fields:
- name: OrderNumber
type: string
json: order_number
validate: required
gorm: uniqueIndex
description: 订单号
- name: CustomerID
type: string
json: customer_id
validate: required,uuid
gorm: index
description: 客户ID
- name: TotalAmount
type: float64
json: total_amount
validate: required,min=0
description: 订单总金额
- name: Status
type: string
json: status
validate: required,oneof=pending paid shipped delivered cancelled
gorm: index
default: pending
description: 订单状态
- name: ShippingAddress
type: string
json: shipping_address
validate: required
description: 收货地址
events:
- name: OrderCreated
description: 订单创建事件
fields:
- name: OrderID
type: string
json: order_id
validate: required,uuid
- name: Items
type: "[]OrderItem" # 支持自定义类型
json: items
- name: OrderPaid
description: 订单支付事件
fields:
- name: OrderID
type: string
json: order_id
- name: PaymentID
type: string
json: payment_id
- name: PaidAt
type: time.Time
json: paid_at
- name: OrderShipped
description: 订单发货事件
fields:
- name: OrderID
type: string
json: order_id
- name: TrackingNumber
type: string
json: tracking_number
- name: OrderDelivered
description: 订单送达事件
fields:
- name: OrderID
type: string
json: order_id
- name: DeliveredAt
type: time.Time
json: delivered_at
projections:
- name: OrderSummaryProjection
aggregate: Order
description: 订单摘要投影 - 用于查询订单列表
fields:
- name: ID
type: string
gorm: primaryKey
- name: OrderNumber
type: string
gorm: uniqueIndex
- name: CustomerID
type: string
gorm: index
- name: TotalAmount
type: float64
- name: Status
type: string
gorm: index
- name: CreatedAt
type: time.Time
gorm: index
infrastructure:
database:
type: postgresql
host: localhost
port: 5432
database: ecommerce
username: ecommerce_user
password: secure_password
cache:
type: redis
host: localhost
port: 6379
message_queue:
type: nats
host: localhost
port: 4222
monitoring:
metrics: true
tracing: true
logging: zap
api:
http:
enabled: true
port: 8080
prefix: /api/v2
grpc:
enabled: true
port: 50051
reflection: true
cors:
enabled: true
origins: ["https://frontend.com", "https://admin.com"]
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"]
headers: ["Content-Type", "Authorization", "X-Requested-With"]
session:
enabled: true
saga:
enabled: true
timeout: 30m
compensation: true
context:
storage: redis
ttl: 24h
compression: true
orchestration:
pattern: choreography
coordinator: enabled
checkpoint:
enabled: true
interval: 30s
storage: postgresql
recovery:
enabled: true
strategy: retry
max_retries: 3
backoff: exponential
long_running:
enabled: true
tasks:
- name: OrderFulfillmentTask
type: batch
timeout: 4h
parallelism: 3
stages:
- name: InventoryCheck
timeout: 5m
retry_policy: 3
- name: PaymentProcessing
timeout: 15m
checkpoint: true
- name: ShippingArrangement
timeout: 1h
retry_policy: 2
- name: Notification
timeout: 5m
context:
required_fields:
- order_id
- customer_id
- payment_method
- shipping_address
distributed_lock: true
state_machine: true
compensation:
enabled: true
rollback_steps:
- ReleaseInventory
- RefundPayment
- CancelShipping
- SendCancellationNotification
```
### 生成项目
使用配置文件生成项目:
```bash
# 基础用法
python3 clean-arch-generator.py --config my-service.yaml
# 指定输出目录
python3 clean-arch-generator.py --config my-service.yaml --output ./projects/
# 使用自定义项目名
python3 clean-arch-generator.py --config my-service.yaml --project custom-name
```
### 支持的Go类型映射
| DSL类型 | Go类型 | 数据库类型 | 描述 |
| ---------------------- | ---------------------- | ------------ | ---------- |
| string | string | VARCHAR(255) | 字符串 |
| int | int | INTEGER | 整数 |
| int64 | int64 | BIGINT | 大整数 |
| float64 | float64 | DECIMAL | 浮点数 |
| bool | bool | BOOLEAN | 布尔值 |
| time.Time | time.Time | TIMESTAMP | 时间戳 |
| uuid.UUID | uuid.UUID | UUID | UUID |
| json.RawMessage | json.RawMessage | JSONB | JSON数据 |
| []string | []string | TEXT[] | 字符串数组 |
| map[string]interface{} | map[string]interface{} | JSONB | JSON对象 |
## 🔧 技术栈
### 核心依赖
- **Go**: 1.21+
- **PostgreSQL**: 15+ (事件存储)
- **Redis**: 7+ (缓存和投影)
- **NATS**: 2+ (消息队列)
### 开发工具
- **Docker**: 容器化部署
- **Docker Compose**: 本地开发
- **Make**: 构建工具
- **Air**: 热重载
## 🎯 会话管理架构
### 分布式会话设计
```
┌─────────────────────────────────────────────────────────────┐
│ Session Management Layer │
├─────────────────┬─────────────────┬───────────────────────────┤
│ Saga Manager │ Context Manager │ Checkpoint Manager │
│ │ │ │
│ ┌─────────────┐ │ ┌─────────────┐ │ ┌───────────────────────┐ │
│ │ Coordinator │ │ │ Session │ │ │ State Machine │ │
│ │ / Choreo │ │ │ Context │ │ │ Recovery Engine │ │
│ └─────────────┘ │ └─────────────┘ │ └───────────────────────┘ │
└─────────────────┴─────────────────┴───────────────────────────┘
│
┌─────────────────────────────────────────────────────────────┐
│ Storage Layer │
├─────────────────┬─────────────────┬───────────────────────────┤
│ Redis │ PostgreSQL │ Event Store │
│ (Session TTL) │ (Checkpoint) │ (Event Log) │
└─────────────────┴─────────────────┴───────────────────────────┘
```
### 会话上下文结构
```go
type SessionContext struct {
SessionID string `json:"session_id"`
SagaID string `json:"saga_id"`
WorkflowID string `json:"workflow_id"`
// 执行状态
CurrentStage string `json:"current_stage"`
StageIndex int `json:"stage_index"`
Status SessionStatus `json:"status"`
// 业务上下文
Payload map[string]interface{} `json:"payload"`
Metadata map[string]string `json:"metadata"`
// 时间控制
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ExpiresAt time.Time `json:"expires_at"`
// 故障恢复
RetryCount int `json:"retry_count"`
LastError string `json:"last_error"`
// 分布式锁
LockToken string `json:"lock_token"`
LockedBy string `json:"locked_by"`
LockExpiry time.Time `json:"lock_expiry"`
}
```
### 会话生命周期管理
```go
type SessionManager interface {
// 会话创建
CreateSession(ctx context.Context, workflowID string, payload map[string]interface{}) (*SessionContext, error)
// 状态更新
UpdateStage(ctx context.Context, sessionID string, stage string, data map[string]interface{}) error
// 检查点
SaveCheckpoint(ctx context.Context, sessionID string, stageIndex int) error
// 故障恢复
RecoverSession(ctx context.Context, sessionID string) (*SessionContext, error)
// 分布式锁
AcquireLock(ctx context.Context, sessionID string, instanceID string) (bool, error)
ReleaseLock(ctx context.Context, sessionID string, lockToken string) error
// 补偿机制
Compensate(ctx context.Context, sessionID string, steps []string) error
}
```
### 实际使用示例
#### 1. 启动长时任务会话
```go
// 创建订单履约会话
session, err := sessionManager.CreateSession(ctx, "order-fulfillment", map[string]interface{}{
"order_id": order.ID,
"customer_id": order.CustomerID,
"payment_method": order.PaymentMethod,
"shipping_address": order.ShippingAddress,
})
```
#### 2. 阶段执行与检查点
```go
// 执行库存检查阶段
func (s *OrderFulfillmentService) executeInventoryCheck(ctx context.Context, sessionID string) error {
// 获取会话上下文
session, err := s.sessionManager.GetSession(ctx, sessionID)
if err != nil {
return fmt.Errorf("failed to get session: %w", err)
}
// 执行业务逻辑
if err := s.inventoryService.CheckAvailability(ctx, session.Payload["order_id"]); err != nil {
// 保存失败状态
s.sessionManager.UpdateStage(ctx, sessionID, "inventory_check_failed", map[string]interface{}{
"error": err.Error(),
})
return err
}
// 保存检查点
return s.sessionManager.SaveCheckpoint(ctx, sessionID, 1)
}
```
#### 3. 故障恢复
```go
// 服务重启后恢复会话
func (s *OrderFulfillmentService) recoverSessions(ctx context.Context) error {
// 获取所有未完成的会话
sessions, err := s.sessionManager.GetActiveSessions(ctx)
if err != nil {
return err
}
for _, session := range sessions {
// 检查分布式锁
locked, err := s.sessionManager.AcquireLock(ctx, session.SessionID, s.instanceID)
if err != nil || !locked {
continue // 其他实例正在处理
}
// 恢复执行
go s.resumeWorkflow(ctx, session)
}
return nil
}
```
### 会话存储策略
#### Redis存储(高性能)
```yaml
session:
context:
storage: redis
ttl: 24h
compression: true
key_prefix: "session:"
cluster_mode: true
```
#### PostgreSQL存储(持久化)
```yaml
session:
context:
storage: postgresql
ttl: 7d
table_name: "session_contexts"
cleanup_interval: 1h
```
### 故障恢复策略
#### 1. 重试策略(Retry)
```go
// 指数退避重试
backoff := &ExponentialBackoff{
InitialInterval: time.Second,
MaxInterval: time.Minute,
Multiplier: 2.0,
MaxRetries: 3,
}
for i := 0; i < backoff.MaxRetries; i++ {
err := executeStage(ctx, sessionID)
if err == nil {
break
}
if i < backoff.MaxRetries-1 {
time.Sleep(backoff.NextInterval(i))
}
}
```
#### 2. 补偿策略(Compensate)
```go
// Saga补偿机制
func (s *OrderFulfillmentService) compensate(ctx context.Context, sessionID string) error {
session, _ := s.sessionManager.GetSession(ctx, sessionID)
// 逆序执行补偿步骤
for i := len(session.CompletedStages) - 1; i >= 0; i-- {
stage := session.CompletedStages[i]
switch stage.Name {
case "inventory_reserved":
s.inventoryService.ReleaseReservation(ctx, session.Payload["order_id"])
case "payment_processed":
s.paymentService.Refund(ctx, session.Payload["payment_id"])
case "shipping_arranged":
s.shippingService.CancelShipment(ctx, session.Payload["shipment_id"])
}
}
return s.sessionManager.Compensate(ctx, sessionID, []string{"all"})
}
```
## 📈 扩展能力
## 🎓 学习资源
### 文档
- [整洁架构指南](docs/architecture.md)
- [API文档](docs/api.md)
- [部署指南](docs/deployment.md)
### 示例项目
- [图片管理系统](examples/clean-arch-go-config.yaml)
- [订单系统](examples/order-system.yaml)
- [用户管理系统](examples/user-system.yaml)
## 🤝 贡献指南
1. Fork项目
2. 创建功能分支
3. 提交Pull Request
4. 通过代码审查
## 📄 许可证
MIT License - 自由使用,欢迎贡献!
Raw data
{
"_id": null,
"home_page": "https://github.com/DotNetAge/micro-gen",
"name": "micro-clean-gen",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "Ray <ray@rayainfo.cn>",
"keywords": "microservice, clean-architecture, code-generator, event-driven, ddd",
"author": "Ray",
"author_email": "Ray <ray@rayainfo.cn>",
"download_url": "https://files.pythonhosted.org/packages/ae/20/63dbc755c49e83d9e7c24f7aa0af5ca206c008bde3857e1e831146dc0e47/micro_clean_gen-1.0.0.tar.gz",
"platform": null,
"description": "# \ud83d\udc0d Python\u7248\u6574\u6d01\u67b6\u6784\u4e8b\u4ef6\u9a71\u52a8\u5fae\u670d\u52a1\u751f\u6210\u5668\n\n> **\u65e0\u9700Go\u73af\u5883\uff0c\u7eafPython\u4e00\u952e\u751f\u6210** \u6574\u6d01\u67b6\u6784 + \u4e8b\u4ef6\u6eaf\u6e90 + CQRS + Projection \u5b8c\u6574\u9879\u76ee\n\n## \ud83c\udfaf \u6838\u5fc3\u7279\u6027\n\n| \u7279\u6027 | \u63cf\u8ff0 |\n| ------------ | ------------------------------ |\n| **\u96f6\u4f9d\u8d56** | \u53ea\u9700Python3\uff0c\u65e0\u9700Go\u73af\u5883 |\n| **\u6574\u6d01\u67b6\u6784** | \u4e25\u683c\u9075\u5faaClean Architecture\u539f\u5219 |\n| **\u4e8b\u4ef6\u6eaf\u6e90** | \u5b8c\u6574\u7684\u4e8b\u4ef6\u5b58\u50a8\u548c\u91cd\u64ad\u673a\u5236 |\n| **CQRS** | \u8bfb\u5199\u5206\u79bb\uff0c\u9ad8\u6027\u80fd\u67e5\u8be2 |\n| **\u6295\u5f71\u7cfb\u7edf** | \u6beb\u79d2\u7ea7\u67e5\u8be2\u54cd\u5e94 |\n| **\u4e00\u952e\u542f\u52a8** | 15\u79d2\u5185\u751f\u6210\u5b8c\u6574\u9879\u76ee |\n\n## \ud83d\ude80 \u5feb\u901f\u5f00\u59cb\n\n### 1. \u751f\u6210\u56fe\u7247\u7ba1\u7406\u7cfb\u7edf\u793a\u4f8b\n\n```bash\npython3 python-generator.py --config examples/clean-arch-go-config.yaml --project image-system --quick-start\n```\n\n### 2. 15\u79d2\u542f\u52a8\u5b8c\u6574\u7cfb\u7edf\n\n```bash\ncd image-system\n./scripts/start.sh\n\n# \u8bbf\u95ee\u670d\u52a1\nAPI: http://localhost:8080\nNATS\u76d1\u63a7: http://localhost:8222\n```\n\n## \ud83d\udcc1 \u751f\u6210\u7684\u9879\u76ee\u7ed3\u6784\n\n```\nproject-name/\n\u251c\u2500\u2500 cmd/ # \u5e94\u7528\u5165\u53e3\n\u2502 \u251c\u2500\u2500 api/ # HTTP API\u670d\u52a1\n\u2502 \u251c\u2500\u2500 consumer/ # \u4e8b\u4ef6\u6d88\u8d39\u8005\n\u2502 \u251c\u2500\u2500 projection/ # \u6295\u5f71\u6784\u5efa\u5668\n\u2502 \u2514\u2500\u2500 migration/ # \u6570\u636e\u5e93\u8fc1\u79fb\n\u251c\u2500\u2500 internal/ # \u6838\u5fc3\u4e1a\u52a1\u903b\u8f91\n\u2502 \u251c\u2500\u2500 domain/ # \u9886\u57df\u5c42\n\u2502 \u2502 \u251c\u2500\u2500 aggregate/ # \u805a\u5408\u6839\n\u2502 \u2502 \u251c\u2500\u2500 event/ # \u9886\u57df\u4e8b\u4ef6\n\u2502 \u2502 \u251c\u2500\u2500 repository/ # \u4ed3\u50a8\u63a5\u53e3\n\u2502 \u2502 \u2514\u2500\u2500 projection/ # \u6295\u5f71\u63a5\u53e3\n\u2502 \u251c\u2500\u2500 usecase/ # \u7528\u4f8b\u5c42\n\u2502 \u2502 \u251c\u2500\u2500 command/ # \u5199\u7528\u4f8b\n\u2502 \u2502 \u251c\u2500\u2500 query/ # \u8bfb\u7528\u4f8b\n\u2502 \u2502 \u2514\u2500\u2500 event/ # \u4e8b\u4ef6\u5904\u7406\n\u2502 \u251c\u2500\u2500 adapter/ # \u9002\u914d\u5668\u5c42\n\u2502 \u2502 \u251c\u2500\u2500 inbound/ # \u8f93\u5165\u9002\u914d\u5668\n\u2502 \u2502 \u2514\u2500\u2500 outbound/ # \u8f93\u51fa\u9002\u914d\u5668\n\u2502 \u251c\u2500\u2500 infrastructure/ # \u57fa\u7840\u8bbe\u65bd\u5c42\n\u2502 \u2502 \u251c\u2500\u2500 config/ # \u914d\u7f6e\u7ba1\u7406\n\u2502 \u2502 \u251c\u2500\u2500 bootstrap/ # \u670d\u52a1\u5f15\u5bfc\u7a0b\u5e8f\n\u2502 \u2502 \u251c\u2500\u2500 database/ # \u6570\u636e\u5e93\u8fde\u63a5\n\u2502 \u2502 \u251c\u2500\u2500 eventstore/ # \u4e8b\u4ef6\u5b58\u50a8\n\u2502 \u2502 \u2514\u2500\u2500 container/ # \u4f9d\u8d56\u6ce8\u5165\u5bb9\u5668\n\u251c\u2500\u2500 configs/ # \u914d\u7f6e\u6587\u4ef6\n\u2502 \u251c\u2500\u2500 app.yaml # \u5e94\u7528\u914d\u7f6e\n\u2502 \u2514\u2500\u2500 .env.example # \u73af\u5883\u53d8\u91cf\u6a21\u677f\n\u251c\u2500\u2500 migrations/ # \u6570\u636e\u5e93\u8fc1\u79fb\n\u251c\u2500\u2500 scripts/ # \u5f00\u53d1\u811a\u672c\n\u2514\u2500\u2500 docs/ # \u9879\u76ee\u6587\u6863\n```\n\n## \ud83c\udfd7\ufe0f \u67b6\u6784\u5c42\u6b21\n\n### \u9886\u57df\u5c42 (Domain Layer)\n- **\u805a\u5408\u6839**: \u6838\u5fc3\u4e1a\u52a1\u903b\u8f91\u548c\u72b6\u6001\n- **\u9886\u57df\u4e8b\u4ef6**: \u4e1a\u52a1\u72b6\u6001\u53d8\u5316\u7684\u8bb0\u5f55\n- **\u4ed3\u50a8\u63a5\u53e3**: \u6570\u636e\u6301\u4e45\u5316\u62bd\u8c61\n\n### \u7528\u4f8b\u5c42 (Use Case Layer)\n- **\u547d\u4ee4\u7528\u4f8b**: \u5904\u7406\u5199\u64cd\u4f5c\n- **\u67e5\u8be2\u7528\u4f8b**: \u5904\u7406\u8bfb\u64cd\u4f5c\n- **\u4e8b\u4ef6\u5904\u7406**: \u54cd\u5e94\u9886\u57df\u4e8b\u4ef6\n\n### \u9002\u914d\u5668\u5c42 (Adapter Layer)\n- **\u8f93\u5165\u9002\u914d\u5668**: HTTP API, gRPC, CLI\n- **\u8f93\u51fa\u9002\u914d\u5668**: \u6570\u636e\u5e93, \u6d88\u606f\u961f\u5217, \u7f13\u5b58\n\n### \u57fa\u7840\u8bbe\u65bd\u5c42 (Infrastructure Layer)\n- **\u4e8b\u4ef6\u5b58\u50a8**: PostgreSQL + NATS\n- **\u6295\u5f71\u5b58\u50a8**: PostgreSQL + Redis\n- **\u7f13\u5b58\u5c42**: Redis\u591a\u7ea7\u7f13\u5b58\n\n## \ud83c\udfaf \u4e8b\u4ef6\u6eaf\u6e90\u5b9e\u73b0\n\n### \u4e8b\u4ef6\u5b58\u50a8\n```go\n// \u4e8b\u4ef6\u5b58\u50a8\u63a5\u53e3\ntype EventStore interface {\n Save(ctx context.Context, aggregateID string, events []interface{}, version int) error\n Load(ctx context.Context, aggregateID string) ([]interface{}, error)\n Publish(ctx context.Context, event interface{}) error\n \n // \u5feb\u7167\u652f\u6301\n SaveSnapshot(ctx context.Context, aggregateID string, snapshot interface{}, version int) error\n LoadSnapshot(ctx context.Context, aggregateID string) (interface{}, int, error)\n GetSnapshotVersion(ctx context.Context, aggregateID string) (int, error)\n}\n\n// \u5feb\u7167\u63a5\u53e3\ntype Snapshot interface {\n GetAggregateID() string\n GetVersion() int\n GetSnapshotData() interface{}\n}\n\n// \u805a\u5408\u6839\u5feb\u7167\u57fa\u7c7b\ntype AggregateSnapshot struct {\n AggregateID string\n Version int\n Data interface{}\n}\n\nfunc (s *AggregateSnapshot) GetAggregateID() string {\n return s.AggregateID\n}\n\nfunc (s *AggregateSnapshot) GetVersion() int {\n return s.Version\n}\n\nfunc (s *AggregateSnapshot) GetSnapshotData() interface{} {\n return s.Data\n}\n```\n\n### \u6295\u5f71\u7cfb\u7edf\n```go\n// \u6295\u5f71\u6784\u5efa\u5668\ntype Projection interface {\n Project(ctx context.Context, event interface{}) error\n Get(ctx context.Context, id string) (interface{}, error)\n}\n```\n\n## \ud83d\udcca \u6027\u80fd\u57fa\u51c6\n\n| \u64cd\u4f5c\u7c7b\u578b | \u54cd\u5e94\u65f6\u95f4 | \u5e76\u53d1\u80fd\u529b |\n| -------- | ------------- | ---------- |\n| \u521b\u5efa\u805a\u5408 | < 50ms | 1000+ TPS |\n| \u67e5\u8be2\u6295\u5f71 | < 5ms | 10000+ QPS |\n| \u4e8b\u4ef6\u91cd\u64ad | < 1s/1000\u4e8b\u4ef6 | \u53ef\u5e76\u884c |\n| \u6295\u5f71\u66f4\u65b0 | < 100ms | \u5b9e\u65f6\u66f4\u65b0 |\n\n## \ud83d\ude80 Bootstrap\u7cfb\u7edf\n\n### \u670d\u52a1\u5f15\u5bfc\u7a0b\u5e8f\n```go\n// \u4f7f\u7528Bootstrap\u542f\u52a8\u5e94\u7528\nfunc main() {\n bootstrap.Run()\n}\n```\n\n### \u914d\u7f6e\u7ba1\u7406\n```go\n// \u4f7f\u7528Viper\u52a0\u8f7d\u914d\u7f6e\ncfg, err := config.LoadConfig()\n```\n\n### \u4f9d\u8d56\u6ce8\u5165\u5bb9\u5668\n```go\n// \u5bb9\u5668\u7ba1\u7406\u6240\u6709\u4f9d\u8d56\ncontainer := container.NewContainer(cfg)\ncontainer.InitInfrastructure(ctx)\ncontainer.InitRepositories()\ncontainer.InitUseCases()\ncontainer.InitHTTPServer()\n```\n\n### \u914d\u7f6e\u65b9\u5f0f\n\n#### 1. \u914d\u7f6e\u6587\u4ef6 (configs/app.yaml)\n```yaml\napp:\n name: my-service\n version: 1.0.0\n environment: development\n debug: true\n\ndatabase:\n host: localhost\n port: 5432\n user: user\n password: password\n dbname: mydb\n```\n\n#### 2. \u73af\u5883\u53d8\u91cf\n```bash\n# \u6570\u636e\u5e93\u914d\u7f6e\nexport DB_HOST=localhost\nexport DB_PORT=5432\nexport DB_USER=user\nexport DB_PASSWORD=password\n\n# \u542f\u52a8\u5e94\u7528\n./scripts/start.sh\n```\n\n#### 3. .env\u6587\u4ef6\n```bash\n# \u590d\u5236\u73af\u5883\u53d8\u91cf\u6a21\u677f\ncp configs/.env.example configs/.env\n# \u7f16\u8f91\u914d\u7f6e\u6587\u4ef6\nvim configs/.env\n```\n\n## \ud83d\udee0\ufe0f \u5f00\u53d1\u5de5\u5177\n\n### \u4e00\u952e\u542f\u52a8\n```bash\n# \u5f00\u53d1\u73af\u5883\n./scripts/start.sh\n\n# \u751f\u4ea7\u73af\u5883\n./scripts/build.sh\ndocker-compose up -d\n```\n\n### \u6570\u636e\u5e93\u8fc1\u79fb\n```bash\n# \u81ea\u52a8\u8fc1\u79fb\ngo run cmd/migration/main.go\n\n# \u624b\u52a8\u8fc1\u79fb\npsql -h localhost -U user -d image_system -f migrations/001_create_events.up.sql\n```\n\n### \u6d4b\u8bd5\u547d\u4ee4\n```bash\n# \u5355\u5143\u6d4b\u8bd5\ngo test ./...\n\n# \u96c6\u6210\u6d4b\u8bd5\n./scripts/test.sh\n\n# \u6027\u80fd\u6d4b\u8bd5\n./scripts/benchmark.sh\n```\n\n## \ud83c\udfa8 DSL\u914d\u7f6e\u6307\u5357\n\n### \u914d\u7f6e\u6587\u4ef6\u7ed3\u6784\u8be6\u89e3\n\nPython\u751f\u6210\u5668\u4f7f\u7528YAML\u683c\u5f0f\u7684DSL\uff08\u9886\u57df\u7279\u5b9a\u8bed\u8a00\uff09\u6765\u5b9a\u4e49\u6574\u4e2a\u5fae\u670d\u52a1\u7684\u67b6\u6784\u3002\u914d\u7f6e\u6587\u4ef6\u652f\u6301\u4ee5\u4e0b\u9876\u7ea7\u914d\u7f6e\u9879\uff1a\n\n#### \ud83d\udccb \u9879\u76ee\u914d\u7f6e (project)\n\u5b9a\u4e49\u9879\u76ee\u7684\u57fa\u672c\u4fe1\u606f\uff1a\n\n```yaml\nproject:\n name: my-service # \u9879\u76ee\u540d\u79f0\uff08\u5fc5\u586b\uff09- \u7528\u4e8e\u5305\u540d\u3001\u6a21\u5757\u540d\n description: \u6211\u7684\u5fae\u670d\u52a1 # \u9879\u76ee\u63cf\u8ff0\uff08\u53ef\u9009\uff09- \u7528\u4e8eREADME\u6587\u6863\n version: 1.0.0 # \u7248\u672c\u53f7\uff08\u53ef\u9009\uff09- \u9ed8\u8ba4\u4e3a1.0.0\n author: Your Name # \u4f5c\u8005\u4fe1\u606f\uff08\u53ef\u9009\uff09\n go_version: 1.21 # Go\u7248\u672c\uff08\u53ef\u9009\uff09- \u9ed8\u8ba4\u4e3a1.21\n```\n\n#### \ud83c\udfd7\ufe0f \u805a\u5408\u6839\u914d\u7f6e (aggregates)\n\u5b9a\u4e49\u4e1a\u52a1\u805a\u5408\u6839\uff0c\u6bcf\u4e2a\u805a\u5408\u6839\u5bf9\u5e94\u4e00\u4e2a\u6838\u5fc3\u4e1a\u52a1\u5b9e\u4f53\uff1a\n\n```yaml\naggregates:\n - name: User # \u805a\u5408\u6839\u540d\u79f0\uff08\u5fc5\u586b\uff09- \u9996\u5b57\u6bcd\u5927\u5199\n description: \u7528\u6237\u805a\u5408\u6839 # \u63cf\u8ff0\uff08\u53ef\u9009\uff09\n fields: # \u805a\u5408\u6839\u5b57\u6bb5\u5b9a\u4e49\n - name: username # \u5b57\u6bb5\u540d\uff08\u5fc5\u586b\uff09\n type: string # Go\u7c7b\u578b\uff08\u5fc5\u586b\uff09- string/int/float64/bool/time.Time\n json: username # JSON\u6807\u7b7e\uff08\u53ef\u9009\uff09- \u9ed8\u8ba4\u4e3a\u5b57\u6bb5\u540d\u5c0f\u5199\n gorm: uniqueIndex # GORM\u6807\u7b7e\uff08\u53ef\u9009\uff09- \u652f\u6301\u6240\u6709GORM\u6807\u7b7e\n validate: required,min=3 # \u9a8c\u8bc1\u6807\u7b7e\uff08\u53ef\u9009\uff09- \u652f\u6301go-playground/validator\n description: \u7528\u6237\u540d # \u5b57\u6bb5\u63cf\u8ff0\uff08\u53ef\u9009\uff09\n \n - name: email\n type: string\n json: email\n validate: required,email\n gorm: uniqueIndex\n \n - name: age\n type: int\n json: age\n validate: min=0,max=150\n \n - name: active\n type: bool\n json: active\n default: true # \u9ed8\u8ba4\u503c\uff08\u53ef\u9009\uff09\n```\n\n#### \u26a1 \u4e8b\u4ef6\u5b9a\u4e49 (events)\n\u6bcf\u4e2a\u805a\u5408\u6839\u53ef\u4ee5\u5b9a\u4e49\u591a\u4e2a\u9886\u57df\u4e8b\u4ef6\uff1a\n\n```yaml\n events:\n - name: UserCreated # \u4e8b\u4ef6\u540d\u79f0\uff08\u5fc5\u586b\uff09- \u5fc5\u987b\u4ee5\u805a\u5408\u6839\u540d\u5f00\u5934\n description: \u7528\u6237\u521b\u5efa\u4e8b\u4ef6 # \u4e8b\u4ef6\u63cf\u8ff0\uff08\u53ef\u9009\uff09\n fields: # \u4e8b\u4ef6\u5b57\u6bb5\uff08\u53ef\u9009\uff09\n - name: UserID\n type: string\n json: user_id\n description: \u7528\u6237ID\n \n - name: Username\n type: string\n json: username\n \n - name: UserUpdated\n description: \u7528\u6237\u66f4\u65b0\u4e8b\u4ef6\n fields:\n - name: UserID\n type: string\n json: user_id\n \n - name: UpdatedFields\n type: map[string]interface{} # \u652f\u6301\u590d\u6742\u7c7b\u578b\n json: updated_fields\n \n - name: UserDeleted\n description: \u7528\u6237\u5220\u9664\u4e8b\u4ef6\n fields:\n - name: UserID\n type: string\n json: user_id\n \n - name: DeletedAt\n type: time.Time\n json: deleted_at\n```\n\n#### \ud83d\udcca \u6295\u5f71\u914d\u7f6e (projections)\n\u5b9a\u4e49\u4e8b\u4ef6\u6295\u5f71\u6a21\u578b\uff08\u53ef\u9009\u914d\u7f6e\uff09\uff1a\n\n```yaml\nprojections:\n - name: UserProjection # \u6295\u5f71\u540d\u79f0\uff08\u5fc5\u586b\uff09\n aggregate: User # \u5173\u8054\u7684\u805a\u5408\u6839\uff08\u5fc5\u586b\uff09\n description: \u7528\u6237\u67e5\u8be2\u6295\u5f71 # \u63cf\u8ff0\uff08\u53ef\u9009\uff09\n fields: # \u6295\u5f71\u5b57\u6bb5\u5b9a\u4e49\n - name: ID\n type: string\n gorm: primaryKey\n \n - name: Username\n type: string\n gorm: index\n \n - name: Email\n type: string\n gorm: uniqueIndex\n \n - name: Active\n type: bool\n gorm: index\n```\n\n#### \ud83d\udd27 \u57fa\u7840\u8bbe\u65bd\u914d\u7f6e (infrastructure)\n\u5b9a\u4e49\u57fa\u7840\u8bbe\u65bd\u4f9d\u8d56\uff1a\n\n```yaml\ninfrastructure:\n database: # \u6570\u636e\u5e93\u914d\u7f6e\n type: postgresql # \u652f\u6301postgresql/mysql/sqlite\n host: localhost\n port: 5432\n database: myservice\n username: user\n password: password\n \n cache: # \u7f13\u5b58\u914d\u7f6e\n type: redis # \u652f\u6301redis/memcached\n host: localhost\n port: 6379\n \n message_queue: # \u6d88\u606f\u961f\u5217\n type: nats # \u652f\u6301nats/rabbitmq/kafka\n host: localhost\n port: 4222\n \n monitoring: # \u76d1\u63a7\u914d\u7f6e\n metrics: true # \u542f\u7528Prometheus\u6307\u6807\n tracing: true # \u542f\u7528\u5206\u5e03\u5f0f\u8ffd\u8e2a\n logging: zap # \u65e5\u5fd7\u5e93\u9009\u62e9\n```\n\n#### \ud83c\udf10 API\u914d\u7f6e (api)\n\u5b9a\u4e49API\u63a5\u53e3\uff1a\n\n```yaml\napi:\n http: # HTTP\u914d\u7f6e\n enabled: true\n port: 8080\n prefix: /api/v1\n \n grpc: # gRPC\u914d\u7f6e\n enabled: true\n port: 50051\n reflection: true # \u542f\u7528\u670d\u52a1\u53cd\u5c04\n \n cors: # CORS\u914d\u7f6e\n enabled: true\n origins: [\"*\"]\n methods: [\"GET\", \"POST\", \"PUT\", \"DELETE\"]\n headers: [\"Content-Type\", \"Authorization\"]\n```\n\n#### \ud83d\udd17 \u4f1a\u8bdd\u7ba1\u7406 (session)\n\u5206\u5e03\u5f0f\u4f1a\u8bdd\u7ba1\u7406\u914d\u7f6e\uff0c\u7528\u4e8e\u957f\u65f6\u4efb\u52a1\u548cSaga\u4e8b\u52a1\uff1a\n\n```yaml\nsession:\n enabled: true # \u542f\u7528\u4f1a\u8bdd\u7ba1\u7406\n \n saga: # Saga\u914d\u7f6e\n enabled: true\n timeout: 30m # Saga\u8d85\u65f6\u65f6\u95f4\n compensation: true # \u542f\u7528\u8865\u507f\u673a\u5236\n \n context: # \u4f1a\u8bdd\u4e0a\u4e0b\u6587\n storage: redis # \u5b58\u50a8\u540e\u7aef: redis/postgresql\n ttl: 24h # \u4f1a\u8bddTTL\n compression: true # \u542f\u7528\u538b\u7f29\n \n orchestration: # \u7f16\u6392\u914d\u7f6e\n pattern: choreography # \u7f16\u6392\u6a21\u5f0f: choreography/orchestration\n coordinator: enabled # \u542f\u7528\u534f\u8c03\u5668\uff08\u4ec5orchestration\u6a21\u5f0f\uff09\n \n checkpoint: # \u68c0\u67e5\u70b9\u673a\u5236\n enabled: true\n interval: 30s # \u81ea\u52a8\u68c0\u67e5\u70b9\u95f4\u9694\n storage: postgresql # \u68c0\u67e5\u70b9\u5b58\u50a8\n \n recovery: # \u6545\u969c\u6062\u590d\n enabled: true\n strategy: retry # \u6062\u590d\u7b56\u7565: retry/rollback/compensate\n max_retries: 3 # \u6700\u5927\u91cd\u8bd5\u6b21\u6570\n backoff: exponential # \u9000\u907f\u7b56\u7565\n```\n\n#### \ud83d\udd04 \u957f\u65f6\u4efb\u52a1 (long_running)\n\u957f\u65f6\u4efb\u52a1\u548c\u6279\u5904\u7406\u914d\u7f6e\uff1a\n\n```yaml\nlong_running:\n enabled: true\n \n tasks:\n - name: ImageProcessingTask\n type: batch # \u4efb\u52a1\u7c7b\u578b: batch/streaming\n timeout: 2h\n parallelism: 5 # \u5e76\u884c\u5ea6\n \n stages: # \u5904\u7406\u9636\u6bb5\n - name: Download\n timeout: 10m\n retry_policy: 3\n \n - name: Process\n timeout: 1h\n checkpoint: true # \u542f\u7528\u68c0\u67e5\u70b9\n \n - name: Upload\n timeout: 30m\n retry_policy: 5\n \n context: # \u4f1a\u8bdd\u4e0a\u4e0b\u6587\n required_fields:\n - user_id\n - image_urls\n - processing_config\n \n distributed_lock: true # \u5206\u5e03\u5f0f\u9501\n state_machine: true # \u72b6\u6001\u673a\n \n compensation: # \u8865\u507f\u673a\u5236\n enabled: true\n rollback_steps:\n - DeleteTempFiles\n - RevertDatabase\n - SendNotification\n```\n\n### \u5b8c\u6574\u914d\u7f6e\u793a\u4f8b\n\n```yaml\n# \u5b8c\u6574\u9879\u76ee\u914d\u7f6e\u793a\u4f8b\nproject:\n name: ecommerce-order-service\n description: \u7535\u5546\u8ba2\u5355\u670d\u52a1 - \u652f\u6301\u8ba2\u5355\u521b\u5efa\u3001\u652f\u4ed8\u3001\u53d1\u8d27\u7b49\u5b8c\u6574\u751f\u547d\u5468\u671f\n version: 2.1.0\n author: \u67b6\u6784\u56e2\u961f\n go_version: 1.21\n\naggregates:\n - name: Order\n description: \u8ba2\u5355\u805a\u5408\u6839\n fields:\n - name: OrderNumber\n type: string\n json: order_number\n validate: required\n gorm: uniqueIndex\n description: \u8ba2\u5355\u53f7\n \n - name: CustomerID\n type: string\n json: customer_id\n validate: required,uuid\n gorm: index\n description: \u5ba2\u6237ID\n \n - name: TotalAmount\n type: float64\n json: total_amount\n validate: required,min=0\n description: \u8ba2\u5355\u603b\u91d1\u989d\n \n - name: Status\n type: string\n json: status\n validate: required,oneof=pending paid shipped delivered cancelled\n gorm: index\n default: pending\n description: \u8ba2\u5355\u72b6\u6001\n \n - name: ShippingAddress\n type: string\n json: shipping_address\n validate: required\n description: \u6536\u8d27\u5730\u5740\n \n events:\n - name: OrderCreated\n description: \u8ba2\u5355\u521b\u5efa\u4e8b\u4ef6\n fields:\n - name: OrderID\n type: string\n json: order_id\n validate: required,uuid\n \n - name: Items\n type: \"[]OrderItem\" # \u652f\u6301\u81ea\u5b9a\u4e49\u7c7b\u578b\n json: items\n \n - name: OrderPaid\n description: \u8ba2\u5355\u652f\u4ed8\u4e8b\u4ef6\n fields:\n - name: OrderID\n type: string\n json: order_id\n \n - name: PaymentID\n type: string\n json: payment_id\n \n - name: PaidAt\n type: time.Time\n json: paid_at\n \n - name: OrderShipped\n description: \u8ba2\u5355\u53d1\u8d27\u4e8b\u4ef6\n fields:\n - name: OrderID\n type: string\n json: order_id\n \n - name: TrackingNumber\n type: string\n json: tracking_number\n \n - name: OrderDelivered\n description: \u8ba2\u5355\u9001\u8fbe\u4e8b\u4ef6\n fields:\n - name: OrderID\n type: string\n json: order_id\n \n - name: DeliveredAt\n type: time.Time\n json: delivered_at\n\nprojections:\n - name: OrderSummaryProjection\n aggregate: Order\n description: \u8ba2\u5355\u6458\u8981\u6295\u5f71 - \u7528\u4e8e\u67e5\u8be2\u8ba2\u5355\u5217\u8868\n fields:\n - name: ID\n type: string\n gorm: primaryKey\n \n - name: OrderNumber\n type: string\n gorm: uniqueIndex\n \n - name: CustomerID\n type: string\n gorm: index\n \n - name: TotalAmount\n type: float64\n \n - name: Status\n type: string\n gorm: index\n \n - name: CreatedAt\n type: time.Time\n gorm: index\n\ninfrastructure:\n database:\n type: postgresql\n host: localhost\n port: 5432\n database: ecommerce\n username: ecommerce_user\n password: secure_password\n \n cache:\n type: redis\n host: localhost\n port: 6379\n \n message_queue:\n type: nats\n host: localhost\n port: 4222\n \n monitoring:\n metrics: true\n tracing: true\n logging: zap\n\napi:\n http:\n enabled: true\n port: 8080\n prefix: /api/v2\n \n grpc:\n enabled: true\n port: 50051\n reflection: true\n \n cors:\n enabled: true\n origins: [\"https://frontend.com\", \"https://admin.com\"]\n methods: [\"GET\", \"POST\", \"PUT\", \"DELETE\", \"OPTIONS\"]\n headers: [\"Content-Type\", \"Authorization\", \"X-Requested-With\"]\n\nsession:\n enabled: true\n \n saga:\n enabled: true\n timeout: 30m\n compensation: true\n \n context:\n storage: redis\n ttl: 24h\n compression: true\n \n orchestration:\n pattern: choreography\n coordinator: enabled\n \n checkpoint:\n enabled: true\n interval: 30s\n storage: postgresql\n \n recovery:\n enabled: true\n strategy: retry\n max_retries: 3\n backoff: exponential\n\nlong_running:\n enabled: true\n \n tasks:\n - name: OrderFulfillmentTask\n type: batch\n timeout: 4h\n parallelism: 3\n \n stages:\n - name: InventoryCheck\n timeout: 5m\n retry_policy: 3\n \n - name: PaymentProcessing\n timeout: 15m\n checkpoint: true\n \n - name: ShippingArrangement\n timeout: 1h\n retry_policy: 2\n \n - name: Notification\n timeout: 5m\n \n context:\n required_fields:\n - order_id\n - customer_id\n - payment_method\n - shipping_address\n \n distributed_lock: true\n state_machine: true\n \n compensation:\n enabled: true\n rollback_steps:\n - ReleaseInventory\n - RefundPayment\n - CancelShipping\n - SendCancellationNotification\n```\n\n### \u751f\u6210\u9879\u76ee\n\n\u4f7f\u7528\u914d\u7f6e\u6587\u4ef6\u751f\u6210\u9879\u76ee\uff1a\n\n```bash\n# \u57fa\u7840\u7528\u6cd5\npython3 clean-arch-generator.py --config my-service.yaml\n\n# \u6307\u5b9a\u8f93\u51fa\u76ee\u5f55\npython3 clean-arch-generator.py --config my-service.yaml --output ./projects/\n\n# \u4f7f\u7528\u81ea\u5b9a\u4e49\u9879\u76ee\u540d\npython3 clean-arch-generator.py --config my-service.yaml --project custom-name\n```\n\n### \u652f\u6301\u7684Go\u7c7b\u578b\u6620\u5c04\n\n| DSL\u7c7b\u578b | Go\u7c7b\u578b | \u6570\u636e\u5e93\u7c7b\u578b | \u63cf\u8ff0 |\n| ---------------------- | ---------------------- | ------------ | ---------- |\n| string | string | VARCHAR(255) | \u5b57\u7b26\u4e32 |\n| int | int | INTEGER | \u6574\u6570 |\n| int64 | int64 | BIGINT | \u5927\u6574\u6570 |\n| float64 | float64 | DECIMAL | \u6d6e\u70b9\u6570 |\n| bool | bool | BOOLEAN | \u5e03\u5c14\u503c |\n| time.Time | time.Time | TIMESTAMP | \u65f6\u95f4\u6233 |\n| uuid.UUID | uuid.UUID | UUID | UUID |\n| json.RawMessage | json.RawMessage | JSONB | JSON\u6570\u636e |\n| []string | []string | TEXT[] | \u5b57\u7b26\u4e32\u6570\u7ec4 |\n| map[string]interface{} | map[string]interface{} | JSONB | JSON\u5bf9\u8c61 |\n\n## \ud83d\udd27 \u6280\u672f\u6808\n\n### \u6838\u5fc3\u4f9d\u8d56\n- **Go**: 1.21+\n- **PostgreSQL**: 15+ (\u4e8b\u4ef6\u5b58\u50a8)\n- **Redis**: 7+ (\u7f13\u5b58\u548c\u6295\u5f71)\n- **NATS**: 2+ (\u6d88\u606f\u961f\u5217)\n\n### \u5f00\u53d1\u5de5\u5177\n- **Docker**: \u5bb9\u5668\u5316\u90e8\u7f72\n- **Docker Compose**: \u672c\u5730\u5f00\u53d1\n- **Make**: \u6784\u5efa\u5de5\u5177\n- **Air**: \u70ed\u91cd\u8f7d\n\n## \ud83c\udfaf \u4f1a\u8bdd\u7ba1\u7406\u67b6\u6784\n\n### \u5206\u5e03\u5f0f\u4f1a\u8bdd\u8bbe\u8ba1\n\n```\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Session Management Layer \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Saga Manager \u2502 Context Manager \u2502 Checkpoint Manager \u2502\n\u2502 \u2502 \u2502 \u2502\n\u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\n\u2502 \u2502 Coordinator \u2502 \u2502 \u2502 Session \u2502 \u2502 \u2502 State Machine \u2502 \u2502\n\u2502 \u2502 / Choreo \u2502 \u2502 \u2502 Context \u2502 \u2502 \u2502 Recovery Engine \u2502 \u2502\n\u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u2502\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Storage Layer \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Redis \u2502 PostgreSQL \u2502 Event Store \u2502\n\u2502 (Session TTL) \u2502 (Checkpoint) \u2502 (Event Log) \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n### \u4f1a\u8bdd\u4e0a\u4e0b\u6587\u7ed3\u6784\n\n```go\ntype SessionContext struct {\n SessionID string `json:\"session_id\"`\n SagaID string `json:\"saga_id\"`\n WorkflowID string `json:\"workflow_id\"`\n \n // \u6267\u884c\u72b6\u6001\n CurrentStage string `json:\"current_stage\"`\n StageIndex int `json:\"stage_index\"`\n Status SessionStatus `json:\"status\"`\n \n // \u4e1a\u52a1\u4e0a\u4e0b\u6587\n Payload map[string]interface{} `json:\"payload\"`\n Metadata map[string]string `json:\"metadata\"`\n \n // \u65f6\u95f4\u63a7\u5236\n CreatedAt time.Time `json:\"created_at\"`\n UpdatedAt time.Time `json:\"updated_at\"`\n ExpiresAt time.Time `json:\"expires_at\"`\n \n // \u6545\u969c\u6062\u590d\n RetryCount int `json:\"retry_count\"`\n LastError string `json:\"last_error\"`\n \n // \u5206\u5e03\u5f0f\u9501\n LockToken string `json:\"lock_token\"`\n LockedBy string `json:\"locked_by\"`\n LockExpiry time.Time `json:\"lock_expiry\"`\n}\n```\n\n### \u4f1a\u8bdd\u751f\u547d\u5468\u671f\u7ba1\u7406\n\n```go\ntype SessionManager interface {\n // \u4f1a\u8bdd\u521b\u5efa\n CreateSession(ctx context.Context, workflowID string, payload map[string]interface{}) (*SessionContext, error)\n \n // \u72b6\u6001\u66f4\u65b0\n UpdateStage(ctx context.Context, sessionID string, stage string, data map[string]interface{}) error\n \n // \u68c0\u67e5\u70b9\n SaveCheckpoint(ctx context.Context, sessionID string, stageIndex int) error\n \n // \u6545\u969c\u6062\u590d\n RecoverSession(ctx context.Context, sessionID string) (*SessionContext, error)\n \n // \u5206\u5e03\u5f0f\u9501\n AcquireLock(ctx context.Context, sessionID string, instanceID string) (bool, error)\n ReleaseLock(ctx context.Context, sessionID string, lockToken string) error\n \n // \u8865\u507f\u673a\u5236\n Compensate(ctx context.Context, sessionID string, steps []string) error\n}\n```\n\n### \u5b9e\u9645\u4f7f\u7528\u793a\u4f8b\n\n#### 1. \u542f\u52a8\u957f\u65f6\u4efb\u52a1\u4f1a\u8bdd\n```go\n// \u521b\u5efa\u8ba2\u5355\u5c65\u7ea6\u4f1a\u8bdd\nsession, err := sessionManager.CreateSession(ctx, \"order-fulfillment\", map[string]interface{}{\n \"order_id\": order.ID,\n \"customer_id\": order.CustomerID,\n \"payment_method\": order.PaymentMethod,\n \"shipping_address\": order.ShippingAddress,\n})\n```\n\n#### 2. \u9636\u6bb5\u6267\u884c\u4e0e\u68c0\u67e5\u70b9\n```go\n// \u6267\u884c\u5e93\u5b58\u68c0\u67e5\u9636\u6bb5\nfunc (s *OrderFulfillmentService) executeInventoryCheck(ctx context.Context, sessionID string) error {\n // \u83b7\u53d6\u4f1a\u8bdd\u4e0a\u4e0b\u6587\n session, err := s.sessionManager.GetSession(ctx, sessionID)\n if err != nil {\n return fmt.Errorf(\"failed to get session: %w\", err)\n }\n \n // \u6267\u884c\u4e1a\u52a1\u903b\u8f91\n if err := s.inventoryService.CheckAvailability(ctx, session.Payload[\"order_id\"]); err != nil {\n // \u4fdd\u5b58\u5931\u8d25\u72b6\u6001\n s.sessionManager.UpdateStage(ctx, sessionID, \"inventory_check_failed\", map[string]interface{}{\n \"error\": err.Error(),\n })\n return err\n }\n \n // \u4fdd\u5b58\u68c0\u67e5\u70b9\n return s.sessionManager.SaveCheckpoint(ctx, sessionID, 1)\n}\n```\n\n#### 3. \u6545\u969c\u6062\u590d\n```go\n// \u670d\u52a1\u91cd\u542f\u540e\u6062\u590d\u4f1a\u8bdd\nfunc (s *OrderFulfillmentService) recoverSessions(ctx context.Context) error {\n // \u83b7\u53d6\u6240\u6709\u672a\u5b8c\u6210\u7684\u4f1a\u8bdd\n sessions, err := s.sessionManager.GetActiveSessions(ctx)\n if err != nil {\n return err\n }\n \n for _, session := range sessions {\n // \u68c0\u67e5\u5206\u5e03\u5f0f\u9501\n locked, err := s.sessionManager.AcquireLock(ctx, session.SessionID, s.instanceID)\n if err != nil || !locked {\n continue // \u5176\u4ed6\u5b9e\u4f8b\u6b63\u5728\u5904\u7406\n }\n \n // \u6062\u590d\u6267\u884c\n go s.resumeWorkflow(ctx, session)\n }\n \n return nil\n}\n```\n\n### \u4f1a\u8bdd\u5b58\u50a8\u7b56\u7565\n\n#### Redis\u5b58\u50a8\uff08\u9ad8\u6027\u80fd\uff09\n```yaml\nsession:\n context:\n storage: redis\n ttl: 24h\n compression: true\n key_prefix: \"session:\"\n cluster_mode: true\n```\n\n#### PostgreSQL\u5b58\u50a8\uff08\u6301\u4e45\u5316\uff09\n```yaml\nsession:\n context:\n storage: postgresql\n ttl: 7d\n table_name: \"session_contexts\"\n cleanup_interval: 1h\n```\n\n### \u6545\u969c\u6062\u590d\u7b56\u7565\n\n#### 1. \u91cd\u8bd5\u7b56\u7565\uff08Retry\uff09\n```go\n// \u6307\u6570\u9000\u907f\u91cd\u8bd5\nbackoff := &ExponentialBackoff{\n InitialInterval: time.Second,\n MaxInterval: time.Minute,\n Multiplier: 2.0,\n MaxRetries: 3,\n}\n\nfor i := 0; i < backoff.MaxRetries; i++ {\n err := executeStage(ctx, sessionID)\n if err == nil {\n break\n }\n \n if i < backoff.MaxRetries-1 {\n time.Sleep(backoff.NextInterval(i))\n }\n}\n```\n\n#### 2. \u8865\u507f\u7b56\u7565\uff08Compensate\uff09\n```go\n// Saga\u8865\u507f\u673a\u5236\nfunc (s *OrderFulfillmentService) compensate(ctx context.Context, sessionID string) error {\n session, _ := s.sessionManager.GetSession(ctx, sessionID)\n \n // \u9006\u5e8f\u6267\u884c\u8865\u507f\u6b65\u9aa4\n for i := len(session.CompletedStages) - 1; i >= 0; i-- {\n stage := session.CompletedStages[i]\n \n switch stage.Name {\n case \"inventory_reserved\":\n s.inventoryService.ReleaseReservation(ctx, session.Payload[\"order_id\"])\n case \"payment_processed\":\n s.paymentService.Refund(ctx, session.Payload[\"payment_id\"])\n case \"shipping_arranged\":\n s.shippingService.CancelShipment(ctx, session.Payload[\"shipment_id\"])\n }\n }\n \n return s.sessionManager.Compensate(ctx, sessionID, []string{\"all\"})\n}\n```\n\n## \ud83d\udcc8 \u6269\u5c55\u80fd\u529b\n\n## \ud83c\udf93 \u5b66\u4e60\u8d44\u6e90\n\n### \u6587\u6863\n- [\u6574\u6d01\u67b6\u6784\u6307\u5357](docs/architecture.md)\n- [API\u6587\u6863](docs/api.md)\n- [\u90e8\u7f72\u6307\u5357](docs/deployment.md)\n\n### \u793a\u4f8b\u9879\u76ee\n- [\u56fe\u7247\u7ba1\u7406\u7cfb\u7edf](examples/clean-arch-go-config.yaml)\n- [\u8ba2\u5355\u7cfb\u7edf](examples/order-system.yaml)\n- [\u7528\u6237\u7ba1\u7406\u7cfb\u7edf](examples/user-system.yaml)\n\n## \ud83e\udd1d \u8d21\u732e\u6307\u5357\n\n1. Fork\u9879\u76ee\n2. \u521b\u5efa\u529f\u80fd\u5206\u652f\n3. \u63d0\u4ea4Pull Request\n4. \u901a\u8fc7\u4ee3\u7801\u5ba1\u67e5\n\n## \ud83d\udcc4 \u8bb8\u53ef\u8bc1\n\nMIT License - \u81ea\u7531\u4f7f\u7528\uff0c\u6b22\u8fce\u8d21\u732e\uff01\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "\u57fa\u4e8e\u6574\u6d01\u67b6\u6784\u7684\u4e8b\u4ef6\u9a71\u52a8\u5fae\u670d\u52a1\u4ee3\u7801\u751f\u6210\u5668",
"version": "1.0.0",
"project_urls": {
"Bug Tracker": "https://github.com/DotNetAge/micro-gen/issues",
"Documentation": "https://micro-gen.readthedocs.io/",
"Homepage": "https://github.com/DotNetAge/micro-gen",
"Repository": "https://github.com/DotNetAge/micro-gen"
},
"split_keywords": [
"microservice",
" clean-architecture",
" code-generator",
" event-driven",
" ddd"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "9ca451421e5f3b3eb9d12c1d64c92f1d3cf8f84084f46a80112459c50852e6f7",
"md5": "380f128441a80f233f99cfad9fc98b59",
"sha256": "0ef14fcbcde8285dd6ee5c3427576991bd0ebe047c41a46774df00cbbacc6c4f"
},
"downloads": -1,
"filename": "micro_clean_gen-1.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "380f128441a80f233f99cfad9fc98b59",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 40567,
"upload_time": "2025-08-29T07:53:59",
"upload_time_iso_8601": "2025-08-29T07:53:59.254973Z",
"url": "https://files.pythonhosted.org/packages/9c/a4/51421e5f3b3eb9d12c1d64c92f1d3cf8f84084f46a80112459c50852e6f7/micro_clean_gen-1.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "ae2063dbc755c49e83d9e7c24f7aa0af5ca206c008bde3857e1e831146dc0e47",
"md5": "5161fd85c0ddfcf37a9de0cc4097048b",
"sha256": "43d6a25f75c6b413ed5244f820370364e02902f762a80372cc94b247e9bdadea"
},
"downloads": -1,
"filename": "micro_clean_gen-1.0.0.tar.gz",
"has_sig": false,
"md5_digest": "5161fd85c0ddfcf37a9de0cc4097048b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 51358,
"upload_time": "2025-08-29T07:54:00",
"upload_time_iso_8601": "2025-08-29T07:54:00.841656Z",
"url": "https://files.pythonhosted.org/packages/ae/20/63dbc755c49e83d9e7c24f7aa0af5ca206c008bde3857e1e831146dc0e47/micro_clean_gen-1.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-29 07:54:00",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "DotNetAge",
"github_project": "micro-gen",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"requirements": [
{
"name": "pyyaml",
"specs": [
[
">=",
"6.0"
]
]
},
{
"name": "jinja2",
"specs": [
[
">=",
"3.1.0"
]
]
},
{
"name": "click",
"specs": [
[
">=",
"8.0.0"
]
]
},
{
"name": "colorama",
"specs": [
[
">=",
"0.4.4"
]
]
},
{
"name": "pathlib2",
"specs": [
[
">=",
"2.3.7"
]
]
},
{
"name": "jsonschema",
"specs": [
[
">=",
"4.0.0"
]
]
},
{
"name": "loguru",
"specs": [
[
">=",
"0.7.0"
]
]
}
],
"lcname": "micro-clean-gen"
}