npm package: @nest-omni/transaction
Description: Transaction management library for NestJS + TypeORM with AsyncLocalStorage support
Author: Nest Omni
Latest version: 1.0.1

# @nest-omni/transaction

Transaction management library for NestJS + TypeORM with `AsyncLocalStorage` support.

## Installation

```bash
npm install @nest-omni/transaction
npm install typeorm reflect-metadata
```

```bash
yarn add @nest-omni/transaction
yarn add typeorm reflect-metadata
```

## Quick Start

### 1. Register Module

Register `TransactionModule` in your `AppModule`. It automatically discovers all TypeORM DataSources:

```typescript
// app.module.ts
import { Module } from '@nestjs/common';
import { TransactionModule } from '@nest-omni/transaction';

@Module({
  imports: [
    TransactionModule.forRoot(),
    // ... 其他模块
  ],
})
export class AppModule {}
```

在入口文件导入 `reflect-metadata`：

```typescript
// main.ts
import 'reflect-metadata';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();
```

### 2. Basic Usage

```typescript
import { Injectable } from '@nestjs/common';
import { Transactional } from '@nest-omni/transaction';
import { User } from './user.entity';
import { DataSource } from 'typeorm';

@Injectable()
export class UserService {
  constructor(
    private readonly dataSource: DataSource,
  ) {}

  @Transactional()
  async createUser(name: string, email: string) {
    // 使用 QueryRunner 自动管理事务
    await this.dataSource.query(
      'INSERT INTO user (name, email) VALUES (?, ?)',
      [name, email]
    );

    // Repository 也在同一事务中
    const user = this.dataSource.getRepository(User).create({ name, email });
    await this.dataSource.getRepository(User).save(user);
  }
}
```

### 3. Transaction Propagation

```typescript
@Injectable()
export class OrderService {
  constructor(
    private readonly userService: UserService,
    private readonly productService: ProductService,
  ) {}

  @Transactional()
  async createOrder(userId: string, productId: string) {
    // REQUIRED: 加入当前事务，如果没有则创建新事务
    await this.userService.updateBalance(userId, -100);

    // REQUIRES_NEW: 总是创建新事务
    await this.productService.recordSale(productId);

    // NESTED: 创建嵌套事务，回滚不影响父事务
    await this.notifyUser(userId);
  }

  @Transactional({ propagation: Propagation.REQUIRES_NEW })
  async recordSale(productId: string) {
    // 独立事务
  }

  @Transactional({ propagation: Propagation.NESTED })
  async notifyUser(userId: string) {
    // 嵌套事务，回滚不会导致 createOrder 回滚
  }
}
```

## Multiple DataSources

```typescript
@Module({
  imports: [
    TransactionModule.forRoot(),

    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'localhost',
      port: 5432,
      database: 'main_db',
    }),

    TypeOrmModule.forRoot({
      name: 'log-db',
      type: 'postgres',
      host: 'localhost',
      port: 5433,
      database: 'log_db',
    }),
  ],
})
export class AppModule {}
```

指定数据源：

```typescript
@Injectable()
export class LogService {
  @Transactional({ connectionName: 'log-db' })
  async writeLog(message: string) {
    // 使用 log-db 数据源
  }
}
```

## Transaction Hooks

### Method 1: Using Listeners

```typescript
import { Injectable } from '@nestjs/common';
import { TransactionEventListener } from '@nest-omni/transaction';

@Injectable()
export class OrderEventListener implements TransactionEventListener {
  async onCommit(...args: unknown[]) {
    // Execute after transaction commit
    console.log('Order created successfully', args);
  }

  async onRollback(error: Error, ...args: unknown[]) {
    // Execute after transaction rollback
    console.error('Order creation failed', error);
  }
}
```

```typescript
@Injectable()
export class OrderService {
  @Transactional()
  @TransactionalEventListeners(OrderEventListener)
  async createOrder(data: OrderDto) {
    // 业务逻辑
  }
}
```

### Method 2: Using Hook Functions

```typescript
import { runOnTransactionCommit, runOnTransactionRollback } from '@nest-omni/transaction';

@Injectable()
export class OrderService {
  @Transactional()
  async createOrder(data: OrderDto) {
    // 业务逻辑...

    // 提交后执行
    runOnTransactionCommit(async () => {
      await this.emailService.sendConfirmation();
    });

    // 回滚后执行
    runOnTransactionRollback(async (error) => {
      await this.loggingService.logError(error);
    });
  }
}
```

## Transaction Options

```typescript
@Transactional({
  // DataSource name
  connectionName?: string;

  // Isolation level
  isolationLevel?: IsolationLevel;

  // Propagation behavior
  propagation?: Propagation;
})
```

### Propagation

| Value | Description |
|---|---|
| `REQUIRED` | Default. Join existing transaction, or create new if none |
| `SUPPORTS` | Join existing transaction, or execute non-transactional if none |
| `NESTED` | Nested transaction, rollback doesn't affect parent transaction |
| `REQUIRES_NEW` | Always create new transaction, suspend current transaction |

### Isolation Level

| Value | Description |
|---|---|
| `READ_UNCOMMITTED` | Read uncommitted |
| `READ_COMMITTED` | Read committed (default) |
| `REPEATABLE_READ` | Repeatable read |
| `SERIALIZABLE` | Serializable |

## Integration Testing

Use `TestTransactionModule` for automatic rollback in tests:

```typescript
import { Test, TestingModule } from '@nestjs/testing';
import { TestTransactionModule, getTestQueryRunnerToken } from '@nest-omni/transaction';
import { DataSource } from 'typeorm';
import { QueryRunner } from 'typeorm';

describe('OrderService', () => {
  let app: INestApplication;
  let testQueryRunner: QueryRunner;

  beforeAll(async () => {
    const module = await Test.createTestingModule({
      imports: [AppModule, TestTransactionModule.forRoot()],
    }).compile();

    app = module.createNestApplication();
    await app.init();

    testQueryRunner = app.get<QueryRunner>(getTestQueryRunnerToken());
  });

  beforeEach(async () => {
    await testQueryRunner.startTransaction();
  });

  afterEach(async () => {
    await testQueryRunner.rollbackTransaction();
  });

  afterAll(async () => {
    await app.close();
  });

  it('should create order', async () => {
    // Test with automatic rollback
  });
});
```

## API

```typescript
// 装饰器
@Transactional(options?: TransactionOptions)
@TransactionalEventListeners(...listeners: Type<TransactionEventListener>[])

// 函数
runInTransaction(fn, options?)
wrapInTransaction(fn, options?)
runOnTransactionCommit(callback)
runOnTransactionRollback(callback)

// 类/接口
TransactionModule
TestTransactionModule
TransactionalError
NoRegisteredDataSourceError
NotRollBackError
TransactionEventListener
```

## License

MIT