Quando se trata de sistemas financeiros, a integridade de dados é uma das preocupações mais cruciais. Em um ambiente de transações financeiras, falhas podem resultar em perda de dinheiro, inconsistência no saldo das contas ou até mesmo falhas de segurança. Para garantir que os dados permaneçam consistentes e seguros, o uso de transações no banco de dados é fundamental. Neste artigo, vamos explorar como as transações no PostgreSQL ajudam a resolver esses problemas, com exemplos práticos usando NestJS e Prisma para implementar soluções robustas.
O Que São Transações Bancárias?
Em um contexto bancário, uma transação geralmente envolve a movimentação de dinheiro entre contas ou a atualização de saldos. A atomicidade de uma transação é essencial para garantir que todos os passos necessários para completar a operação sejam realizados com sucesso, e que, caso ocorra algum erro em qualquer etapa, todo o processo seja revertido para evitar inconsistências.
Por exemplo, ao transferir dinheiro de uma conta para outra, a transação envolve:
- Subtrair o valor da conta de origem.
- Adicionar o valor à conta de destino.
Se uma dessas etapas falhar, como uma falha de rede ou erro de sistema, o saldo de uma conta pode ser atualizado sem a outra, levando a dados inconsistentes. Transações são usadas para garantir que ambas as ações ocorram ou nenhuma delas, mantendo a integridade dos dados.
A Solução: Transações no PostgreSQL
O PostgreSQL é um banco de dados relacional que suporta transações de forma nativa, garantindo que operações de banco de dados sejam atômicas, consistentes, isoladas e duráveis — características conhecidas pela sigla ACID (Atomicidade, Consistência, Isolamento e Durabilidade). Usando transações, podemos garantir que todas as etapas de uma operação bancária sejam concluídas com sucesso ou, em caso de erro, revertidas.
Como Funciona uma Transação no PostgreSQL?
Em termos simples, uma transação no PostgreSQL começa com a instrução BEGIN
e termina com COMMIT
. Se algum erro ocorrer durante a transação, o comando ROLLBACK
é chamado para reverter todas as alterações feitas até aquele ponto. Aqui está um exemplo básico de uma transação SQL no PostgreSQL:
BEGIN;
UPDATE contas SET saldo = saldo - 100 WHERE id = 1; -- Subtrai 100 da conta de origem
UPDATE contas SET saldo = saldo + 100 WHERE id = 2; -- Adiciona 100 na conta de destino
COMMIT; -- Se tudo ocorrer bem, confirma as alterações
Se algo der errado, como a conta de origem não ter saldo suficiente ou uma falha no banco de dados, um ROLLBACK
pode ser acionado para garantir que nenhuma das alterações seja aplicada.
Exemplo Prático Usando NestJS e Prisma
Agora que entendemos o conceito de transações, vamos integrar isso com NestJS e Prisma.
Configuração do Prisma com PostgreSQL
Primeiro, vamos configurar o Prisma para trabalhar com PostgreSQL no seu projeto NestJS. Certifique-se de que o Prisma está configurado corretamente no seu projeto.
- Instale o Prisma e as dependências:
npm install @prisma/client
npm install prisma --save-dev
-
Configure o Prisma (no arquivo
prisma/schema.prisma
):
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Conta {
id Int @id @default(autoincrement())
saldo Decimal @default(0) @db.Decimal(10, 2)
}
- Execute a migração para aplicar as mudanças no banco de dados:
npx prisma migrate dev --name init
Implementando Transações no NestJS com Prisma
No NestJS, podemos usar o Prisma para gerenciar transações dentro dos serviços. Vamos criar um serviço que simula a transferência de fundos entre contas. Usaremos o método prisma.$transaction
para garantir que a transferência seja atômica.
Código do Serviço
import { Injectable } from '@nestjs/common';
import { PrismaService } from './prisma.service'; // Serviço Prisma
import { Prisma } from '@prisma/client';
@Injectable()
export class TransacoesService {
constructor(private prisma: PrismaService) {}
async transferirFundos(contaOrigemId: number, contaDestinoId: number, valor: number) {
const contaOrigem = await this.prisma.conta.findUnique({ where: { id: contaOrigemId } });
const contaDestino = await this.prisma.conta.findUnique({ where: { id: contaDestinoId } });
if (!contaOrigem || !contaDestino) {
throw new Error('Conta não encontrada');
}
if (contaOrigem.saldo < valor) {
throw new Error('Saldo insuficiente');
}
// Iniciando a transação
return this.prisma.$transaction(async (prisma) => {
// Subtrai da conta de origem
await prisma.conta.update({
where: { id: contaOrigemId },
data: { saldo: contaOrigem.saldo - valor },
});
// Adiciona na conta de destino
await prisma.conta.update({
where: { id: contaDestinoId },
data: { saldo: contaDestino.saldo + valor },
});
});
}
}
Explicação do Código:
-
prisma.$transaction
: Este método é usado para envolver as operações de banco de dados dentro de uma transação. Se qualquer parte da transação falhar, todas as alterações feitas dentro da transação serão revertidas automaticamente. - Validação: Antes de iniciar a transação, validamos se as contas existem e se há saldo suficiente na conta de origem.
-
Operações atômicas: Usamos o
prisma.conta.update
para atualizar os saldos das contas. Ambas as operações de subtração e adição de saldo acontecem dentro da mesma transação, garantindo que ambas ocorram com sucesso ou que nenhuma delas aconteça se um erro ocorrer.
Benefícios das Transações para Sistemas Bancários
Ao usar transações no PostgreSQL, garantimos:
- Atomicidade: Se algo der errado, como saldo insuficiente ou erro de banco de dados, a transação inteira será revertida e nenhuma alteração será persistida.
- Consistência: Após a transação, o banco de dados estará em um estado consistente, sem saldo incorreto ou dados duplicados.
- Isolamento: As transações são isoladas umas das outras, o que significa que transações concorrentes não interferem umas nas outras.
-
Durabilidade: Uma vez que a transação é confirmada com
COMMIT
, os dados são garantidos como persistentes, mesmo em caso de falha de sistema.
Considerações Finais
O uso de transações no PostgreSQL é essencial para garantir a integridade de dados em sistemas financeiros. Transações asseguram que operações críticas, como transferências de fundos, sejam realizadas de forma atômica e segura.
Ao adotar transações adequadas e seguir boas práticas de manipulação de dados, é possível minimizar riscos, como perda de dados e inconsistências, oferecendo uma experiência segura e confiável para os usuários do sistema bancário.
Top comments (0)