Skip to main content

Refatorando camadas de acesso a dados

O Copilot Chat pode sugerir maneiras de desacoplar o código de acesso a dados da lógica de negócios, facilitando a manutenção e a escala de um aplicativo.

Consultas SQL embutidas em código e código de acesso a dados fortemente acoplado podem dificultar a escala ou a troca de bancos de dados, e geralmente levam a lógica repetida.

O Copilot Chat pode recomendar a implementação de um padrão de repositório ou uma camada de acesso a dados que abstrai as interações de banco de dados, tornando o código mais modular e reduzindo a redundância. Ele também pode ajudar a refatorar consultas SQL em versões otimizadas, aprimorando o desempenho e a capacidade de manutenção.

Note

As respostas descritas neste artigo são exemplos. Respostas do Copilot Chat são não determinísticas, portanto, você pode obter respostas diferentes das mostradas aqui.

Cenário de exemplo

Este código Python se conecta a um banco de dados SQLite, recupera um registro de usuário e retorna os dados do usuário. No entanto, ele não abstrai a lógica de conexão ao banco de dados e usa uma consulta embutida em código vulnerável à injeção de SQL.

import sqlite3

def get_user_by_id(user_id):
    conn = sqlite3.connect('database.db')
    cursor = conn.cursor()
    cursor.execute(f"SELECT display_name FROM users WHERE id = {user_id}")
    user = cursor.fetchone()
    conn.close()
    return user

Exemplo de prompt 1

Você pode começar fazendo uma pergunta geral ao Copilot sobre como aprimorar o código.

How can I improve this code to make it safe and easier to update and expand? List possible improvements but don't show revised code.

Exemplo de resposta 1

O Copilot faz várias sugestões para aprimorar o código:

  • Abstraia os detalhes de conexão do banco de dados.
  • Use consultas parametrizadas para evitar a injeção de SQL.
  • Use um gerenciador de contexto para garantir que a conexão de banco de dados seja encerrada corretamente se ocorrer um erro.
  • Adicione tratamento de erros.

Exemplo de prompt 2

Você pode usar a resposta ao seu primeiro prompt para escrever um prompt mais específico.

Rewrite this code to make it more scalable and easier to maintain. Use a context manager. Avoid hardcoded SQL queries and tightly coupled data access code. Instead, use a repository pattern to abstract database interactions and make the code more modular and reusable. Where possible optimize the code to improve performance. Include error trapping, and make sure the code is not vulnerable to SQL injection.

Exemplo de resposta 2

O Copilot sugere dividir o código em arquivos separados, cada um com uma finalidade específica. Ele também fornece um código de exemplo para retornar os detalhes de um usuário.

As alterações sugeridas implementam um padrão de repositório e usam consultas parametrizadas para evitar a injeção de SQL.

# database.py
import sqlite3

class Database:
    def __init__(self, db_path):
        self.db_path = db_path

    def __enter__(self):
        self.conn = sqlite3.connect(self.db_path)
        self.cursor = self.conn.cursor()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type or exc_val or exc_tb:
            self.conn.rollback()
        else:
            self.conn.commit()
        self.conn.close()

# user_repository.py
class UserRepository:
    def __init__(self, db):
        self.db = db

    def get_user_by_id(self, user_id):
        try:
            self.db.cursor.execute("SELECT display_name FROM users WHERE id = ?", (user_id,))
            user = self.db.cursor.fetchone()
            return user
        except sqlite3.Error as e:
            print(f"An error occurred: {e}")
            return None

# main.py
from database import Database
from user_repository import UserRepository

def get_user_by_id(user_id):
    with Database('database.db') as db:
        user_repo = UserRepository(db)
        return user_repo.get_user_by_id(user_id)

# Example usage
if __name__ == "__main__":
    user = get_user_by_id(1)
    if user:
        print(user)
    else:
        print("User not found or an error occurred.")

O código revisado aprimora o original de várias maneiras:

  • O código agora implementa uma camada básica de acesso a dados.
  • A classe Database manipula a conexão com o banco de dados SQLite, implementando o protocolo do gerenciador de contexto com os métodos __enter__ e __exit__. Isso garante que a conexão ao banco de dados seja gerenciada corretamente, incluindo a confirmação de transações e o encerramento da conexão.
  • A classe UserRepository encapsula a lógica para acessar dados do usuário.
  • Os valores das consultas são parametrizados para evitar a injeção de SQL.
  • Erros são detectados, com detalhes impressos no console.

Leitura adicional