From cd4fba96ed7680a57ccb4b4442357976358c76fe Mon Sep 17 00:00:00 2001 From: Developer Date: Wed, 3 Dec 2025 20:44:24 -0800 Subject: [PATCH] =?UTF-8?q?docs(blockchain-service):=20=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E6=9E=B6=E6=9E=84=E8=AE=BE=E8=AE=A1=E4=BB=A5=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=E5=85=B6=E4=BB=96=E5=BE=AE=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 modules/ 分层模块文件结构 (api/application/domain/infrastructure) - 抽取 AggregateRoot 聚合根基类,统一领域事件管理 - 补充 BlockNumber、TxHash 值对象的完整定义 - 添加值对象导出索引文件 - 新增第16章架构兼容性说明: - 与其他服务的架构对比表 - 依赖倒置原则和六边形架构端口适配器说明 - 命名约定规范 - Symbol Token 注入规范 - 更新文档版本至 1.1.0 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../blockchain-service/DEVELOPMENT_GUIDE.md | 542 +++++++++++++++++- 1 file changed, 525 insertions(+), 17 deletions(-) diff --git a/backend/services/blockchain-service/DEVELOPMENT_GUIDE.md b/backend/services/blockchain-service/DEVELOPMENT_GUIDE.md index b8aa17aa..333f2c3b 100644 --- a/backend/services/blockchain-service/DEVELOPMENT_GUIDE.md +++ b/backend/services/blockchain-service/DEVELOPMENT_GUIDE.md @@ -97,6 +97,12 @@ blockchain-service/ ├── prisma/ │ └── schema.prisma ├── src/ +│ ├── modules/ # NestJS 模块定义 (与其他服务保持一致) +│ │ ├── api.module.ts # API 层模块 +│ │ ├── application.module.ts # 应用层模块 +│ │ ├── domain.module.ts # 领域层模块 +│ │ └── infrastructure.module.ts # 基础设施层模块 +│ │ │ ├── api/ # 入站适配器 (Driving Adapters) │ │ ├── controllers/ │ │ │ ├── health.controller.ts @@ -131,6 +137,7 @@ blockchain-service/ │ │ │ ├── domain/ # 领域层 (核心业务) │ │ ├── aggregates/ +│ │ │ ├── aggregate-root.base.ts # 聚合根基类 (统一事件管理) │ │ │ ├── deposit-transaction/ │ │ │ │ ├── deposit-transaction.aggregate.ts │ │ │ │ ├── deposit-transaction.factory.ts @@ -184,7 +191,9 @@ blockchain-service/ │ │ │ │ ├── monitored-address.repository.impl.ts │ │ │ │ └── block-checkpoint.repository.impl.ts │ │ │ └── mappers/ -│ │ │ └── deposit-transaction.mapper.ts +│ │ │ ├── deposit-transaction.mapper.ts +│ │ │ ├── monitored-address.mapper.ts +│ │ │ └── transaction-request.mapper.ts │ │ ├── kafka/ │ │ │ ├── event-publisher.service.ts │ │ │ ├── event-consumer.controller.ts @@ -239,6 +248,198 @@ blockchain-service/ └── DEVELOPMENT_GUIDE.md ``` +### 2.3 模块化设计 (与其他服务保持一致) + +为了与 identity-service、wallet-service、leaderboard-service 等服务保持架构一致性,采用分层模块化设计: + +```typescript +// src/modules/infrastructure.module.ts +import { Global, Module } from '@nestjs/common'; +import { PrismaService } from '@/infrastructure/persistence/prisma/prisma.service'; +import { RedisService } from '@/infrastructure/redis/redis.service'; +import { EvmProviderAdapter } from '@/infrastructure/blockchain/evm-provider.adapter'; +import { EventPublisherService } from '@/infrastructure/kafka/event-publisher.service'; +import { WalletClientService } from '@/infrastructure/external/wallet-service/wallet-client.service'; +import { IdentityClientService } from '@/infrastructure/external/identity-service/identity-client.service'; + +@Global() +@Module({ + providers: [ + PrismaService, + RedisService, + EvmProviderAdapter, + EventPublisherService, + WalletClientService, + IdentityClientService, + ], + exports: [ + PrismaService, + RedisService, + EvmProviderAdapter, + EventPublisherService, + WalletClientService, + IdentityClientService, + ], +}) +export class InfrastructureModule {} +``` + +```typescript +// src/modules/domain.module.ts +import { Module } from '@nestjs/common'; +import { InfrastructureModule } from './infrastructure.module'; +import { + DEPOSIT_TRANSACTION_REPOSITORY, + MONITORED_ADDRESS_REPOSITORY, + BLOCK_CHECKPOINT_REPOSITORY, +} from '@/domain/repositories'; +import { DepositTransactionRepositoryImpl } from '@/infrastructure/persistence/repositories/deposit-transaction.repository.impl'; +import { MonitoredAddressRepositoryImpl } from '@/infrastructure/persistence/repositories/monitored-address.repository.impl'; +import { BlockCheckpointRepositoryImpl } from '@/infrastructure/persistence/repositories/block-checkpoint.repository.impl'; +import { ConfirmationPolicyService } from '@/domain/services/confirmation-policy.service'; +import { ChainConfigService } from '@/domain/services/chain-config.service'; + +@Module({ + imports: [InfrastructureModule], + providers: [ + // 仓储实现 (依赖倒置) + { + provide: DEPOSIT_TRANSACTION_REPOSITORY, + useClass: DepositTransactionRepositoryImpl, + }, + { + provide: MONITORED_ADDRESS_REPOSITORY, + useClass: MonitoredAddressRepositoryImpl, + }, + { + provide: BLOCK_CHECKPOINT_REPOSITORY, + useClass: BlockCheckpointRepositoryImpl, + }, + // 领域服务 + ConfirmationPolicyService, + ChainConfigService, + ], + exports: [ + DEPOSIT_TRANSACTION_REPOSITORY, + MONITORED_ADDRESS_REPOSITORY, + BLOCK_CHECKPOINT_REPOSITORY, + ConfirmationPolicyService, + ChainConfigService, + ], +}) +export class DomainModule {} +``` + +```typescript +// src/modules/application.module.ts +import { Module } from '@nestjs/common'; +import { DomainModule } from './domain.module'; +import { InfrastructureModule } from './infrastructure.module'; +import { DepositDetectionService } from '@/application/services/deposit-detection.service'; +import { BalanceQueryService } from '@/application/services/balance-query.service'; +import { TransactionBroadcastService } from '@/application/services/transaction-broadcast.service'; +import { AddressRegistryService } from '@/application/services/address-registry.service'; +import { AddressCacheService } from '@/infrastructure/redis/address-cache.service'; +import { EventListenerService } from '@/infrastructure/blockchain/event-listener.service'; +import { BlockScannerService } from '@/infrastructure/blockchain/block-scanner.service'; + +@Module({ + imports: [DomainModule, InfrastructureModule], + providers: [ + // 应用服务 + DepositDetectionService, + BalanceQueryService, + TransactionBroadcastService, + AddressRegistryService, + // 基础设施服务 (需要应用层依赖) + AddressCacheService, + EventListenerService, + BlockScannerService, + ], + exports: [ + DepositDetectionService, + BalanceQueryService, + TransactionBroadcastService, + AddressRegistryService, + AddressCacheService, + ], +}) +export class ApplicationModule {} +``` + +```typescript +// src/modules/api.module.ts +import { Module } from '@nestjs/common'; +import { ApplicationModule } from './application.module'; +import { HealthController } from '@/api/controllers/health.controller'; +import { BalanceController } from '@/api/controllers/balance.controller'; +import { InternalController } from '@/api/controllers/internal.controller'; +import { EventConsumerController } from '@/infrastructure/kafka/event-consumer.controller'; + +@Module({ + imports: [ApplicationModule], + controllers: [ + HealthController, + BalanceController, + InternalController, + EventConsumerController, + ], +}) +export class ApiModule {} +``` + +```typescript +// src/app.module.ts +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { ScheduleModule } from '@nestjs/schedule'; +import { ApiModule } from '@/modules/api.module'; +import appConfig from '@/config/app.config'; +import databaseConfig from '@/config/database.config'; +import kafkaConfig from '@/config/kafka.config'; +import redisConfig from '@/config/redis.config'; +import chainConfig from '@/config/chain.config'; + +@Module({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + load: [appConfig, databaseConfig, kafkaConfig, redisConfig, chainConfig], + }), + ScheduleModule.forRoot(), + ApiModule, + ], +}) +export class AppModule {} +``` + +**模块依赖关系图:** + +``` +┌─────────────────────────────────────────────────────────────┐ +│ AppModule │ +│ ┌───────────────────────────────────────────────────────┐ │ +│ │ ApiModule │ │ +│ │ Controllers: Health, Balance, Internal, EventConsumer │ │ +│ └───────────────────────┬───────────────────────────────┘ │ +│ │ imports │ +│ ┌───────────────────────▼───────────────────────────────┐ │ +│ │ ApplicationModule │ │ +│ │ Services: DepositDetection, BalanceQuery, ... │ │ +│ └───────────────────────┬───────────────────────────────┘ │ +│ │ imports │ +│ ┌───────────────────────▼───────────────────────────────┐ │ +│ │ DomainModule │ │ +│ │ Repositories (interfaces → impl), Domain Services │ │ +│ └───────────────────────┬───────────────────────────────┘ │ +│ │ imports │ +│ ┌───────────────────────▼───────────────────────────────┐ │ +│ │ InfrastructureModule (@Global) │ │ +│ │ Prisma, Redis, Kafka, EVM Provider, External Clients │ │ +│ └───────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + --- ## 3. 数据模型设计 @@ -424,11 +625,66 @@ model BlockchainEvent { ## 4. 领域层设计 -### 4.1 聚合根:DepositTransaction +### 4.1 聚合根基类 (与其他服务保持一致) + +为了与 authorization-service、wallet-service 等服务保持架构一致性,抽取统一的聚合根基类: + +```typescript +// src/domain/aggregates/aggregate-root.base.ts + +import { DomainEvent } from '@/domain/events/domain-event.base'; + +/** + * 聚合根基类 + * + * 所有聚合根都应继承此基类,统一管理领域事件的收集和清理。 + * 参考: authorization-service/src/domain/aggregates/aggregate-root.base.ts + */ +export abstract class AggregateRoot { + private readonly _domainEvents: DomainEvent[] = []; + + /** + * 聚合根唯一标识 + */ + abstract get id(): TId | undefined; + + /** + * 获取所有待发布的领域事件 + */ + get domainEvents(): ReadonlyArray { + return [...this._domainEvents]; + } + + /** + * 添加领域事件 + * @param event 领域事件 + */ + protected addDomainEvent(event: DomainEvent): void { + this._domainEvents.push(event); + } + + /** + * 清空领域事件(在事件发布后调用) + */ + clearDomainEvents(): void { + this._domainEvents.length = 0; + } + + /** + * 检查是否有待发布的领域事件 + */ + hasDomainEvents(): boolean { + return this._domainEvents.length > 0; + } +} +``` + +### 4.2 聚合根:DepositTransaction ```typescript // src/domain/aggregates/deposit-transaction/deposit-transaction.aggregate.ts +import { AggregateRoot } from '../aggregate-root.base'; import { DomainEvent, DepositDetectedEvent, DepositConfirmedEvent } from '@/domain/events'; import { ChainType, TxHash, EvmAddress, TokenAmount, BlockNumber } from '@/domain/value-objects'; import { DepositStatus } from '@/domain/enums'; @@ -455,10 +711,10 @@ export interface DepositTransactionProps { updatedAt?: Date; } -export class DepositTransaction { - private readonly _domainEvents: DomainEvent[] = []; - - private constructor(private props: DepositTransactionProps) {} +export class DepositTransaction extends AggregateRoot { + private constructor(private props: DepositTransactionProps) { + super(); + } // Getters get id(): bigint | undefined { return this.props.id; } @@ -569,13 +825,7 @@ export class DepositTransaction { this.props.lastNotifyError = error; } - private addDomainEvent(event: DomainEvent): void { - this._domainEvents.push(event); - } - - clearDomainEvents(): void { - this._domainEvents.length = 0; - } + // 注意:addDomainEvent 和 clearDomainEvents 方法已在 AggregateRoot 基类中定义 toProps(): DepositTransactionProps { return { ...this.props }; @@ -583,7 +833,9 @@ export class DepositTransaction { } ``` -### 4.2 值对象 +### 4.3 值对象 + +#### 4.3.1 EVM 地址值对象 ```typescript // src/domain/value-objects/evm-address.vo.ts @@ -618,6 +870,8 @@ export class EvmAddress { } ``` +#### 4.3.2 代币金额值对象 + ```typescript // src/domain/value-objects/token-amount.vo.ts @@ -664,6 +918,8 @@ export class TokenAmount { } ``` +#### 4.3.3 链类型值对象 + ```typescript // src/domain/value-objects/chain-type.vo.ts @@ -721,7 +977,165 @@ export class ChainType { } ``` -### 4.3 领域事件 +#### 4.3.4 区块号值对象 + +```typescript +// src/domain/value-objects/block-number.vo.ts + +/** + * 区块号值对象 + * + * 封装区块号,确保类型安全和业务规则验证 + */ +export class BlockNumber { + private constructor(private readonly _value: bigint) {} + + get value(): bigint { + return this._value; + } + + /** + * 创建区块号值对象 + * @param value 区块号(支持 number 或 bigint) + * @throws Error 如果区块号为负数 + */ + static create(value: number | bigint): BlockNumber { + const bn = BigInt(value); + if (bn < 0n) { + throw new Error(`Invalid block number: ${value}. Block number cannot be negative.`); + } + return new BlockNumber(bn); + } + + /** + * 计算两个区块之间的差值 + */ + diff(other: BlockNumber): bigint { + return this._value - other._value; + } + + /** + * 检查是否大于另一个区块号 + */ + greaterThan(other: BlockNumber): boolean { + return this._value > other._value; + } + + /** + * 检查是否小于另一个区块号 + */ + lessThan(other: BlockNumber): boolean { + return this._value < other._value; + } + + /** + * 加上指定数量的区块 + */ + add(blocks: number | bigint): BlockNumber { + return new BlockNumber(this._value + BigInt(blocks)); + } + + /** + * 减去指定数量的区块 + */ + subtract(blocks: number | bigint): BlockNumber { + const result = this._value - BigInt(blocks); + if (result < 0n) { + throw new Error('Block number cannot be negative after subtraction'); + } + return new BlockNumber(result); + } + + equals(other: BlockNumber): boolean { + return this._value === other._value; + } + + toString(): string { + return this._value.toString(); + } + + toNumber(): number { + return Number(this._value); + } +} +``` + +#### 4.3.5 交易哈希值对象 + +```typescript +// src/domain/value-objects/tx-hash.vo.ts + +/** + * 交易哈希值对象 + * + * 封装 EVM 交易哈希,确保格式正确(0x + 64位十六进制字符) + */ +export class TxHash { + private static readonly PATTERN = /^0x[a-fA-F0-9]{64}$/; + + private constructor(private readonly _value: string) {} + + get value(): string { + return this._value; + } + + /** + * 获取标准化(小写)的哈希值 + */ + get normalized(): string { + return this._value.toLowerCase(); + } + + /** + * 创建交易哈希值对象 + * @param hash 交易哈希字符串 + * @throws Error 如果哈希格式无效 + */ + static create(hash: string): TxHash { + if (!this.isValid(hash)) { + throw new Error(`Invalid transaction hash: ${hash}. Expected format: 0x + 64 hex characters.`); + } + // 统一存储为小写格式 + return new TxHash(hash.toLowerCase()); + } + + /** + * 验证交易哈希格式是否有效 + */ + static isValid(hash: string): boolean { + return this.PATTERN.test(hash); + } + + /** + * 获取缩短显示格式 (0x1234...abcd) + */ + toShortString(prefixLength = 6, suffixLength = 4): string { + return `${this._value.slice(0, prefixLength + 2)}...${this._value.slice(-suffixLength)}`; + } + + equals(other: TxHash): boolean { + return this._value === other._value; + } + + toString(): string { + return this._value; + } +} +``` + +#### 4.3.6 值对象导出索引 + +```typescript +// src/domain/value-objects/index.ts + +export { ChainType, ChainTypeEnum } from './chain-type.vo'; +export { EvmAddress } from './evm-address.vo'; +export { TokenAmount } from './token-amount.vo'; +export { BlockNumber } from './block-number.vo'; +export { TxHash } from './tx-hash.vo'; +``` + +### 4.4 领域事件 ```typescript // src/domain/events/deposit-detected.event.ts @@ -790,7 +1204,7 @@ export class DepositConfirmedEvent extends DomainEvent { } ``` -### 4.4 仓储接口 (Ports) +### 4.5 仓储接口 (Ports) ```typescript // src/domain/repositories/deposit-transaction.repository.interface.ts @@ -1873,5 +2287,99 @@ for db in rwa_identity rwa_wallet ... rwa_blockchain; do --- -*文档版本: 1.0.0* +## 16. 架构兼容性说明 + +### 16.1 与其他服务的架构对比 + +本服务严格遵循与其他微服务相同的 **DDD + 六边形架构 + 微服务** 设计原则: + +| 特性 | blockchain-service | identity-service | wallet-service | admin-service | +|-----|-------------------|------------------|----------------|---------------| +| **DDD 分层** | ✅ | ✅ | ✅ | ✅ | +| **六边形架构** | ✅ | ✅ | ✅ | ✅ | +| **模块化设计** | ✅ (modules/) | ✅ | ✅ | ✅ | +| **聚合根基类** | ✅ (AggregateRoot) | ✅ | ✅ | ✅ | +| **值对象** | 5个 (ChainType, EvmAddress, TokenAmount, BlockNumber, TxHash) | 2个+ | 5个+ | 7个+ | +| **领域事件** | ✅ | ❌ | ✅ | ❌ | +| **CQRS** | ✅ | ✅ | ✅ | ✅ | +| **Kafka 集成** | ✅ | ✅ | ❌ | ❌ | +| **Redis 缓存** | ✅ | ✅ | ❌ | ❌ | +| **定时任务** | ✅ (@Cron) | ❌ | ❌ | ❌ | + +### 16.2 关键架构原则 + +#### 依赖倒置原则 (Dependency Inversion) + +``` +┌─────────────────────────────────────────────────────────┐ +│ Domain Layer │ +│ ┌─────────────────────────────────────────────────┐ │ +│ │ Repository Interfaces (Ports) │ │ +│ │ - IDepositTransactionRepository │ │ +│ │ - IMonitoredAddressRepository │ │ +│ └─────────────────────────────────────────────────┘ │ +│ ▲ │ +│ │ 依赖 │ +│ │ │ +│ ┌─────────────────────────────────────────────────┐ │ +│ │ Infrastructure Layer │ │ +│ │ Repository Implementations │ │ +│ │ - DepositTransactionRepositoryImpl │ │ +│ │ - MonitoredAddressRepositoryImpl │ │ +│ └─────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────┘ +``` + +#### 六边形架构端口与适配器 + +| 类型 | 端口 (Port) | 适配器 (Adapter) | +|------|------------|------------------| +| **入站 (Driving)** | Controllers | HTTP API | +| **入站 (Driving)** | Kafka Consumer | Event Handler | +| **出站 (Driven)** | Repository Interfaces | Prisma Repositories | +| **出站 (Driven)** | Cache Interface | Redis Cache | +| **出站 (Driven)** | Event Publisher | Kafka Producer | +| **出站 (Driven)** | External Services | HTTP Clients | + +### 16.3 命名约定 + +为与其他服务保持一致,请遵循以下命名约定: + +| 类型 | 命名规则 | 示例 | +|------|---------|------| +| **聚合根** | `*.aggregate.ts` | `deposit-transaction.aggregate.ts` | +| **值对象** | `*.vo.ts` | `chain-type.vo.ts`, `block-number.vo.ts` | +| **领域事件** | `*.event.ts` | `deposit-detected.event.ts` | +| **仓储接口** | `*.repository.interface.ts` | `deposit-transaction.repository.interface.ts` | +| **仓储实现** | `*.repository.impl.ts` | `deposit-transaction.repository.impl.ts` | +| **映射器** | `*.mapper.ts` | `deposit-transaction.mapper.ts` | +| **模块** | `*.module.ts` | `api.module.ts`, `domain.module.ts` | + +### 16.4 Symbol Token 注入规范 + +使用 Symbol 作为依赖注入 Token,与其他服务保持一致: + +```typescript +// 定义 (domain/repositories/index.ts) +export const DEPOSIT_TRANSACTION_REPOSITORY = Symbol('DEPOSIT_TRANSACTION_REPOSITORY'); +export const MONITORED_ADDRESS_REPOSITORY = Symbol('MONITORED_ADDRESS_REPOSITORY'); +export const BLOCK_CHECKPOINT_REPOSITORY = Symbol('BLOCK_CHECKPOINT_REPOSITORY'); + +// 注册 (modules/domain.module.ts) +{ + provide: DEPOSIT_TRANSACTION_REPOSITORY, + useClass: DepositTransactionRepositoryImpl, +} + +// 注入 (application/services/*.ts) +constructor( + @Inject(DEPOSIT_TRANSACTION_REPOSITORY) + private readonly depositRepo: IDepositTransactionRepository, +) {} +``` + +--- + +*文档版本: 1.1.0* *最后更新: 2025-12-03* +*变更说明: 添加模块化设计、聚合根基类、完整值对象定义、架构兼容性说明*