Genex 后端开发指南
DDD + Clean Architecture + 微服务 | NestJS + Go + Kong + PostgreSQL
1. 技术栈总览
| 层级 |
技术 |
用途 |
| API网关 |
Kong |
限流、认证、路由、负载均衡 |
| 主要业务服务 |
NestJS (TypeScript) |
业务逻辑微服务 |
| 高性能服务 |
Go |
撮合引擎、链上事件监听、高频任务 |
| 翻译层 |
Go + Redis |
地址映射、Gas代付、术语翻译 |
| 数据库 |
PostgreSQL 15+ |
核心业务数据 |
| 时序数据库 |
TimescaleDB |
行情数据、监控指标 |
| 缓存 |
Redis Cluster |
热点数据、会话、订单簿缓存 |
| 消息队列 |
Kafka |
事件驱动、链上事件监听 |
| 搜索 |
Elasticsearch |
全文检索、日志分析 |
| AI/ML |
FastAPI (Python) |
信用评分、价格预测、异常检测 |
2. 架构设计:三层五域
2.1 五大业务域
| 域 |
服务名 |
语言 |
职责 |
| D1 发行域 |
issuer-service |
NestJS |
发行方入驻、券管理、发行审批 |
| D2 交易域 |
trading-service |
Go |
撮合引擎、订单簿、做市商 |
| D3 清算域 |
clearing-service |
NestJS + Go |
链上结算、兑付清算、退款 |
| D4 合规域 |
compliance-service |
NestJS |
KYC/AML、OFAC、Travel Rule |
| D5 AI域 |
ai-service |
FastAPI |
信用评分、定价、异常检测 |
| 通用 |
user-service |
NestJS |
用户注册、认证、Profile |
| 通用 |
translate-service |
Go |
UX翻译层(地址映射、Gas代付) |
| 通用 |
notification-service |
NestJS |
消息推送、邮件、短信 |
| 通用 |
chain-indexer |
Go |
链上事件监听、数据索引 |
2.2 服务间通信
┌──────────────────────────────────────────────┐
│ Kong API Gateway │
│ 限流 | OAuth 2.1 + JWT | 路由 | 负载均衡 │
├──────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌──────────┐ ┌────────────┐ │
│ │ user │ │ issuer │ │ trading │ │
│ │ service │ │ service │ │ service(Go)│ │
│ └────┬────┘ └────┬─────┘ └─────┬──────┘ │
│ │ │ │ │
│ └────────────┼──────────────┘ │
│ │ │
│ ┌─────┴─────┐ │
│ │ Kafka │ │
│ └─────┬─────┘ │
│ ┌────────────┼───────────────┐ │
│ ┌────┴────┐ ┌─────┴──────┐ ┌─────┴─────┐ │
│ │clearing │ │compliance │ │ai-service │ │
│ │service │ │service │ │(FastAPI) │ │
│ └─────────┘ └────────────┘ └───────────┘ │
└──────────────────────────────────────────────┘
- 同步通信:gRPC(服务间直接调用,如撮合→结算)
- 异步通信:Kafka事件驱动(链上事件、通知、审计日志)
3. DDD + Clean Architecture(NestJS服务)
3.1 模块结构(以issuer-service为例)
issuer-service/
├── src/
│ ├── main.ts
│ ├── app.module.ts
│ ├── domain/ # 领域层(纯业务逻辑,零外部依赖)
│ │ ├── entities/
│ │ │ ├── issuer.entity.ts # 发行方聚合根
│ │ │ ├── coupon-template.entity.ts
│ │ │ └── credit-rating.vo.ts # 值对象
│ │ ├── repositories/
│ │ │ └── issuer.repository.ts # Repository接口
│ │ ├── services/
│ │ │ ├── issuer-domain.service.ts
│ │ │ └── credit-scoring.service.ts
│ │ └── events/
│ │ ├── issuer-approved.event.ts
│ │ └── coupon-issued.event.ts
│ ├── application/ # 应用层(用例编排)
│ │ ├── commands/
│ │ │ ├── register-issuer.command.ts
│ │ │ ├── create-coupon.command.ts
│ │ │ └── handlers/
│ │ ├── queries/
│ │ │ ├── get-issuer.query.ts
│ │ │ └── handlers/
│ │ └── dto/
│ │ ├── create-issuer.dto.ts
│ │ └── create-coupon.dto.ts
│ ├── infrastructure/ # 基础设施层(外部实现)
│ │ ├── persistence/
│ │ │ ├── issuer.repository.impl.ts
│ │ │ ├── issuer.orm-entity.ts
│ │ │ └── typeorm.config.ts
│ │ ├── messaging/
│ │ │ └── kafka-publisher.ts
│ │ └── external/
│ │ └── chain-client.ts # 链上交互
│ └── interfaces/ # 接口层(HTTP/gRPC)
│ ├── http/
│ │ ├── issuer.controller.ts
│ │ └── coupon.controller.ts
│ └── grpc/
│ └── issuer.grpc-controller.ts
├── test/
└── package.json
3.2 领域实体
// src/domain/entities/issuer.entity.ts
export class Issuer {
readonly id: string;
readonly companyName: string;
private _creditRating: CreditRating;
private _issuanceQuota: number;
private _tier: IssuerTier;
private _status: IssuerStatus;
// 信用评分四因子
updateCreditScore(metrics: CreditMetrics): void {
const score =
0.35 * metrics.redemptionRate +
0.25 * (1 - metrics.breakageRatio) +
0.20 * Math.log(metrics.marketTenure + 1) / Math.log(37) +
0.20 * metrics.userSatisfaction;
this._creditRating = CreditRating.fromScore(score);
this._issuanceQuota = this._creditRating.calculateQuota();
}
// 发券校验
canIssueCoupon(params: CreateCouponParams): Result<void> {
if (this._status !== IssuerStatus.ACTIVE)
return Result.fail('发行方状态异常');
if (params.totalValue > this.remainingQuota)
return Result.fail('超出发行额度');
if (params.expiryDays > 365)
return Result.fail('Utility Track有效期不得超过12个月');
return Result.ok();
}
}
3.3 应用层Command
// src/application/commands/handlers/create-coupon.handler.ts
@CommandHandler(CreateCouponCommand)
export class CreateCouponHandler {
constructor(
private issuerRepo: IssuerRepository,
private eventBus: EventBus,
) {}
async execute(command: CreateCouponCommand): Promise<CouponDraft> {
const issuer = await this.issuerRepo.findById(command.issuerId);
if (!issuer) throw new NotFoundException('发行方不存在');
// 领域校验
const validation = issuer.canIssueCoupon(command.params);
if (validation.isFailure) throw new BadRequestException(validation.error);
// 创建券草稿(待审核)
const draft = CouponDraft.create(issuer, command.params);
await this.issuerRepo.saveCouponDraft(draft);
// 发布领域事件
this.eventBus.publish(new CouponDraftCreatedEvent(draft));
return draft;
}
}
4. Go 高性能服务
4.1 撮合引擎
// trading-service/internal/matching/engine.go
type MatchingEngine struct {
orderBooks map[string]*OrderBook // couponId → OrderBook
mu sync.RWMutex
}
type OrderBook struct {
Bids *redblacktree.Tree // 买单:价格降序
Asks *redblacktree.Tree // 卖单:价格升序
}
func (e *MatchingEngine) SubmitOrder(order *Order) []*Trade {
e.mu.Lock()
defer e.mu.Unlock()
book := e.getOrCreateBook(order.CouponID)
// Utility Track价格校验
if order.CouponType == CouponTypeUtility && order.Price > order.FaceValue {
return nil // 拒绝溢价单
}
// 撮合:价格优先 → 时间优先
trades := book.Match(order)
// 成交后发布事件到Kafka
for _, trade := range trades {
e.publisher.Publish("trade.matched", trade)
}
return trades
}
4.2 链上事件监听
// chain-indexer/internal/indexer/indexer.go
type ChainIndexer struct {
ethClient *ethclient.Client
kafka sarama.SyncProducer
contracts map[string]common.Address
}
func (i *ChainIndexer) Start(ctx context.Context) {
// 监听Genex Chain事件
query := ethereum.FilterQuery{
Addresses: []common.Address{
i.contracts["CouponFactory"],
i.contracts["Settlement"],
i.contracts["Redemption"],
},
}
logs := make(chan types.Log)
sub, _ := i.ethClient.SubscribeFilterLogs(ctx, query, logs)
for {
select {
case log := <-logs:
event := i.parseEvent(log)
i.kafka.SendMessage(&sarama.ProducerMessage{
Topic: "chain.events",
Value: sarama.ByteEncoder(event.Marshal()),
})
case err := <-sub.Err():
log.Error("subscription error", "err", err)
// 重连逻辑
case <-ctx.Done():
return
}
}
}
5. UX翻译层
核心创新:将所有Web3操作翻译为Web2用户可理解的操作。
// translate-service/internal/service/translate.go
type TranslateService struct {
mappingDB *redis.Client // 手机号→链上地址映射
mpcClient MPCClient // MPC钱包服务
paymaster PaymasterClient // Gas代付服务
}
// 手机号→链上地址
func (s *TranslateService) ResolveAddress(phone string) (common.Address, error) {
addr, err := s.mappingDB.Get(ctx, "phone:"+phone).Result()
if err == redis.Nil {
return common.Address{}, ErrUserNotFound
}
return common.HexToAddress(addr), nil
}
// 购买券:法币→链上原子交换
func (s *TranslateService) ExecutePurchase(req PurchaseRequest) (*PurchaseResult, error) {
// 1. 法币支付确认
// 2. 法币→稳定币转换
// 3. 调用Settlement合约原子交换(Gas由Paymaster代付)
// 4. 返回"订单号"(映射TX Hash)
}
6. API设计
6.1 RESTful规范
API规范:
风格: RESTful
版本控制: URL路径 /api/v1/
认证: OAuth 2.1 + JWT
限流:
普通用户: 100 req/min
发行方: 1,000 req/min
做市商: 10,000 req/min
响应格式: JSON
错误处理: RFC 7807 Problem Details
6.2 核心API端点
| 域 |
路径前缀 |
主要接口 |
| 用户 |
/api/v1/users |
注册、登录、KYC、资产查询 |
| 券 |
/api/v1/coupons |
发行、查询、转让、核销 |
| 交易 |
/api/v1/trades |
挂单、撤单、成交、历史 |
| 支付 |
/api/v1/payments |
充值、提现、法币转换 |
| 发行方 |
/api/v1/issuers |
入驻、审核、数据统计 |
| 合规 |
/api/v1/compliance |
KYC、AML、审计日志 |
| 翻译层 |
/api/v1/translate |
地址映射、Gas代付 |
7. 数据库设计
7.1 PostgreSQL核心表
-- 用户表
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
phone VARCHAR(20) UNIQUE,
email VARCHAR(100) UNIQUE,
kyc_level SMALLINT NOT NULL DEFAULT 0,
wallet_mode VARCHAR(10) NOT NULL DEFAULT 'standard',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 发行方表
CREATE TABLE issuers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
company_name VARCHAR(200) NOT NULL,
business_license VARCHAR(100),
credit_rating VARCHAR(5) NOT NULL DEFAULT 'BBB',
credit_score NUMERIC(5,2) NOT NULL DEFAULT 60.00,
issuance_quota NUMERIC(15,2) NOT NULL DEFAULT 100000,
tier VARCHAR(10) NOT NULL DEFAULT 'silver',
status VARCHAR(20) NOT NULL DEFAULT 'pending',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 券表(链下缓存,权威数据在链上)
CREATE TABLE coupons (
id UUID PRIMARY KEY,
chain_token_id BIGINT UNIQUE, -- 链上Token ID
issuer_id UUID REFERENCES issuers(id),
face_value NUMERIC(12,2) NOT NULL,
current_price NUMERIC(12,2),
expiry_date DATE NOT NULL,
coupon_type VARCHAR(10) NOT NULL DEFAULT 'utility',
status VARCHAR(20) NOT NULL DEFAULT 'available',
resale_count SMALLINT NOT NULL DEFAULT 0,
max_resale_count SMALLINT NOT NULL DEFAULT 3,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 地址映射表(翻译层核心)
CREATE TABLE address_mappings (
user_id UUID REFERENCES users(id),
chain_address VARCHAR(42) NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (user_id)
);
8. 安全规范
| 领域 |
规范 |
| 传输 |
TLS 1.3 + 服务间mTLS |
| 认证 |
OAuth 2.1 + JWT(短有效期 + Refresh Token) |
| 授权 |
RBAC |
| 数据加密 |
AES-256(敏感字段),HSM管理密钥 |
| API安全 |
Rate Limiting + CORS + CSRF防护 |
| SQL注入 |
ORM参数化查询(TypeORM / sqlx) |
| 审计 |
全链路操作日志,不可篡改 |
9. 部署架构
# Kubernetes部署
apiVersion: apps/v1
kind: Deployment
metadata:
name: issuer-service
spec:
replicas: 3
template:
spec:
containers:
- name: issuer-service
image: genex/issuer-service:latest
resources:
requests: { cpu: "250m", memory: "512Mi" }
limits: { cpu: "1000m", memory: "1Gi" }
env:
- name: DB_HOST
valueFrom:
configMapKeyRef: { name: db-config, key: host }
- name: DB_PASSWORD
valueFrom:
secretKeyRef: { name: db-secrets, key: password }
多区域部署
| 区域 |
部署 |
角色 |
| AWS美国(主) |
全量服务 + 主数据库 + Genex Chain主验证节点 |
|
| AWS新加坡 |
亚太服务 + 灾备数据库 + 区域验证节点 |
|
| 香港 |
监管节点 + 审计接口 |
|
10. 监控与可观测性
| 组件 |
工具 |
用途 |
| 指标 |
Prometheus + Grafana |
服务指标、业务指标 |
| 日志 |
ELK Stack |
日志收集分析 |
| 链路追踪 |
Jaeger |
分布式调用追踪 |
| 告警 |
AlertManager + PagerDuty |
异常告警 |
文档版本: v1.0
基于: Genex 券交易平台 - 软件需求规格说明书 v4.1
技术栈: NestJS + Go + Kong + PostgreSQL + Kafka + Redis