@fluojs/prisma 1.0.2 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +134 -54
- package/README.md +134 -53
- package/dist/module.d.ts +2 -2
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +9 -14
- package/dist/prisma-service-brand.d.ts +13 -0
- package/dist/prisma-service-brand.d.ts.map +1 -0
- package/dist/prisma-service-brand.js +23 -0
- package/dist/service.d.ts +31 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +114 -17
- package/dist/transaction-decorator-contract.notes.d.ts +19 -0
- package/dist/transaction-decorator-contract.notes.d.ts.map +1 -0
- package/dist/transaction-decorator-contract.notes.js +1 -0
- package/dist/transaction.d.ts +34 -10
- package/dist/transaction.d.ts.map +1 -1
- package/dist/transaction.js +86 -8
- package/package.json +6 -6
package/README.ko.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<p><a href="./README.md"><kbd>English</kbd></a> <strong><kbd>한국어</kbd></strong></p>
|
|
4
4
|
|
|
5
|
-
fluo 애플리케이션을 위한 Prisma
|
|
5
|
+
fluo 애플리케이션을 위한 Node.js 20+ Prisma lifecycle 및 ALS 기반 transaction context입니다. `PrismaClient`를 모듈 시스템에 연결하고 자동 연결 관리와 요청 범위 트랜잭션을 제공합니다.
|
|
6
6
|
|
|
7
7
|
## 목차
|
|
8
8
|
|
|
@@ -10,10 +10,10 @@ fluo 애플리케이션을 위한 Prisma 라이프사이클 및 ALS 기반 트
|
|
|
10
10
|
- [사용 시점](#사용-시점)
|
|
11
11
|
- [빠른 시작](#빠른-시작)
|
|
12
12
|
- [공통 패턴](#공통-패턴)
|
|
13
|
-
- [
|
|
13
|
+
- [서비스 트랜잭션 경계 (@Transaction)](#서비스-트랜잭션-경계-transaction)
|
|
14
|
+
- [요청 트랜잭션 인터셉터 호환성](#요청-트랜잭션-인터셉터-호환성)
|
|
14
15
|
- [여러 클라이언트를 위한 이름 있는 등록](#여러-클라이언트를-위한-이름-있는-등록)
|
|
15
|
-
- [수동
|
|
16
|
-
- [자동 요청 트랜잭션](#자동-요청-트랜잭션)
|
|
16
|
+
- [수동 트랜잭션과 current()](#수동-트랜잭션과-current)
|
|
17
17
|
- [종료와 status 계약](#종료와-status-계약)
|
|
18
18
|
- [비동기 설정과 격리](#비동기-설정과-격리)
|
|
19
19
|
- [수동 모듈 조합](#수동-모듈-조합)
|
|
@@ -31,7 +31,7 @@ pnpm add @prisma/client
|
|
|
31
31
|
|
|
32
32
|
## 사용 시점
|
|
33
33
|
|
|
34
|
-
- Prisma를 ORM으로 사용하면서 fluo의 의존성 주입 및 라이프사이클 훅과 통합하고 싶을 때.
|
|
34
|
+
- Node.js 20+에서 Prisma를 ORM으로 사용하면서 fluo의 의존성 주입 및 라이프사이클 훅과 통합하고 싶을 때.
|
|
35
35
|
- 여러 서비스와 리포지토리 사이에서 `tx` 객체를 일일이 전달하지 않고도 트랜잭션 컨텍스트를 안정적으로 공유하고 싶을 때.
|
|
36
36
|
- 애플리케이션 시작 시 자동 `$connect`, 종료 시 자동 `$disconnect`가 필요할 때.
|
|
37
37
|
|
|
@@ -56,33 +56,101 @@ class AppModule {}
|
|
|
56
56
|
|
|
57
57
|
## 공통 패턴
|
|
58
58
|
|
|
59
|
-
###
|
|
59
|
+
### 서비스 트랜잭션 경계 (@Transaction)
|
|
60
60
|
|
|
61
|
-
`
|
|
61
|
+
`@Transaction()` 데코레이터는 서비스 레이어에서 트랜잭션 경계를 정의하는 권장 방법입니다. 이 데코레이터가 적용된 메서드 내부에서 발생하는 모든 리포지토리 호출은 동일한 Prisma 트랜잭션을 공유합니다.
|
|
62
62
|
|
|
63
63
|
```typescript
|
|
64
64
|
import { Inject } from '@fluojs/core';
|
|
65
|
-
import { PrismaService } from '@fluojs/prisma';
|
|
65
|
+
import { PrismaService, Transaction, type PrismaServiceFacade } from '@fluojs/prisma';
|
|
66
66
|
import { PrismaClient } from '@prisma/client';
|
|
67
|
+
import { UserRepository } from './user.repository';
|
|
68
|
+
|
|
69
|
+
export class UserService {
|
|
70
|
+
constructor(private readonly repo: UserRepository) {}
|
|
71
|
+
|
|
72
|
+
@Transaction()
|
|
73
|
+
async onboardUser(dto: CreateUserDto) {
|
|
74
|
+
const user = await this.repo.create(dto);
|
|
75
|
+
await this.repo.initProfile(user.id);
|
|
76
|
+
return user;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
67
79
|
|
|
68
80
|
@Inject(PrismaService)
|
|
69
81
|
export class UserRepository {
|
|
70
|
-
constructor(private readonly prisma:
|
|
82
|
+
constructor(private readonly prisma: PrismaServiceFacade<PrismaClient>) {}
|
|
83
|
+
|
|
84
|
+
async create(data: any) {
|
|
85
|
+
// facade 타입은 표준 PrismaClient delegate를 노출합니다.
|
|
86
|
+
// @Transaction() 내부에서 호출되면 자동으로 활성 트랜잭션에 참여합니다.
|
|
87
|
+
return this.prisma.user.create({ data });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async initProfile(userId: string) {
|
|
91
|
+
return this.prisma.profile.create({ data: { userId } });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`@Transaction()` 메서드 호출은 재진입(reentrant)이 가능합니다. 데코레이터가 적용된 메서드가 다른 데코레이터 적용 메서드를 호출하더라도 하나의 동일한 Prisma 트랜잭션 안에서 실행됩니다.
|
|
97
|
+
|
|
98
|
+
### 요청 트랜잭션 인터셉터 호환성
|
|
99
|
+
|
|
100
|
+
`PrismaTransactionInterceptor`는 기존 `@UseInterceptors(...)` request-wide boundary를 위한 deprecated 1.x 호환성 export로 복원되었습니다. 이름 없는 `PrismaModule.forRoot(...)`와 `forRootAsync(...)` 등록이 이 interceptor를 provider 및 export로 제공하며, `PrismaService.requestTransaction(...)`에 위임하고 request `AbortSignal`을 전달합니다.
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { Controller, Post, UseInterceptors } from '@fluojs/http';
|
|
104
|
+
import { PrismaTransactionInterceptor } from '@fluojs/prisma';
|
|
105
|
+
|
|
106
|
+
@Controller('/orders')
|
|
107
|
+
export class OrdersController {
|
|
108
|
+
@Post('/')
|
|
109
|
+
@UseInterceptors(PrismaTransactionInterceptor)
|
|
110
|
+
createOrder() {
|
|
111
|
+
return this.orders.create();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
새 비즈니스 작업에는 서비스 계층 `@Transaction()`을 우선 사용하세요. 전체 요청에 하나의 트랜잭션이 정말 필요하거나 이름 있는/여러 Prisma 등록에서 특정 서비스를 선택해야 한다면 명시적 `requestTransaction(...)`을 사용하세요. 호환성 interceptor는 이름 없는 기본 등록만 대상으로 합니다.
|
|
117
|
+
|
|
118
|
+
요청 전체 원자성이 정말 필요한 경우에는 application code에서 boundary와 cancellation input을 명시적으로 드러내세요.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { Inject } from '@fluojs/core';
|
|
122
|
+
import { Controller, Post, type RequestContext } from '@fluojs/http';
|
|
123
|
+
import { PrismaService } from '@fluojs/prisma';
|
|
124
|
+
import { PrismaClient } from '@prisma/client';
|
|
71
125
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
126
|
+
@Inject(PrismaService, OrdersService)
|
|
127
|
+
@Controller('/orders')
|
|
128
|
+
export class OrdersController {
|
|
129
|
+
constructor(
|
|
130
|
+
private readonly prisma: PrismaService<PrismaClient>,
|
|
131
|
+
private readonly orders: OrdersService,
|
|
132
|
+
) {}
|
|
133
|
+
|
|
134
|
+
@Post('/checkout')
|
|
135
|
+
checkout(input: CheckoutInput, context: RequestContext) {
|
|
136
|
+
const { request } = context;
|
|
137
|
+
return this.prisma.requestTransaction(
|
|
138
|
+
() => this.orders.checkout(input),
|
|
139
|
+
request.signal,
|
|
140
|
+
);
|
|
75
141
|
}
|
|
76
142
|
}
|
|
77
143
|
```
|
|
78
144
|
|
|
145
|
+
이는 서비스 `@Transaction()`을 대체하는 방식이 아니라 좁은 호환성 패턴입니다. 요청 전체 트랜잭션은 HTTP 작업 전체에서 데이터베이스 lock을 유지할 수 있으므로 boundary를 짧고 명시적으로 유지하세요.
|
|
146
|
+
|
|
79
147
|
### 여러 클라이언트를 위한 이름 있는 등록
|
|
80
148
|
|
|
81
|
-
하나의 애플리케이션 컨테이너 안에서 여러 Prisma Client가 필요하다면 각 등록에 명시적인 `name`을 부여하고 `getPrismaServiceToken(name)`으로 대응되는 토큰을 주입하세요.
|
|
149
|
+
하나의 애플리케이션 컨테이너 안에서 여러 Prisma Client가 필요하다면 각 등록에 명시적인 `name`을 부여하고 `getPrismaServiceToken(name)`으로 대응되는 토큰을 주입하세요. 이름 있는 클라이언트를 사용할 때는 `@Transaction()`에 해당 서비스로 접근할 수 있는 accessor를 전달하세요. 기본 `@Transaction()` 해석은 Prisma service/facade 형태의 속성만 선택합니다. 다른 persistence 통합의 transaction-like 객체는 무시되므로 모호한 host에서는 명시적 accessor를 사용해야 합니다.
|
|
82
150
|
|
|
83
151
|
```typescript
|
|
84
152
|
import { Inject } from '@fluojs/core';
|
|
85
|
-
import { PrismaModule, PrismaService, getPrismaServiceToken } from '@fluojs/prisma';
|
|
153
|
+
import { PrismaModule, PrismaService, getPrismaServiceToken, Transaction, type PrismaServiceFacade } from '@fluojs/prisma';
|
|
86
154
|
|
|
87
155
|
const usersPrismaModule = PrismaModule.forRoot({ name: 'users', client: usersPrisma });
|
|
88
156
|
const analyticsPrismaModule = PrismaModule.forRoot({ name: 'analytics', client: analyticsPrisma });
|
|
@@ -90,64 +158,68 @@ const analyticsPrismaModule = PrismaModule.forRoot({ name: 'analytics', client:
|
|
|
90
158
|
@Inject(getPrismaServiceToken('users'), getPrismaServiceToken('analytics'))
|
|
91
159
|
export class MultiDatabaseService {
|
|
92
160
|
constructor(
|
|
93
|
-
private readonly users:
|
|
94
|
-
private readonly analytics:
|
|
161
|
+
private readonly users: PrismaServiceFacade<typeof usersPrisma>,
|
|
162
|
+
private readonly analytics: PrismaServiceFacade<typeof analyticsPrisma>,
|
|
95
163
|
) {}
|
|
96
164
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
165
|
+
@Transaction((self) => self.users)
|
|
166
|
+
async updateAndLog(userId: string, data: any) {
|
|
167
|
+
const user = await this.users.user.update({ where: { id: userId }, data });
|
|
168
|
+
// 이 호출은 'analytics'가 별도로 트랜잭션을 열지 않는 한 'users' 트랜잭션 밖에 있습니다.
|
|
169
|
+
await this.analytics.report.create({ data: { event: 'update', userId } });
|
|
170
|
+
return user;
|
|
101
171
|
}
|
|
102
172
|
}
|
|
103
173
|
```
|
|
104
174
|
|
|
105
|
-
|
|
175
|
+
### 수동 트랜잭션과 current()
|
|
106
176
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
`prisma.transaction()`을 사용하여 대화형 트랜잭션 블록을 생성합니다. 블록 내부의 모든 `current()` 호출은 트랜잭션 범위의 클라이언트를 사용합니다.
|
|
177
|
+
`PrismaService`는 트랜잭션 범위 내에 있으면 자동으로 트랜잭션용 클라이언트를, 그렇지 않으면 루트 클라이언트를 반환하는 `current()` 메서드를 제공합니다. 외부 라이브러리에 클라이언트를 전달하거나 복잡한 수동 트랜잭션 처리가 필요한 경우 escape hatch로 사용하세요.
|
|
110
178
|
|
|
111
179
|
```typescript
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
});
|
|
116
|
-
```
|
|
180
|
+
import { Inject } from '@fluojs/core';
|
|
181
|
+
import { PrismaService } from '@fluojs/prisma';
|
|
182
|
+
import { PrismaClient } from '@prisma/client';
|
|
117
183
|
|
|
118
|
-
|
|
184
|
+
@Inject(PrismaService)
|
|
185
|
+
export class AdvancedRepository {
|
|
186
|
+
constructor(private readonly prisma: PrismaService<PrismaClient>) {}
|
|
119
187
|
|
|
120
|
-
|
|
188
|
+
async customOperation() {
|
|
189
|
+
const tx = this.prisma.current();
|
|
190
|
+
// fluo가 자동으로 감싸지 않는 작업을 수행하거나,
|
|
191
|
+
// PrismaClient를 직접 기대하는 외부 유틸리티에 전달할 때 tx를 사용하세요.
|
|
192
|
+
return tx.user.findMany();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
121
196
|
|
|
122
|
-
|
|
197
|
+
수동 대화형 트랜잭션 블록에는 `prisma.transaction()`을 사용하세요:
|
|
123
198
|
|
|
124
199
|
```typescript
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
@Post()
|
|
131
|
-
async create() {
|
|
132
|
-
// 이후 PrismaService.current()를 사용하는 모든 리포지토리 호출은 이 트랜잭션을 공유합니다.
|
|
133
|
-
}
|
|
134
|
-
}
|
|
200
|
+
await this.prisma.transaction(async () => {
|
|
201
|
+
const tx = this.prisma.current();
|
|
202
|
+
const user = await tx.user.create({ data });
|
|
203
|
+
await tx.profile.create({ data: { userId: user.id } });
|
|
204
|
+
});
|
|
135
205
|
```
|
|
136
206
|
|
|
137
|
-
|
|
207
|
+
이미 활성 트랜잭션 컨텍스트가 있는 상태에서 `transaction()`을 호출하면 `PrismaService`는 중첩 Prisma 트랜잭션을 새로 열지 않고 활성 트랜잭션 클라이언트를 재사용합니다. 중첩 호출에는 isolation level 같은 트랜잭션 옵션을 전달하면 안 됩니다. 활성 컨텍스트에서 옵션을 제공하면 ambient transaction을 재사용하는 동안 호출자의 의도를 조용히 버리지 않도록 예외로 거부합니다.
|
|
138
208
|
|
|
139
209
|
### 종료와 status 계약
|
|
140
210
|
|
|
141
|
-
`PrismaService.requestTransaction(...)`은 정상 serving 전과 중에는 사용할 수 있지만, 애플리케이션
|
|
211
|
+
`PrismaService.requestTransaction(...)`은 정상 serving 전과 중에는 사용할 수 있지만, 애플리케이션 shutdown이 시작된 뒤에는 새 요청 범위 트랜잭션을 거부합니다. 새 outer 수동 `transaction(...)` 및 서비스 `@Transaction()` boundary도 shutdown 시작 후에는 거부됩니다. 이미 열린 boundary는 `$disconnect()` 전에 drain되므로 shutdown이 활성 Prisma transaction과 경합하지 않습니다. 종료 중에는 열린 요청 트랜잭션을 abort하고, 가장 바깥 transaction boundary가 settle될 때까지 추적한 다음 `$disconnect()` 실행 전에 drain합니다. 기존 수동 `transaction(...)` boundary 안에서 열린 중첩 `requestTransaction(...)` 호출도 동일합니다. 해당 호출은 ambient Prisma transaction client를 재사용하고, 바깥 boundary가 끝날 때까지 `details.activeRequestTransactions`에 표시되며, 두 번째 Prisma transaction을 열지 않습니다.
|
|
142
212
|
|
|
143
213
|
`createPrismaPlatformStatusSnapshot(...)`와 `PrismaService.createPlatformStatusSnapshot()`은 같은 라이프사이클 계약을 진단 surface에 노출합니다.
|
|
144
214
|
|
|
145
|
-
- `readiness.status`는 `onModuleInit()`이 클라이언트를 연결하기 전, Prisma가 종료 중이거나 stopped 상태일 때,
|
|
215
|
+
- `readiness.status`는 `onModuleInit()`이 클라이언트를 연결하기 전, Prisma가 종료 중이거나 stopped 상태일 때, `strictTransactions`가 켜져 있는데 `$transaction(...)`을 지원하지 않을 때, 그리고 클라이언트가 interactive transaction을 지원하지만 호스트 런타임이 `AsyncLocalStorage`를 제공하지 않을 때 `not-ready`입니다. ALS 미지원 상태의 readiness reason은 `Prisma transaction context requires AsyncLocalStorage support from the host runtime.`이며 `details.transactionContext`가 `unavailable`로 보고됩니다. 이 상태는 Prisma 클라이언트 자체는 연결되어 있고 기능적으로 정상일 수 있으므로 일반 database readiness 실패와 구분됩니다.
|
|
146
216
|
- `health.status`는 종료 중 요청 트랜잭션을 drain하는 동안 `degraded`, disconnect 이후 `unhealthy`입니다.
|
|
147
217
|
- `details.activeRequestTransactions`, `details.lifecycleState`, `details.strictTransactions`, `details.supportsTransaction`, `details.transactionAbortSignalSupport`는 현재 요청 트랜잭션과 트랜잭션 capability 상태를 설명합니다.
|
|
148
|
-
- `details.transactionContext: 'als'`는 요청 및 서비스 트랜잭션 경계가 사용하는 async-local transaction context를 식별합니다.
|
|
218
|
+
- `details.transactionContext: 'als'`는 요청 및 서비스 트랜잭션 경계가 사용하는 async-local transaction context를 식별합니다. `details.transactionContext: 'unavailable'`은 호스트 런타임이 사용 가능한 `AsyncLocalStorage`를 노출하지 않았음을 나타내며, 이 경우 `transaction()`과 `requestTransaction()`은 Prisma 트랜잭션을 열기 전에 예외를 던집니다.
|
|
149
219
|
- `ownership.externallyManaged: false`와 `ownership.ownsResources: true`는 패키지가 fluo 애플리케이션 라이프사이클 안에서 등록된 클라이언트의 `$connect()` / `$disconnect()` lifecycle hook을 소유한다는 의미입니다.
|
|
150
220
|
|
|
221
|
+
`details.transactionContext`가 `unavailable`이면 패키지는 동기 stack 기반 컨텍스트로 fallback하지 않습니다. async boundary 사이에서 `current()`를 잃기 때문입니다. fallback boundary는 애플리케이션이 소유합니다. 트랜잭션 컨텍스트 없이도 데이터베이스 접근이 필요한 호출자는 (예: `PRISMA_CLIENT` 토큰을 통해) 원시 `PrismaClient`를 직접 호출하고 자체 일관성 semantics를 관리하거나, `AsyncLocalStorage`를 제공하는 호스트 런타임(Node.js 20+가 문서화된 경로)에서 실행해야 합니다. `unavailable` readiness 상태는 운영적으로 실행 가능한 신호로 취급하세요. health check에 노출하고, 호스트가 ALS를 제공하거나 애플리케이션이 비트랜잭션 접근 경로로 전환할 때까지 트랜잭션 의존 handler로 트래픽을 라우팅하지 마세요.
|
|
222
|
+
|
|
151
223
|
### 비동기 설정과 격리
|
|
152
224
|
|
|
153
225
|
주입된 설정이나 다른 비동기 소스에서 Prisma 클라이언트를 만들어야 할 때는 `PrismaModule.forRootAsync(...)`를 사용하세요. 비동기 factory는 애플리케이션 컨테이너마다 한 번 resolve되며, 테스트나 여러 앱을 띄우는 프로세스에서 같은 모듈 정의를 재사용하더라도 별도 bootstrap 사이에서 공유되지 않습니다.
|
|
@@ -167,7 +239,7 @@ PrismaModule.forRootAsync({
|
|
|
167
239
|
|
|
168
240
|
하나의 컴파일된 애플리케이션 안에서는 하위 provider가 동일하게 resolve된 `PrismaService`, ALS 트랜잭션 컨텍스트, 라이프사이클 관리 대상 클라이언트를 공유합니다. 서로 다른 애플리케이션 컨테이너는 독립된 factory 결과를 받으므로 `$connect` / `$disconnect` 소유권과 요청 트랜잭션 상태가 격리됩니다.
|
|
169
241
|
|
|
170
|
-
트랜잭션 경계에는 호스트가 제공하는 `AsyncLocalStorage` 지원이 필요합니다. `@fluojs/prisma`는 런타임이 노출하는 `globalThis.AsyncLocalStorage` 또는 Node.js의 `process.getBuiltinModule('node:async_hooks')` 호스트 경계를 통해
|
|
242
|
+
트랜잭션 경계에는 호스트가 제공하는 `AsyncLocalStorage` 지원이 필요합니다. 패키지 manifest는 `engines.node >=20.0.0`을 선언하며, root wrapper는 문서화된 Node.js 20+ Prisma 통합 경로입니다. `@fluojs/prisma`는 런타임이 노출하는 `globalThis.AsyncLocalStorage` 또는 Node.js의 `process.getBuiltinModule('node:async_hooks')` 호스트 경계를 통해 ALS를 resolve합니다. 두 경로 모두 사용할 수 없거나 host builtin lookup이 실패하면 동기 stack fallback으로 async boundary 사이의 `current()`를 잃는 대신, Prisma 트랜잭션을 열기 전에 `transaction()`과 `requestTransaction()`이 예외를 던집니다. 이 상태는 `createPlatformStatusSnapshot().details.transactionContext`에 `unavailable`로 보고됩니다.
|
|
171
243
|
|
|
172
244
|
### 수동 모듈 조합
|
|
173
245
|
|
|
@@ -175,7 +247,7 @@ PrismaModule.forRootAsync({
|
|
|
175
247
|
|
|
176
248
|
```typescript
|
|
177
249
|
import { defineModule } from '@fluojs/runtime';
|
|
178
|
-
import { PrismaModule
|
|
250
|
+
import { PrismaModule } from '@fluojs/prisma';
|
|
179
251
|
import { PrismaClient } from '@prisma/client';
|
|
180
252
|
|
|
181
253
|
const prisma = new PrismaClient();
|
|
@@ -183,7 +255,6 @@ const prisma = new PrismaClient();
|
|
|
183
255
|
class ManualPrismaModule {}
|
|
184
256
|
|
|
185
257
|
defineModule(ManualPrismaModule, {
|
|
186
|
-
exports: [PrismaService, PrismaTransactionInterceptor],
|
|
187
258
|
imports: [PrismaModule.forRoot({ client: prisma })],
|
|
188
259
|
});
|
|
189
260
|
```
|
|
@@ -193,7 +264,7 @@ defineModule(ManualPrismaModule, {
|
|
|
193
264
|
### `PrismaModule`
|
|
194
265
|
|
|
195
266
|
- `PrismaModule.forRoot(options)` / `PrismaModule.forRootAsync(options)`
|
|
196
|
-
- `forRoot(...)`와 `forRootAsync(...)`도 이름 있는/scoped 등록을 위해 `name`을 받을 수 있습니다.
|
|
267
|
+
- `forRoot(...)`와 `forRootAsync(...)`도 이름 있는/scoped 등록을 위해 `name`을 받을 수 있으며, 이름 없는 등록을 전역 provider로 export해야 할 때 `global?: boolean`을 받을 수 있습니다.
|
|
197
268
|
- `forRootAsync(...)`는 client와 transaction 설정을 factory에서 반환하는 DI-aware Prisma 옵션을 받습니다. 모듈 identity와 visibility가 factory 실행 전에 결정되도록 `name` 또는 `global`은 최상위 async 등록 옵션에 전달하세요.
|
|
198
269
|
- `forRootAsync(...)`는 애플리케이션 컨테이너마다 옵션을 한 번 resolve하여, 별도 bootstrap 사이에서 클라이언트 라이프사이클과 요청 트랜잭션 격리를 보존합니다.
|
|
199
270
|
- `strictTransactions: true` 설정 시 트랜잭션 미지원 환경에서 즉시 예외를 발생시킵니다.
|
|
@@ -206,13 +277,21 @@ defineModule(ManualPrismaModule, {
|
|
|
206
277
|
- `current(): TClient | PrismaTransactionClient<TClient>`
|
|
207
278
|
- 현재 컨텍스트에 맞는 트랜잭션 클라이언트 또는 루트 클라이언트를 반환합니다.
|
|
208
279
|
- `transaction(fn, options?): Promise<T>`
|
|
209
|
-
- 대화형 트랜잭션 내에서 함수를 실행합니다. 이미 트랜잭션 컨텍스트가 활성화되어 있으면 callback은 그 컨텍스트를 재사용하며, 새 Prisma 트랜잭션 경계가 열리지 않기 때문에 중첩 트랜잭션 옵션은 거부됩니다.
|
|
280
|
+
- 대화형 트랜잭션 내에서 함수를 실행합니다. 이미 트랜잭션 컨텍스트가 활성화되어 있으면 callback은 그 컨텍스트를 재사용하며, 새 Prisma 트랜잭션 경계가 열리지 않기 때문에 중첩 트랜잭션 옵션은 거부됩니다. shutdown이 시작된 뒤에는 새 outer transaction boundary를 거부합니다.
|
|
210
281
|
- `requestTransaction(fn, signal?, options?): Promise<T>`
|
|
211
282
|
- HTTP 요청 라이프사이클에 특화된 트랜잭션 경계를 실행합니다. Abort를 인식하고, shutdown 중에는 disconnect 전에 열린 요청 트랜잭션을 drain하며, Prisma client가 `signal` 옵션을 거부하면 해당 옵션 없이 재시도합니다. `transaction()`과 마찬가지로 중첩 호출은 활성 트랜잭션 컨텍스트를 재사용하고, 트랜잭션 설정을 조용히 무시하지 않도록 중첩 옵션을 거부합니다.
|
|
212
283
|
|
|
213
|
-
|
|
284
|
+
Provider가 `current()`, `transaction(...)`, `requestTransaction(...)`, `createPlatformStatusSnapshot()` 같은 wrapper 메서드만 필요로 한다면 `PrismaService<TClient>`를 사용하세요. 생성된 Prisma Client delegate를 직접 호출하는 repository 주입에는 `PrismaServiceFacade<TClient>`를 사용하세요. 이 facade는 활성 트랜잭션이 있으면 해당 트랜잭션 client로, 없으면 root client로 호출을 전달합니다. `PrismaService.createFacade(...)`는 module-provider wiring을 위한 저수준 compatibility helper로 유지되며, 애플리케이션 코드는 `PrismaModule.forRoot(...)` / `forRootAsync(...)`를 우선 사용해야 합니다.
|
|
285
|
+
|
|
286
|
+
### `Transaction`
|
|
287
|
+
|
|
288
|
+
- 서비스 계층 트랜잭션 경계를 위한 표준 TC39 method decorator입니다. 기본적으로 Prisma service/facade 형태의 속성을 resolve하고, 이름 있는 client나 모호한 host에는 accessor를 받을 수 있으며, 외부 경계에는 Prisma transaction option을 전달할 수 있습니다.
|
|
289
|
+
|
|
290
|
+
### `PrismaTransactionInterceptor` (deprecated 호환성)
|
|
214
291
|
|
|
215
|
-
-
|
|
292
|
+
- 기존 1.x import를 위한 request-wide HTTP 호환성 interceptor입니다.
|
|
293
|
+
- `PrismaService.requestTransaction(...)`에 위임하고 request cancellation을 전달합니다.
|
|
294
|
+
- 새 코드에서는 서비스 `@Transaction()` 또는 명시적 request boundary를 우선 사용하세요.
|
|
216
295
|
|
|
217
296
|
### `PRISMA_CLIENT` (Token)
|
|
218
297
|
|
|
@@ -241,6 +320,7 @@ defineModule(ManualPrismaModule, {
|
|
|
241
320
|
- `PrismaModuleOptions`
|
|
242
321
|
- `PrismaClientLike`
|
|
243
322
|
- `PrismaHandleProvider`
|
|
323
|
+
- `PrismaServiceFacade<TClient>`
|
|
244
324
|
- `PrismaTransactionClient<TClient>`
|
|
245
325
|
- `InferPrismaTransactionClient<TClient>`
|
|
246
326
|
- `InferPrismaTransactionOptions<TClient>`
|
|
@@ -248,7 +328,7 @@ defineModule(ManualPrismaModule, {
|
|
|
248
328
|
## 관련 패키지
|
|
249
329
|
|
|
250
330
|
- `@fluojs/runtime`: 애플리케이션 라이프사이클 훅을 관리합니다.
|
|
251
|
-
- `@fluojs/http`:
|
|
331
|
+
- `@fluojs/http`: 명시적 `requestTransaction(...)` 경계와 함께 사용할 수 있는 요청 라이프사이클 primitive를 제공합니다.
|
|
252
332
|
- `@fluojs/terminus`: Prisma를 위한 헬스 인디케이터를 제공합니다.
|
|
253
333
|
|
|
254
334
|
## 예제 소스
|