Configuración de Base de Datos
Tabla de Contenidos
- Propósito
- ¿Para quién es esto?
- Descripción General de Arquitectura de Base de Datos
- Configuración Local de MongoDB
- Configuración Cloud de MongoDB
- Configuración de Base de Datos
- Gestión de Esquemas
- Estrategia de Indexación
- Respaldo y Restauración
- Monitoreo y Mantenimiento
- Solución de Problemas
Propósito
Esta guía proporciona instrucciones completas para configurar, configurar y mantener bases de datos MongoDB en la plataforma Algesta. Cubre configuración de desarrollo local, despliegue en la nube, agrupación de conexiones, gestión de esquemas y tareas operacionales como respaldo/restauración.
Siguiendo esta guía, podrás:
- Configurar MongoDB localmente para desarrollo
- Configurar instancias de MongoDB Atlas u hospedadas en la nube
- Entender cadenas de conexión y agrupación
- Gestionar esquemas de base de datos e índices
- Realizar operaciones de respaldo y restauración
- Monitorear salud y rendimiento de base de datos
¿Para quién es esto?
Esta guía es para desarrolladores configurando bases de datos locales, ingenieros DevOps aprovisionando bases de datos en la nube, y administradores de bases de datos gestionando datos de producción. Asume familiaridad con MongoDB, bases de datos de documentos y administración básica de bases de datos.
Descripción General de Arquitectura de Base de Datos
La plataforma Algesta implementa un patrón de base-de-datos-por-servicio, donde cada microservicio posee su base de datos MongoDB dedicada para aislamiento de datos y autonomía.
Estrategia de Base de Datos
| Microservicio | Nombre de Base de Datos | Propósito | Tamaño Estimado |
|---|---|---|---|
| Orders MS | orders_ms | Ciclo de vida de órdenes, cotizaciones, activos | 10+ GB |
| Notifications MS | notifications_ms | Seguimiento de notificaciones | 1 GB |
| Provider MS | providers_ms | Proveedores, subastas, documentos | 5 GB |
Principios Clave:
- Aislamiento de Datos: Cada servicio tiene acceso exclusivo a su base de datos
- Autonomía: Los servicios pueden evolucionar esquemas independientemente
- Consistencia Eventual: Datos entre servicios sincronizados vía eventos (Kafka)
- Desnormalización: Duplicar datos donde sea necesario para rendimiento
Versión de MongoDB: 7.0+ (especificado en Docker Compose y configuraciones de despliegue)
ODM: Mongoose 8.16+ (ver package.json en cada microservicio)
Referencia: Ver Esquemas de Base de Datos para documentación detallada de esquemas.
Configuración Local de MongoDB
Opción 1: Instalación Nativa de MongoDB
macOS (Homebrew):
# Install MongoDB Community Editionbrew tap mongodb/brewbrew install mongodb-community@7.0
# Start MongoDB servicebrew services start mongodb-community@7.0
# Verify installationmongosh --eval "db.version()"# Expected: 7.0.xLinux (Ubuntu/Debian):
# Import MongoDB public GPG keywget -qO- https://www.mongodb.org/static/pgp/server-7.0.asc | sudo tee /etc/apt/trusted.gpg.d/server-7.0.asc
# Add MongoDB repositoryecho "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
# Update and installsudo apt-get updatesudo apt-get install -y mongodb-org
# Start MongoDBsudo systemctl start mongodsudo systemctl enable mongod
# Verifymongosh --eval "db.version()"Windows: Descargar instalador desde mongodb.com/try/download/community y seguir el instalador GUI.
Verificación:
# Connect to MongoDBmongosh
# In mongosh shell:> db.version()7.0.x
> show dbsadmin 40.00 KiBconfig 60.00 KiBlocal 40.00 KiBOpción 2: MongoDB en Docker
Ejecutar Contenedor de MongoDB:
docker run -d \ --name algesta-mongodb \ -p 27017:27017 \ -v algesta-mongo-data:/data/db \ -e MONGO_INITDB_ROOT_USERNAME=admin \ -e MONGO_INITDB_ROOT_PASSWORD=password \ mongo:7.0Verificación:
# Verificar contenedor en ejecucióndocker ps | grep algesta-mongodb
# Conectar a MongoDBmongosh "mongodb://admin:password@localhost:27017/?authSource=admin"
# Probar conexión> db.runCommand({ ping: 1 }){ ok: 1 }Persistencia: Los datos se almacenan en el volumen de Docker algesta-mongo-data, sobreviven reinicios del contenedor.
Detener/Iniciar:
docker stop algesta-mongodbdocker start algesta-mongodbCrear bases de datos
Para desarrollo local, crear bases de datos para cada servicio:
mongosh
# Switch to orders database (creates if doesn't exist)> use orders_msswitched to db orders_ms
# Insert a test document to persist database> db.test.insertOne({ created: new Date() })
# Repeat for other databases> use notifications_ms> db.test.insertOne({ created: new Date() })
> use providers_ms> db.test.insertOne({ created: new Date() })
# Verify databases created> show dbsorders_ms 40.00 KiBnotifications_ms 40.00 KiBproviders_ms 40.00 KiBConfiguración Cloud de MongoDB
Opción 1: MongoDB Atlas (Recomendado)
1. Crear Cuenta de Atlas:
- Registrarse en mongodb.com/cloud/atlas
- Crear cluster de nivel gratuito (M0) para desarrollo
2. Configurar Cluster:
- Proveedor: GCP (coincide con despliegue de Cloud Run)
- Región:
us-east1(o más cercana a servicios de Cloud Run) - Nivel de Cluster: M0 (gratuito, 512 MB) para desarrollo, M10+ para producción
3. Crear Usuario de Base de Datos:
Usuario: algesta_adminContraseña: <generar contraseña fuerte>Roles: Lectura y escritura en cualquier base de datos4. Acceso de Red:
- Para desarrollo: Agregar tu dirección IP
- Para Cloud Run: Agregar
0.0.0.0/0(permitir todos) o usar VPC Peering
5. Obtener Cadena de Conexión:
mongodb+srv://algesta_admin:<password>@cluster0.xxxxx.mongodb.net/?retryWrites=true&w=majority6. Crear bases de datos:
- Hacer clic en “Browse Collections” → “Add My Own Data”
- Crear:
orders_ms,notifications_ms,providers_ms
Verificación:
# Test connectionmongosh "mongodb+srv://algesta_admin:<password>@cluster0.xxxxx.mongodb.net/"
> show dbsorders_ms ...notifications_ms ...providers_ms ...Opción 2: GCP Cloud SQL para MongoDB (Futuro)
Nota: GCP no soporta MongoDB nativamente. Usar MongoDB Atlas (región GCP) o auto-hospedado en GCE.
Auto-hospedado en GCE:
- Crear instancia de Compute Engine
- Instalar MongoDB 7.0
- Configurar reglas de firewall (permitir puerto 27017 desde Cloud Run)
- Configurar replica set para alta disponibilidad
Configuración de Base de Datos
Cadenas de Conexión
Formato:
mongodb://[username:password@]host[:port]/database[?options]Ejemplos:
Local (sin autenticación):
MONGODB_URI=mongodb://localhost:27017/orders_msLocal (con autenticación):
MONGODB_URI=mongodb://admin:password@localhost:27017/orders_ms?authSource=adminMongoDB Atlas:
MONGODB_URI=mongodb+srv://algesta_admin:password@cluster0.xxxxx.mongodb.net/orders_ms?retryWrites=true&w=majorityProducción (Replica Set):
MONGODB_URI=mongodb://user:pass@mongo1.example.com:27017,mongo2.example.com:27017,mongo3.example.com:27017/orders_ms?replicaSet=rs0&authSource=adminConfiguración de Pool de Conexiones
Ubicación: algesta-ms-orders-nestjs/src/shared/infrastructure/database/database.config.ts
export const getMongoConfig = (): MongoConnectionOptions => { const isProduction = process.env.NODE_ENV === "production";
const uri = process.env.MONGODB_URI ?? "mongodb://localhost:27017/documents_ms";
return { uri, options: { maxPoolSize: parseInt(process.env.DB_POOL_MAX ?? "20", 10), minPoolSize: parseInt(process.env.DB_POOL_MIN ?? "5", 10), maxIdleTimeMS: parseInt(process.env.DB_POOL_IDLE_TIMEOUT ?? "30000", 10), connectTimeoutMS: parseInt( process.env.DB_POOL_CONNECTION_TIMEOUT ?? "2000", 10 ), socketTimeoutMS: parseInt(process.env.DB_SOCKET_TIMEOUT ?? "30000", 10), serverSelectionTimeoutMS: parseInt( process.env.DB_SERVER_SELECTION_TIMEOUT ?? "5000", 10 ), bufferCommands: !isProduction, }, };};Configuraciones Clave:
| Configuración | Desarrollo | Producción | Propósito |
|---|---|---|---|
maxPoolSize | 20 | 50 | Máximo de conexiones en pool |
minPoolSize | 5 | 10 | Mínimo de conexiones mantenidas abiertas |
maxIdleTimeMS | 30000 (30s) | 30000 | Cerrar conexiones inactivas |
connectTimeoutMS | 2000 (2s) | 5000 (5s) | Timeout para nuevas conexiones |
socketTimeoutMS | 30000 (30s) | 45000 (45s) | Timeout para operaciones de socket |
serverSelectionTimeoutMS | 5000 (5s) | 10000 (10s) | Timeout para encontrar servidor MongoDB |
bufferCommands | true | false | Almacenar comandos si está desconectado |
Ajuste:
- Aumentar
maxPoolSizepara servicios de alto tráfico - Disminuir
connectTimeoutMSpara detección rápida de fallos - Habilitar
retryWrites=trueen cadena de conexión para producción
Verificación:
// In main.ts, log connection statusmongoose.connection.on("connected", () => { console.log("MongoDB connected:", mongoose.connection.name); console.log("Pool size:", mongoose.connection.db.serverConfig.s.poolSize);});Gestión de Esquemas
Definición de Esquemas con Mongoose
Algesta usa esquemas de Mongoose (enfoque code-first), no esquemas manuales de MongoDB.
Ejemplo: algesta-ms-orders-nestjs/src/domain/entities/order.entity.ts
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";import { Document } from "mongoose";
@Schema({ timestamps: true })export class Order extends Document { @Prop({ required: true, unique: true }) orderId: string;
@Prop({ required: true, enum: ["CALL_CENTER", "WEB", "MOBILE", "ADMIN"] }) channel: string;
@Prop({ required: true }) service: string;
@Prop({ required: true }) address: string;
@Prop({ required: true }) city: string;
@Prop({ type: Object }) assignedProvider?: { providerId: string; providerName: string; providerEmail: string; assignedAt: Date; };
@Prop({ type: [String] }) assetIds?: string[];
// Mongoose adds createdAt, updatedAt automatically}
export const OrderSchema = SchemaFactory.createForClass(Order);Funcionalidades Clave:
- Decoradores:
@Schema(),@Prop()definen esquema - Validación:
required,enum,unique - Documentos Embebidos:
type: Objectpara sub-documentos - Arreglos:
type: [String]para arreglos - Timestamps:
timestamps: trueagregacreatedAt,updatedAt
Evolución de Esquemas
Algesta NO tiene un sistema formal de migraciones (deuda técnica).
Enfoque Actual:
- Actualizar esquema de Mongoose en código
- Desplegar nuevo código
- MongoDB permite evolución sin esquema (documentos antiguos coexisten con nuevos)
- Opcional: Ejecutar script manual para actualizar documentos existentes
Ejemplo de Script de Migración:
const mongoose = require("mongoose");const { Order } = require("../dist/domain/entities/order.entity");
async function migrate() { await mongoose.connect(process.env.MONGODB_URI);
const result = await Order.updateMany( { status: { $exists: false } }, { $set: { status: "NEW" } } );
console.log(`Updated ${result.modifiedCount} documents`); await mongoose.disconnect();}
migrate();Ejecutar Migración:
MONGODB_URI=mongodb://localhost:27017/orders_ms node scripts/migrate-add-status-field.jsMejor Práctica: Probar migraciones en copia de datos de producción primero.
Mejora Futura: Implementar framework de migraciones (ej., migrate-mongo).
Estrategia de Indexación
Los índices son críticos para el rendimiento de consultas. Algesta usa creación manual de índices (no auto-indexados por Mongoose en producción).
Crear Índices
En MongoDB Shell:
use orders_ms;
// Unique index on orderIddb.orders.createIndex({ orderId: 1 }, { unique: true });
// Compound index for common queriesdb.orders.createIndex({ status: 1, createdAt: -1 });
// Index on embedded fieldsdb.orders.createIndex({ "assignedProvider.providerId": 1 });
// Index on array fieldsdb.orders.createIndex({ assetIds: 1 });Verificación:
db.orders.getIndexes();Salida Esperada:
[ { "v": 2, "key": { "_id": 1 }, "name": "_id_" }, { "v": 2, "key": { "orderId": 1 }, "name": "orderId_1", "unique": true }, { "v": 2, "key": { "status": 1, "createdAt": -1 }, "name": "status_1_createdAt_-1" }]Índices Recomendados por Servicio
Orders MS (orders_ms):
db.orders.createIndex({ orderId: 1 }, { unique: true });db.orders.createIndex({ status: 1 });db.orders.createIndex({ channel: 1 });db.orders.createIndex({ phoneNumber: 1 });db.orders.createIndex({ createdAt: -1 });db.orders.createIndex({ "assignedProvider.providerId": 1 });db.orders.createIndex({ assetIds: 1 });db.orders.createIndex({ status: 1, createdAt: -1 }); // CompoundNotifications MS (notifications_ms):
db.notifications.createIndex({ userId: 1 });db.notifications.createIndex({ status: 1 });db.notifications.createIndex({ createdAt: -1 });db.notifications.createIndex({ userId: 1, status: 1 }); // Compound
// TTL Index (auto-delete after 90 days)db.notifications.createIndex({ createdAt: 1 }, { expireAfterSeconds: 7776000 });Provider MS (providers_ms):
db.providers.createIndex({ email: 1 }, { unique: true });db.providers.createIndex({ identification: 1 });db.providers.createIndex({ isActive: 1 });
db.auction.createIndex({ auctionId: 1 }, { unique: true });db.auction.createIndex({ orderId: 1 });db.auction.createIndex({ "auctionBids.providerId": 1 });db.auction.createIndex({ "auctionBids.isCurrent": 1 });db.auction.createIndex({ orderId: 1, "auctionBids.providerId": 1 }); // CompoundMonitoreo de Índices
Verificar Uso de Índices:
db.orders.aggregate([{ $indexStats: {} }]);Perfilado de Consultas Lentas:
// Enable profiling (log queries > 100ms)db.setProfilingLevel(1, { slowms: 100 });
// View slow queriesdb.system.profile.find().sort({ ts: -1 }).limit(10);Respaldo y Restauración
Estrategias de Respaldo
Desarrollo Local:
# Backup single databasemongodump --uri="mongodb://localhost:27017/orders_ms" \ --gzip \ --archive="backups/orders_ms-$(date +%Y%m%d).gz"
# Backup all databasesmongodump --uri="mongodb://localhost:27017" \ --gzip \ --archive="backups/all-dbs-$(date +%Y%m%d).gz"Verificación:
ls -lh backups/# Esperado: orders_ms-20251120.gz (archivo comprimido)Producción (MongoDB Atlas):
- Respaldos automatizados habilitados por defecto (cada 24 horas)
- Retención: 2 días (tier gratuito), configurable para tiers pagos
- Respaldos manuales: Atlas UI → Backup → Take Snapshot
Procedimientos de Restauración
Restaurar desde Respaldo:
# Restore to local MongoDBmongorestore --uri="mongodb://localhost:27017/orders_ms" \ --gzip \ --archive="backups/orders_ms-20251120.gz" \ --dropFlags:
--drop: Eliminar colecciones existentes antes de restaurar (destructivo!)--gzip: Descomprimir archivo gzipped--archive: Restaurar desde archivo comprimido
Restaurar a Diferente Base de Datos:
mongorestore --uri="mongodb://localhost:27017/orders_ms_restore" \ --gzip \ --archive="backups/orders_ms-20251120.gz"Verificación:
mongosh mongodb://localhost:27017/orders_ms_restore
> db.orders.countDocuments()12345 # Verificar que el conteo de documentos coincida con el respaldoExportar/Importar Colecciones Específicas
Exportar a JSON:
mongoexport --uri="mongodb://localhost:27017/orders_ms" \ --collection=orders \ --out=orders-export.json \ --jsonArrayImportar desde JSON:
mongoimport --uri="mongodb://localhost:27017/orders_ms" \ --collection=orders \ --file=orders-export.json \ --jsonArrayMonitoreo y Mantenimiento
Consultas de Monitoreo
Verificar Estadísticas de Base de Datos:
db.orders.stats();Salida de Ejemplo:
{ "ns": "orders_ms.orders", "size": 52428800, // 50 MB "count": 10000, "avgObjSize": 5242, "storageSize": 20971520, // 20 MB (compressed) "totalIndexSize": 4194304, // 4 MB "indexSizes": { "_id_": 2097152, "orderId_1": 1048576, "status_1_createdAt_-1": 1048576 }}Monitorear Conexiones:
db.serverStatus().connections;Salida de Ejemplo:
{ "current": 12, // Active connections "available": 51188, // Max connections "totalCreated": 450}Tareas de Mantenimiento
Compactar Colecciones (Recuperar Espacio):
db.runCommand({ compact: "orders" });Reconstruir Índices:
db.orders.reIndex();Eliminar Datos Antiguos (Limpieza Manual):
// Delete notifications older than 90 daysdb.notifications.deleteMany({ createdAt: { $lt: new Date(Date.now() - 90 * 24 * 60 * 60 * 1000) },});Verificar Retraso de Replicación (Replica Sets):
rs.printReplicationInfo();Solución de Problemas
Problema: No se puede conectar a MongoDB desde servicio NestJS
Diagnóstico:
# Check MongoDB runningmongosh --eval "db.version()"
# Test connection stringmongosh "mongodb://localhost:27017/orders_ms" --eval "db.runCommand({ ping: 1 })"Soluciones:
- Verificar
MONGODB_URIen.env:MONGODB_URI=mongodb://localhost:27017/orders_ms - Verificar que MongoDB esté escuchando en el puerto correcto:
Ventana de terminal lsof -i :27017 - Para MongoDB en Docker, usar
host.docker.internalen lugar delocalhostdesde contenedores - Verificar reglas de firewall (despliegues en la nube)
Problema: Consultas lentas, degradación de rendimiento
Diagnóstico:
// Enable profilingdb.setProfilingLevel(1, { slowms: 100 });
// Find slow queriesdb.system.profile .find({ millis: { $gt: 100 } }) .sort({ ts: -1 }) .limit(10);Soluciones:
- Verificar si existen índices:
db.orders.getIndexes();
- Analizar plan de ejecución de consulta:
db.orders.find({ status: "NEW" }).explain("executionStats");
- Agregar índices faltantes basados en patrones de consulta
- Aumentar tamaño del pool de conexiones si las conexiones están agotadas
Problema: Error de clave duplicada en orderId
Diagnóstico:
MongoServerError: E11000 duplicate key error collection: orders_ms.orders index: orderId_1Causa: Intentando insertar orden con orderId existente.
Soluciones:
- Verificar que la lógica de generación de
orderIdsea única (ej., UUID, basado en timestamp) - Verificar condiciones de carrera en creación de órdenes
- Consultar orden existente antes de insertar:
const existing = await this.orderModel.findOne({ orderId });if (existing) throw new ConflictException("Order already exists");
Problema: Pool de conexiones de base de datos agotado
Diagnóstico:
MongoServerError: connection pool is fullSoluciones:
- Aumentar
maxPoolSizeendatabase.config.ts:maxPoolSize: parseInt(process.env.DB_POOL_MAX ?? '50', 10), - Verificar fugas de conexión (cursores no cerrados):
db.serverStatus().connections.current;
- Monitorear consultas activas:
db.currentOp({ active: true });
- Reiniciar servicio para resetear pool
Guías Relacionadas:
- Esquemas de Base de Datos: Documentación detallada de esquemas
- Configuración de Desarrollo Local: Configurar MongoDB para desarrollo
- Configuración de Entorno: Variables de entorno de conexión a base de datos
- Solución de Problemas: Solución de problemas específicos de base de datos
Para Soporte:
- Revisar Documentación de Mongoose: mongoosejs.com
- Consultar Documentación de MongoDB Atlas: docs.atlas.mongodb.com
- Contactar equipo DevOps para acceso a base de datos de producción