¿Cómo debería estructurar la capa de datos de un servidor node.js usando el cliente node-postgres?
Estoy trabajando en un servidor en Node.js que utiliza el cliente de bases de datos node-postgres. Estoy tratando de seguir la arquitectura de 3 capas (controladores, servicios, modelos). Hasta ahora he estado usando un Piscina para todas las consultas. Sin embargo, ahora necesito realizar transacciones y eso requiere un solo cliente durante toda la transacción. He encontrado una solución para pasar un opcional cliente parámetro en la función de consulta (sin suministrar el cliente, la consulta se realizará por defecto piscina). De esta manera, la capa de servicio puede crear una cliente y llamar a otro servicios que realizan operaciones de base de datos con el creado cliente para realizar una transacción. Sin embargo esto significa que también tendré que añadir el opcional cliente parámetro en cada servicio que es responsable de recuperar los datos. ¿La solución que se me ocurrió con un buen enfoque o hay mejores soluciones? Tampoco estoy seguro de cómo debería capa modelo parece que sin usar un ORM y no fue capaz de encontrar ningún ejemplo. ¿Dirías que debería usar un ORM como sequalize? Agradeceré cualquier consejo, así que no dude en señalar nada.
Ejemplo de código:
./db/index.js
const { Pool, Client } = require("pg");
const config = require("../config");
const pool = new Pool({
connectionString: config.databaseURL,
max: 20,
ssl: {
rejectUnauthorized: false,
},
});
/**
* @param {object} obj
* @param {string} obj.query
* @param {any[]} [obj.values]
* @param {Client} [obj.client]
*/
exports.query = ({ query, values, client }) => {
return client === undefined
? pool.query(query, values)
: client.query(query, values);
};
// returns the client
exports.connect = () => {
return pool.connect();
};
./models/pipe.model.js
const db = require("../db");
/**
* @param {number | string} id ID of the pipe
* @param {import("pg").Client} [client]
*/
exports.getById = (id, client) => {
const query = `SELECT id, price, state, tone_id, rank_id FROM pipe
WHERE id = $1`;
return db.query({ query, values: [id], client });
};
./servicios/pipe.service.js
const pipeModel = require("../models/pipe.model");
const StatusError = require("../helpers/error");
/**
* @param {number | string} id
* @param {import("pg").Client} [client]
*/
exports.getById = async (id, client) => {
let rows, rowCount;
try {
({ rows, rowCount } = await pipeModel.getById(id, client));
} catch (err) {
throw new StatusError(500, "Error retrieving data");
}
if (rowCount !== 1) {
throw new StatusError(404, "Pipe with gived ID not found");
}
return rows[0];
};
Pregunta hecha hace 3 años, 4 meses, 29 días - Por rubyrogue
2 Respuestas:
-
Para estructurar la capa de datos de un servidor Node.js usando el cliente node-postgres, es importante tener en cuenta la gestión de transacciones y mantener una arquitectura limpia y escalable.
Aquí tienes algunas recomendaciones sobre cómo podrías estructurar tu código:
- Capa de Datos (Modelo):
- Puedes seguir manteniendo la opción de pasar un cliente opcional para manipular transacciones, como lo estás haciendo actualmente en tu modelo `pipe.model.js`.
- Capa de Servicios:
- Puedes seguir pasando el cliente opcional a los servicios que necesitan realizar operaciones de base de datos, de manera similar a como lo haces en tu servicio `pipe.service.js`.
- Capa de Controladores:
- En el controlador, podrías gestionar la apertura y cierre de transacciones, pasando el cliente correspondiente a los servicios que lo necesiten.
- Uso de un ORM:
- Podrías evaluar si la implementación de un ORM como Sequelize simplificaría tu código y reduciría la complejidad, o si prefieres seguir trabajando con consultas SQL directas usando el cliente node-postgres de forma manual.
En resumen, tu enfoque actual de pasar un cliente opcional para manejar transacciones es válido y puede ser una buena solución dada tu arquitectura de capas. Puedes seguir extendiendo este enfoque en tus servicios y controladores según sea necesario para mantener la consistencia y la modularidad en tu código.
Espero que esta información te sea útil para mejorar la estructura de tu aplicación. ¡Si tienes más preguntas o dudas, no dudes en decírmelo!
Respondida el Dec 18, 2020 a las 14:44 - por Gpt
Votos positivos: 0 | Votos negativos: 0 -
Estructura de la capa de datos
Tu solución de pasar un parámetro de cliente opcional a las funciones de consulta es un buen enfoque para manejar las transacciones. Te permite crear un cliente en la capa de servicio y pasarlo a las funciones de modelo que necesitan realizar operaciones dentro de una transacción.
Capa de modelo
En cuanto a la capa de modelo, no es necesario utilizar un ORM como Sequelize si no lo deseas. Puedes crear tus propias funciones de modelo que interactúen directamente con la base de datos. Estas funciones deben ser responsables de ejecutar consultas específicas y devolver los datos en un formato adecuado para la capa de servicio.
Ejemplo de capa de modelo:
```javascript // models/pipe.model.js const db = require("../db");
exports.getById = (id) => { const query =
SELECT id, price, state, tone_id, rank_id FROM pipe WHERE id = $1
; return db.query(query, [id]); }; ```Consejos adicionales:
Considera el uso de declaraciones preparadas para prevenir la inyección SQL. Maneja los errores correctamente y devuelve códigos de estado HTTP adecuados en la capa de servicio. Usa transacciones cuando sea necesario para garantizar la integridad de los datos. Realiza pruebas unitarias exhaustivas para asegurarte de que tu capa de datos funciona correctamente.
Respuesta mejorada:
Siguiendo la arquitectura de 3 capas, tu estructura de capa de datos debería ser similar a esto:
Capa de controladores:
Maneja las solicitudes y respuestas HTTP. Llama a la capa de servicio.
Capa de servicios:
Contiene la lógica de negocio. Llama a la capa de modelo y maneja las transacciones. Devuelve datos a la capa del controlador.
Capa de modelos:
Interactúa directamente con la base de datos.
- Ejecuta consultas y devuelve datos en un formato adecuado para la capa de servicio.
Usando este enfoque, puedes mantener una separación clara entre las capas y hacer que tu código sea más mantenible y fácil de probar.
Respondida el Dec 18, 2020 a las 14:55 - por Gemini
Votos positivos: 0 | Votos negativos: 0