Skip to main content

Refactorización de capas de acceso a datos

El Copilot Chat puede sugerir distintas formas de desacoplar el código de acceso a datos de la lógica de negocios, lo que facilita el mantenimiento y el escalado de una aplicación.

Las consultas SQL codificadas de forma rígida y el código de acceso a datos estrechamente acoplado pueden dificultar el escalado o cambio de bases de datos y, a menudo, dan lugar a una lógica repetida.

El Copilot Chat puede recomendar la implementación de un patrón de repositorio o una capa de acceso a datos que abstraiga las interacciones de la base de datos, lo que hace que el código sea más modular y reduce la redundancia. También puede ayudar a refactorizar las consultas SQL en versiones optimizadas, lo que mejora el rendimiento y la capacidad de mantenimiento.

Note

Las respuestas descritas en este artículo son ejemplos. Las respuestas del Copilot Chat no son deterministas, por lo que puedes obtener respuestas diferentes a las aquí mostradas.

Escenario de ejemplo

Este código de Python se conecta a una base de datos de SQLite, recupera un registro de usuario y devuelve los datos del usuario. Sin embargo, no puede abstraer la lógica de conexión de la base de datos y se usa una consulta codificada de forma rígida que es vulnerable a la inyección de código 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

Indicación de ejemplo 1

Para empezar, formula a Copilot una pregunta general sobre cómo mejorar el 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.

Respuesta de ejemplo 1

Copilot realiza varias sugerencias para mejorar el código:

  • Abstrae los detalles de conexión de la base de datos.
  • Usa consultas con parámetros para evitar la inyección de código SQL.
  • Usa un administrador de contextos para asegurarte de que la conexión de la base de datos se cierre correctamente si se produce un error.
  • Agrega control de errores.

Indicación de ejemplo 2

Puedes usar la respuesta a la primera indicación para escribir una indicación más específica.

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.

Respuesta de ejemplo 2

Copilot sugiere dividir el código en archivos independientes, cada uno con un propósito específico. También proporciona código de ejemplo para devolver los detalles de un usuario.

Los cambios sugeridos implementan un patrón de repositorio y usan consultas con parámetros para evitar la inyección de código 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.")

El código revisado mejora el original de varias maneras:

  • El código implementa ahora una capa de acceso a datos básica.
  • La clase Database controla la conexión a la base de datos SQLite e implementa el protocolo del administrador de contextos con los métodos __enter__ y __exit__. Esto garantiza que la conexión de base de datos se administre correctamente, incluidos la confirmación de transacciones y el cierre de la conexión.
  • La clase UserRepository encapsula la lógica para acceder a los datos de usuario.
  • Los valores de las consultas se parametrizan para evitar la inyección de código SQL.
  • Se detectan errores y los detalles se imprimen en la consola.

Información adicional