728x90
Prisma에서 트랜잭션을 처리하기 위해서는 Service 계층에 Prisma 관련 코드가 필요하게 됩니다. 하지만 Prisma 트랜잭션을 직접 다루면 Service 계층이 ORM에 의존하게 됩니다. 이를 해결하기 위한 방안으로 트랜잭션 관련 로직을 추상화하는 TransactionManager를 고민해보았습니다.
Prisma Interactive Transaction
Prisma에서 트랜잭션을 처리하는 방식으로, prismaClient.$transaction()을 활용한 Interactive Transaction 방식을 사용할 수 있습니다. 이를 통해 트랜잭션을 관리하며, 트랜잭션 내에서 여러 쿼리를 처리하는 것이 가능합니다.
예시 코드는 다음과 같습니다
// prisma.client.ts
export const prismaClient = new PrismaClient();
// user.service.ts
async function createUser() {
return await prisma.$transaction(async (tx) => {
await tx.user.create(user);
if (!user) {
throw new Error();
}
await tx.token.create(user);
});
}
TransactionManager
TransactionManager를 도입하면 트랜잭션 관련 코드가 별도의 클래스로 분리되어 Service 계층의 순수성을 유지할 수 있습니다. 다만 코드가 복잡해지고, 모든 repository 코드에 tx 파라미터를 전달해야 한다는 단점이 있습니다.
다음은 TransactionManager를 적용한 예시입니다:
// transaction.manager.ts
export default interface TransactionManager {
execute<T>(callback: (tx: unknown) => Promise<T>): Promise<T>;
}
export const transactionManager = new PrismaTransactionManager();
// prisma.client.ts
export const prismaClient = new PrismaClient();
export const getPrismaClient = (tx?: Prisma.TransactionClient) => {
return tx || prismaClient;
};
export class PrismaTransactionManager implements TransactionManager {
public async execute<T>(
callback: (tx: Prisma.TransactionClient) => Promise<T>,
): Promise<T> {
return await prismaClient.$transaction(async (tx) => {
return await callback(tx);
});
}
}
// user.repository.ts
const createUser = async (
data: Prisma.UserCreateInput,
tx?: Prisma.TransactionClient,
) => {
const prisma = getPrismaClient(tx);
return await prisma.user.create({ data });
};
// user.service.ts
async function createUser() {
return transactionManager.execute(async (tx) => {
await userRepository(user, tx);
if (!user) {
throw new Error();
}
await tokenRepository(token, tx);
});
반응형
'NestJS & Fastify' 카테고리의 다른 글
[NestJS] Interceptor를 사용해서 Transaction 분리하기 (0) | 2025.03.16 |
---|