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:
parent
08541f1d8d
commit
cd4fba96ed
|
|
@ -97,6 +97,12 @@ blockchain-service/
|
||||||
├── prisma/
|
├── prisma/
|
||||||
│ └── schema.prisma
|
│ └── schema.prisma
|
||||||
├── src/
|
├── src/
|
||||||
|
│ ├── modules/ # NestJS 模块定义 (与其他服务保持一致)
|
||||||
|
│ │ ├── api.module.ts # API 层模块
|
||||||
|
│ │ ├── application.module.ts # 应用层模块
|
||||||
|
│ │ ├── domain.module.ts # 领域层模块
|
||||||
|
│ │ └── infrastructure.module.ts # 基础设施层模块
|
||||||
|
│ │
|
||||||
│ ├── api/ # 入站适配器 (Driving Adapters)
|
│ ├── api/ # 入站适配器 (Driving Adapters)
|
||||||
│ │ ├── controllers/
|
│ │ ├── controllers/
|
||||||
│ │ │ ├── health.controller.ts
|
│ │ │ ├── health.controller.ts
|
||||||
|
|
@ -131,6 +137,7 @@ blockchain-service/
|
||||||
│ │
|
│ │
|
||||||
│ ├── domain/ # 领域层 (核心业务)
|
│ ├── domain/ # 领域层 (核心业务)
|
||||||
│ │ ├── aggregates/
|
│ │ ├── aggregates/
|
||||||
|
│ │ │ ├── aggregate-root.base.ts # 聚合根基类 (统一事件管理)
|
||||||
│ │ │ ├── deposit-transaction/
|
│ │ │ ├── deposit-transaction/
|
||||||
│ │ │ │ ├── deposit-transaction.aggregate.ts
|
│ │ │ │ ├── deposit-transaction.aggregate.ts
|
||||||
│ │ │ │ ├── deposit-transaction.factory.ts
|
│ │ │ │ ├── deposit-transaction.factory.ts
|
||||||
|
|
@ -184,7 +191,9 @@ blockchain-service/
|
||||||
│ │ │ │ ├── monitored-address.repository.impl.ts
|
│ │ │ │ ├── monitored-address.repository.impl.ts
|
||||||
│ │ │ │ └── block-checkpoint.repository.impl.ts
|
│ │ │ │ └── block-checkpoint.repository.impl.ts
|
||||||
│ │ │ └── mappers/
|
│ │ │ └── mappers/
|
||||||
│ │ │ └── deposit-transaction.mapper.ts
|
│ │ │ ├── deposit-transaction.mapper.ts
|
||||||
|
│ │ │ ├── monitored-address.mapper.ts
|
||||||
|
│ │ │ └── transaction-request.mapper.ts
|
||||||
│ │ ├── kafka/
|
│ │ ├── kafka/
|
||||||
│ │ │ ├── event-publisher.service.ts
|
│ │ │ ├── event-publisher.service.ts
|
||||||
│ │ │ ├── event-consumer.controller.ts
|
│ │ │ ├── event-consumer.controller.ts
|
||||||
|
|
@ -239,6 +248,198 @@ blockchain-service/
|
||||||
└── DEVELOPMENT_GUIDE.md
|
└── 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. 数据模型设计
|
## 3. 数据模型设计
|
||||||
|
|
@ -424,11 +625,66 @@ model BlockchainEvent {
|
||||||
|
|
||||||
## 4. 领域层设计
|
## 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
|
```typescript
|
||||||
// src/domain/aggregates/deposit-transaction/deposit-transaction.aggregate.ts
|
// src/domain/aggregates/deposit-transaction/deposit-transaction.aggregate.ts
|
||||||
|
|
||||||
|
import { AggregateRoot } from '../aggregate-root.base';
|
||||||
import { DomainEvent, DepositDetectedEvent, DepositConfirmedEvent } from '@/domain/events';
|
import { DomainEvent, DepositDetectedEvent, DepositConfirmedEvent } from '@/domain/events';
|
||||||
import { ChainType, TxHash, EvmAddress, TokenAmount, BlockNumber } from '@/domain/value-objects';
|
import { ChainType, TxHash, EvmAddress, TokenAmount, BlockNumber } from '@/domain/value-objects';
|
||||||
import { DepositStatus } from '@/domain/enums';
|
import { DepositStatus } from '@/domain/enums';
|
||||||
|
|
@ -455,10 +711,10 @@ export interface DepositTransactionProps {
|
||||||
updatedAt?: Date;
|
updatedAt?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DepositTransaction {
|
export class DepositTransaction extends AggregateRoot<bigint> {
|
||||||
private readonly _domainEvents: DomainEvent[] = [];
|
private constructor(private props: DepositTransactionProps) {
|
||||||
|
super();
|
||||||
private constructor(private props: DepositTransactionProps) {}
|
}
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
get id(): bigint | undefined { return this.props.id; }
|
get id(): bigint | undefined { return this.props.id; }
|
||||||
|
|
@ -569,13 +825,7 @@ export class DepositTransaction {
|
||||||
this.props.lastNotifyError = error;
|
this.props.lastNotifyError = error;
|
||||||
}
|
}
|
||||||
|
|
||||||
private addDomainEvent(event: DomainEvent): void {
|
// 注意:addDomainEvent 和 clearDomainEvents 方法已在 AggregateRoot 基类中定义
|
||||||
this._domainEvents.push(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
clearDomainEvents(): void {
|
|
||||||
this._domainEvents.length = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
toProps(): DepositTransactionProps {
|
toProps(): DepositTransactionProps {
|
||||||
return { ...this.props };
|
return { ...this.props };
|
||||||
|
|
@ -583,7 +833,9 @@ export class DepositTransaction {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4.2 值对象
|
### 4.3 值对象
|
||||||
|
|
||||||
|
#### 4.3.1 EVM 地址值对象
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// src/domain/value-objects/evm-address.vo.ts
|
// src/domain/value-objects/evm-address.vo.ts
|
||||||
|
|
@ -618,6 +870,8 @@ export class EvmAddress {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### 4.3.2 代币金额值对象
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// src/domain/value-objects/token-amount.vo.ts
|
// src/domain/value-objects/token-amount.vo.ts
|
||||||
|
|
||||||
|
|
@ -664,6 +918,8 @@ export class TokenAmount {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### 4.3.3 链类型值对象
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// src/domain/value-objects/chain-type.vo.ts
|
// 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
|
```typescript
|
||||||
// src/domain/events/deposit-detected.event.ts
|
// src/domain/events/deposit-detected.event.ts
|
||||||
|
|
@ -790,7 +1204,7 @@ export class DepositConfirmedEvent extends DomainEvent {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4.4 仓储接口 (Ports)
|
### 4.5 仓储接口 (Ports)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// src/domain/repositories/deposit-transaction.repository.interface.ts
|
// 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*
|
*最后更新: 2025-12-03*
|
||||||
|
*变更说明: 添加模块化设计、聚合根基类、完整值对象定义、架构兼容性说明*
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue