docs(blockchain-service): 完善架构设计以兼容其他微服务

- 添加 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 <noreply@anthropic.com>
This commit is contained in:
Developer 2025-12-03 20:44:24 -08:00
parent 08541f1d8d
commit cd4fba96ed
1 changed files with 525 additions and 17 deletions

View File

@ -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<TId = bigint> {
private readonly _domainEvents: DomainEvent[] = [];
/**
* 聚合根唯一标识
*/
abstract get id(): TId | undefined;
/**
* 获取所有待发布的领域事件
*/
get domainEvents(): ReadonlyArray<DomainEvent> {
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<bigint> {
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*
*变更说明: 添加模块化设计、聚合根基类、完整值对象定义、架构兼容性说明*