Saltearse al contenido

Esquemas de bases de datos

Esquemas de bases de datos

1. Descripción general de arquitectura de bases de datos

La plataforma Algesta implementa un patrón de base de datos por servicio, donde cada microservicio tiene su propia base de datos MongoDB para aislamiento y autonomía de datos.

Estrategia de Base de datos:

  • Patrón: Base de datos por Microservicio
  • Base de datos: MongoDB (la versión varía según el entorno de despliegue)
  • ODM: Mongoose para definición y validación de esquemas (ver package.json para la versión)
  • Agrupación de Conexiones: Tamaño de pool configurado para rendimiento
  • Índices: Índices estratégicos para optimización de consultas
  • Transacciones: Transacciones MongoDB para operaciones multi-documento
  • Replicación: Conjuntos de réplicas para alta disponibilidad (producción)

Referencias de Versión: Las versiones exactas de MongoDB y Mongoose se especifican en el package.json de cada Microservicio:

  • algesta-ms-orders-nestjs/package.json
  • algesta-ms-notifications-nestjs/package.json
  • algesta-ms-provider-nestjs/package.json

Principios Clave:

  • Aislamiento de Datos: Cada servicio posee sus propios datos
  • Autonomía: Los servicios pueden evolucionar esquemas independientemente
  • Consistencia Eventual: Datos entre servicios sincronizados mediante eventos
  • Desnormalización: Duplicar datos donde sea necesario para rendimiento

2. Base de datos del Microservicio Orders

La base de datos del Microservicio Orders es la más compleja, con modelos de dominio extensos y relaciones.

Nombre de Base de datos: orders

Descripción General de Colecciones

ColecciónPropósitoTamaño Prom/DocCantidad Registros (Est.)Índices
ordersDocumentos principales de órdenes5-50 KB10,000+8 índices
assetsInventario de activos2-5 KB5,000+12 índices
locationsDatos de ubicación0.5-1 KB2,000+3 índices
messagesMensajes de comunicación0.5-1 KB50,000+4 índices
techniciansPerfiles de técnicos1-2 KB100+2 índices
usersCuentas de usuario1-2 KB1,000+3 índices
companiesInformación de empresas1-3 KB500+2 índices
order-historyRastro de auditoría0.5-2 KB100,000+3 índices
formsPlantillas de formulariosVariable50+2 índices
category-assetsCategorías de activos0.2-0.5 KB20+1 índice

Colección orders

Propósito: Almacenar datos completos del ciclo de vida de órdenes

Esquema: algesta-ms-orders-nestjs/src/domain/entities/order.entity.ts

Campos Clave:

  • _id (ObjectId): Clave primaria de MongoDB
  • orderId (string, único): Identificador de negocio (ej., ORDER-12345)
  • channel (enum): Origen de la orden (CALL_CENTER, WEB, MOBILE, ADMIN)
  • service (string): Servicio solicitado
  • address (string): Ubicación del servicio
  • city (string): Ciudad
  • Descripción (string): Detalles de la orden
  • Estado (enum): Estado actual (NEW, QUOTE_SENT, ASSIGNED, COMPLETED, etc.)
  • typeOrder (enum): Tipo de orden (WARRANTY, MAINTENANCE, REPAIR, INSTALLATION)
  • phoneNumber (string): Teléfono de contacto
  • emailContact (string): Email de contacto
  • assignedProvider (objeto embebido): Detalles de asignación del proveedor
  • workProcess (objeto embebido): Seguimiento del ciclo de vida del trabajo
  • advance (objeto embebido): Seguimiento de pago anticipado
  • quote (objeto embebido): Gestión de cotización
  • guarantees (objeto embebido): Contratos y pólizas
  • assetIds (arreglo de strings): Referencias a la colección de activos
  • technicalVisit (objeto embebido): Flujo de visita técnica
  • providerRating (objeto embebido): Retroalimentación del cliente
  • createdAt (Date): Marca de tiempo de creación
  • updatedAt (Date): Marca de tiempo de última actualización

Índices:

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({ assignedTo: 1 });
db.orders.createIndex({ createdAt: -1 });
db.orders.createIndex({ "assignedProvider.providerId": 1 });
db.orders.createIndex({ assetIds: 1 });

Documento de Ejemplo:

{
"_id": ObjectId("507f1f77bcf86cd799439011"),
"orderId": "ORDER-12345",
"channel": "WEB",
"service": "Air Conditioning Repair",
"address": "Calle 123 #45-67",
"city": "Bogotá",
"status": "ASSIGNED",
"typeOrder": "REPAIR",
"phoneNumber": "+57 300 1234567",
"emailContact": "client@example.com",
"assignedProvider": {
"providerId": "PROV-001",
"providerName": "AC Experts SAS",
"providerEmail": "contact@acexperts.com",
"assignedAt": ISODate("2025-11-19T10:30:00Z")
},
"workProcess": {
"startWork": {
"confirmed": true,
"confirmedAt": ISODate("2025-11-20T08:00:00Z")
},
"initialInspection": {
"completed": true,
"completedAt": ISODate("2025-11-20T09:30:00Z"),
"findings": "Refrigerant leak detected"
}
},
"quote": {
"totalAmount": 500000,
"laborCost": 200000,
"materialsCost": 300000,
"approved": true,
"approvedAt": ISODate("2025-11-19T15:00:00Z")
},
"assetIds": ["ASSET-001", "ASSET-002"],
"createdAt": ISODate("2025-11-19T09:00:00Z"),
"updatedAt": ISODate("2025-11-20T09:30:00Z")
}

Colección assets

Propósito: Rastrear inventario y ciclo de vida de activos

Esquema: algesta-ms-orders-nestjs/src/domain/entities/orders/asset.entity.ts

Campos Clave:

  • _id (ObjectId): MongoDB primary key
  • serialNumber (string): Manufacturer serial number
  • serialId (string, sparse): Internal asset ID
  • category (string): Asset category
  • name (string): Asset name
  • brand (string): Manufacturer brand
  • model (string): Model number
  • assetEstado (enum): operational, operational_with_faults, maintenance, out_of_service, retired
  • city (string): Asset location city
  • address (string): Asset address
  • assetLocation (string): Specific location
  • location (object): Geolocation {latitude, longitude}
  • clientPrioridad (enum): high, medium, low
  • clientImportance (enum): high, medium, low
  • assetValorPrioridad (enum): high, medium, low
  • acquisitionDate (Date): Purchase date
  • purchasePrice (number): Original price
  • currentAssetValor (number): Current Valor
  • preventiveMaintenanceValor (number): Preventive maintenance cost
  • correctiveMaintenanceValor (number): Corrective maintenance cost
  • createdAt (Date): Creation timestamp
  • updatedAt (Date): Last update timestamp

Indexes:

db.assets.createIndex({ serialNumber: 1 });
db.assets.createIndex({ serialId: 1 }, { sparse: true });
db.assets.createIndex({ category: 1 });
db.assets.createIndex({ client: 1 });
db.assets.createIndex({ assetStatus: 1 });
db.assets.createIndex({ status: 1 });
db.assets.createIndex({ location: 1 });
db.assets.createIndex({ assetLocation: 1 });
db.assets.createIndex({ clientPriority: 1 });
db.assets.createIndex({ clientImportance: 1 });
db.assets.createIndex({ assetValuePriority: 1 });
db.assets.createIndex({ createdAt: -1 });

Documento de Ejemplo:

{
"_id": ObjectId("507f1f77bcf86cd799439012"),
"serialNumber": "AC-2025-001234",
"serialId": "ASSET-001",
"category": "HVAC",
"name": "Split Air Conditioner 18000 BTU",
"brand": "LG",
"model": "LS-H186KLV0",
"assetStatus": "operational",
"city": "Bogotá",
"address": "Calle 123 #45-67",
"assetLocation": "Office Floor 3",
"location": {
"latitude": 4.7110,
"longitude": -74.0721
},
"clientPriority": "high",
"clientImportance": "high",
"assetValuePriority": "medium",
"acquisitionDate": ISODate("2023-01-15T00:00:00Z"),
"purchasePrice": 3500000,
"currentAssetValue": 2800000,
"preventiveMaintenanceValue": 150000,
"correctiveMaintenanceValue": 500000,
"createdAt": ISODate("2023-01-15T10:00:00Z"),
"updatedAt": ISODate("2025-11-19T09:00:00Z")
}

Colección order-history

Propósito: Rastro de auditoría para cambios en órdenes

Campos Clave:

  • _id (ObjectId): MongoDB primary key
  • orderId (string): Associated order ID
  • action (string): Action performed (CREATED, UPDATED, Estado_CHANGED, ASSIGNED, etc.)
  • userId (string): User who performed action
  • userName (string): User name
  • userEmail (string): User email
  • timestamp (Date): Action timestamp
  • changes (object): Changed fields (before/after Valors)

Indexes:

db["order-history"].createIndex({ orderId: 1 });
db["order-history"].createIndex({ timestamp: -1 });
db["order-history"].createIndex({ userId: 1 });

Documento de Ejemplo:

{
"_id": ObjectId("507f1f77bcf86cd799439013"),
"orderId": "ORDER-12345",
"action": "STATUS_CHANGED",
"userId": "user-001",
"userName": "Admin User",
"userEmail": "admin@algesta.com",
"timestamp": ISODate("2025-11-20T09:30:00Z"),
"changes": {
"status": {
"before": "NEW",
"after": "ASSIGNED"
},
"assignedProvider": {
"before": null,
"after": {
"providerId": "PROV-001",
"providerName": "AC Experts SAS"
}
}
}
}

Otras Colecciones

locations: Datos de dirección y geolocalización messages: Mensajes de comunicación dentro de órdenes technicians: Perfiles de técnicos internos users: Cuentas de usuario (clientes, agentes, administradores) companies: Información de empresas forms: Plantillas de formularios para formularios dinámicos category-assets: Definiciones de categorías de activos

Referencia: algesta-ms-orders-nestjs/src/domain/entities/

3. Base de datos del Microservicio Notifications

La base de datos del Microservicio Notifications es más simple, enfocada en el seguimiento de notificaciones.

Nombre de Base de datos: notifications

Descripción General de Colecciones

ColecciónPropósitoTamaño Prom/DocCantidad Registros (Est.)Índices
notificationsRegistros de notificaciones0.5-1 KB100,000+3 índices

Colección notifications

Propósito: Rastrear historial y estado de notificaciones

Esquema: algesta-ms-notifications-nestjs/src/domain/entities/notification.entity.ts

Campos Clave:

  • _id (ObjectId): MongoDB primary key
  • userId (string): User ID receiving notification
  • title (string): Notification title/subject
  • message (string): Notification content
  • Estado (enum): NOT_VIEWED, VIEWED
  • createdAt (Date): Creation timestamp
  • updatedAt (Date): Last update timestamp

Indexes:

db.notifications.createIndex({ userId: 1 });
db.notifications.createIndex({ status: 1 });
db.notifications.createIndex({ createdAt: -1 });
db.notifications.createIndex({ userId: 1, status: 1 }); // Compound index

TTL Index (Auto-delete old notifications):

db.notifications.createIndex({ createdAt: 1 }, { expireAfterSeconds: 7776000 }); // 90 days

Documento de Ejemplo:

{
"_id": ObjectId("507f1f77bcf86cd799439014"),
"userId": "user-001",
"title": "Order Created",
"message": "Your order ORDER-12345 has been created successfully.",
"status": "NOT_VIEWED",
"createdAt": ISODate("2025-11-19T09:05:00Z"),
"updatedAt": ISODate("2025-11-19T09:05:00Z")
}

Política de Retención: 90 días mediante índice TTL

Referencia: algesta-ms-notifications-nestjs/src/domain/entities/notification.entity.ts

4. Base de datos del Microservicio Provider

La base de datos del Microservicio Provider es la más compleja, con el sofisticado sistema de subastas.

Nombre de Base de datos: providers

Descripción General de Colecciones

ColecciónPropósitoTamaño Prom/DocCantidad Registros (Est.)Índices
providersPerfiles de proveedores1-2 KB500+3 índices
auctionRegistros de subastas con ofertas10-100 KB10,000+14 índices
DocumentosRegistros de documentos0.5-1 KB5,000+4 índices
servicesServicios disponibles0.5-1 KB50+3 índices
service_providersMapeo proveedor-servicio0.2 KB2,000+2 índices
usersCuentas de usuario1-2 KB1,500+3 índices
ordersDatos de órdenes desnormalizadosVariable10,000+3 índices

Colección providers

Propósito: Almacenar perfiles de proveedores e información bancaria

Esquema: algesta-ms-provider-nestjs/src/domain/entities/provider.entity.ts

Campos Clave:

  • _id (ObjectId): MongoDB primary key
  • name (string): Provider first name
  • lastName (string): Provider last name
  • email (string, unique): Provider email
  • identification (string): Tax ID
  • phone (string): Contact phone
  • city (string): Provider city
  • country (string): Provider country
  • address (string): Physical address
  • location (object): {latitude, longitude}
  • bank (string): Bank name
  • typeAccount (enum): SAVINGS, CHECKING
  • numberAccount (string): Account number
  • password (string): Hashed password
  • role (enum): PROVIDER
  • isActivo (boolean): Account Activo Estado
  • createdAt (Date): Registration timestamp
  • updatedAt (Date): Last update timestamp

Indexes:

db.providers.createIndex({ email: 1 }, { unique: true });
db.providers.createIndex({ identification: 1 });
db.providers.createIndex({ isActive: 1 });

Documento de Ejemplo:

{
"_id": ObjectId("507f1f77bcf86cd799439015"),
"name": "Juan",
"lastName": "Pérez",
"email": "juan.perez@acexperts.com",
"identification": "900123456-7",
"phone": "+57 300 9876543",
"city": "Bogotá",
"country": "Colombia",
"address": "Carrera 50 #100-20",
"location": {
"latitude": 4.7110,
"longitude": -74.0721
},
"bank": "Bancolombia",
"typeAccount": "CHECKING",
"numberAccount": "1234567890",
"password": "$2b$10$xyz...",
"role": "PROVIDER",
"isActive": true,
"createdAt": ISODate("2024-01-10T10:00:00Z"),
"updatedAt": ISODate("2025-11-19T09:00:00Z")
}

auction Collection

Propósito: Store auction records with bids, dynamic quotations, and inflations

Schema: algesta-ms-provider-nestjs/src/domain/entities/auction.entity.ts

Key Fields:

  • _id (ObjectId): MongoDB primary key
  • auctionId (string, unique): Business identifier
  • orderId (string): Associated order ID
  • assetId (string): Associated asset ID
  • duration (object): {days, hours, minutes}
  • expiredAuction (Date): Expiration timestamp
  • durationInHours (number): Total duration
  • questions (array): Custom questions for providers
  • auctionBids (array): Provider bids (see detailed structure below)
  • dynamicQuotations (array): Dynamic quotation structures
  • auctionInflations (array): Price inflations by admin
  • price (object): Final price Documentoation
  • createdAt (Date): Creation timestamp
  • updatedAt (Date): Last update timestamp

auctionBids Array Structure:

{
bidId: string;
providerId: string;
providerEmail: string;
providerName: string;
offerData: {
totalOfferValue: number;
laborCost: number;
materials: [
{ item: string; quantity: number; unitPrice: number; totalPrice: number; }
];
spareParts: [
{ part: string; quantity: number; unitPrice: number; totalPrice: number; }
];
equipment: [
{ equipment: string; quantity: number; dailyRate: number; days: number; totalPrice: number; }
];
transport: {
origin: string;
destination: string;
distance: number;
cost: number;
};
items: [ /* Flexible item structure */ ];
};
isAssigned: boolean;
assignedAt: Date;
isCurrent: boolean;
isRejected: boolean;
rejectedAt: Date;
metadata: {
ipAddress: string;
userAgent: string;
};
}

dynamicQuotations Array Structure:

{
quotationId: string;
providerId: string;
items: [
{
itemId: string;
name: string;
description: string;
quantity: number;
unitPrice: number;
totalPrice: number;
category: 'LABOR' | 'MATERIAL' | 'EQUIPMENT' | 'TRANSPORT' | 'OTHER';
enabled: boolean;
}
];
transport: {
origin: string;
destination: string;
type: 'LOCAL' | 'NATIONAL' | 'INTERNATIONAL';
cost: number;
};
totalEstimatedCost: number;
notes: string;
}

auctionInflations Array Structure:

{
inflationId: string;
messageId: string;
bidId: string;
providerId: string;
inflationData: {
items: [
{
/* Original item data */
inflationPercentage: number;
originalPrice: number;
inflatedPrice: number;
}
];
totalInflatedValue: number;
};
createdBy: {
userId: string;
userName: string;
userEmail: string;
};
metadata: {
createdAt: Date;
ipAddress: string;
};
}

Indexes (Critical for Performance):

db.auction.createIndex({ auctionId: 1 }, { unique: true });
db.auction.createIndex({ orderId: 1 });
db.auction.createIndex({ assetId: 1 });
db.auction.createIndex({ createdAt: -1 });
db.auction.createIndex({ "auctionBids.providerId": 1 });
db.auction.createIndex({ "auctionBids.isCurrent": 1 });
db.auction.createIndex({ orderId: 1, "auctionBids.providerId": 1 }); // Compound
db.auction.createIndex({ orderId: 1, "auctionBids.providerId": 1, "auctionBids.isCurrent": 1 }); // Compound
db.auction.createIndex({ "dynamicQuotations.providerId": 1 });
db.auction.createIndex({ orderId: 1, "dynamicQuotations.providerId": 1 }); // Compound
db.auction.createIndex({ "auctionInflations.bidId": 1 });
db.auction.createIndex({ "auctionInflations.providerId": 1 });
db.auction.createIndex({ orderId: 1, "auctionInflations.bidId": 1 }); // Compound
db.auction.createIndex({ "auctionInflations.createdAt": -1 });

Sample Documento (Simplified):

{
"_id": ObjectId("507f1f77bcf86cd799439016"),
"auctionId": "AUCTION-12345",
"orderId": "ORDER-12345",
"assetId": "ASSET-001",
"duration": { "days": 2, "hours": 0, "minutes": 0 },
"expiredAuction": ISODate("2025-11-21T09:00:00Z"),
"durationInHours": 48,
"questions": [
{ "question": "Do you have experience with this equipment?", "order": 1, "responseType": "BOOLEAN" }
],
"auctionBids": [
{
"bidId": "BID-001",
"providerId": "PROV-001",
"providerEmail": "contact@acexperts.com",
"providerName": "AC Experts SAS",
"offerData": {
"totalOfferValue": 500000,
"laborCost": 200000,
"materials": [
{ "item": "Refrigerant R410A", "quantity": 2, "unitPrice": 100000, "totalPrice": 200000 }
],
"transport": {
"origin": "Warehouse A",
"destination": "Client Site",
"distance": 15,
"cost": 50000
}
},
"isAssigned": true,
"assignedAt": ISODate("2025-11-19T15:00:00Z"),
"isCurrent": true,
"isRejected": false
}
],
"createdAt": ISODate("2025-11-19T09:00:00Z"),
"updatedAt": ISODate("2025-11-19T15:00:00Z")
}

Documentos Collection

Propósito: Track provider Document validation

Key Fields:

  • _id (ObjectId): MongoDB primary key
  • providerId (string): Associated provider
  • DocumentoType (enum): RUT, CAMARA_COMERCIO, CEDULA, CERT_BANCARIA, POLIZA, ARL
  • fileUrl (string): Documento URL (Azure Blob Storage)
  • Estado (enum): Pendiente, APPROVED, REJECTED, EXPIRED
  • expirationDate (Date): Documento expiration
  • validatedBy (string): Admin who validated
  • validatedAt (Date): Validation timestamp
  • createdAt (Date): Upload timestamp
  • updatedAt (Date): Last update timestamp

Indexes:

db.documents.createIndex({ providerId: 1 });
db.documents.createIndex({ documentType: 1 });
db.documents.createIndex({ status: 1 });
db.documents.createIndex({ expirationDate: 1 });

Documento de Ejemplo:

{
"_id": ObjectId("507f1f77bcf86cd799439017"),
"providerId": "PROV-001",
"documentType": "RUT",
"fileUrl": "https://storage.azure.com/.../rut-prov-001.pdf",
"status": "APPROVED",
"expirationDate": ISODate("2026-12-31T00:00:00Z"),
"validatedBy": "admin-001",
"validatedAt": ISODate("2025-01-15T10:30:00Z"),
"createdAt": ISODate("2025-01-10T14:00:00Z"),
"updatedAt": ISODate("2025-01-15T10:30:00Z")
}

service_providers Collection

Propósito: Junction table for provider-service associations

Key Fields:

  • _id (ObjectId): MongoDB primary key
  • providerId (string): Provider ID
  • serviceId (string): Service ID
  • assignedAt (Date): Association timestamp

Indexes:

db.service_providers.createIndex({ providerId: 1 });
db.service_providers.createIndex({ serviceId: 1 });
db.service_providers.createIndex({ providerId: 1, serviceId: 1 }, { unique: true }); // Prevent duplicates

Documento de Ejemplo:

{
"_id": ObjectId("507f1f77bcf86cd799439018"),
"providerId": "PROV-001",
"serviceId": "SERVICE-HVAC",
"assignedAt": ISODate("2024-01-10T10:00:00Z")
}

Reference: algesta-ms-provider-nestjs/src/domain/entities/

5. Base de datos Connection Configuration

All Microservicios use consistent MongoDB connection configuration.

Connection String Format

mongodb://[username:password@]host[:port]/database[?options]

Example:

mongodb://localhost:27017/orders
mongodb://user:pass@mongodb.example.com:27017/orders?replicaSet=rs0

Connection Pool Settings

{
maxPoolSize: 20, // Maximum connections in pool (configurable via DB_POOL_MAX)
minPoolSize: 5, // Minimum connections in pool (configurable via DB_POOL_MIN)
maxIdleTimeMS: 30000, // Max idle time before connection closed (30 seconds)
connectTimeoutMS: 2000, // Connection timeout (2 seconds)
socketTimeoutMS: 45000, // Socket timeout (45 seconds)
serverSelectionTimeoutMS: 5000, // Server selection timeout (5 seconds)
}

Mongoose Options

{
useNewUrlParser: true, // Use new MongoDB connection string parser
useUnifiedTopology: true, // Use unified topology engine
autoIndex: true, // Build indexes on startup (disable in production for large collections)
serverSelectionTimeoutMS: 5000,
socketTimeoutMS: 45000,
family: 4, // Use IPv4
}

Production Considerations:

  • Disable autoIndex for large collections (create indexes manually)
  • Use connection string parameters for replica sets
  • Enable retryWrites for automatic retry on write failures

Reference: algesta-ms-orders-nestjs/src/shared/infrastructure/Base de datos/Base de datos.config.ts and similar files

6. Index Strategy

Strategic indexing is critical for query performance in MongoDB.

Single Field Indexes

Propósito: Optimize queries on a single field

Examples:

db.orders.createIndex({ orderId: 1 }, { unique: true }); // Unique index
db.orders.createIndex({ status: 1 }); // Regular index
db.orders.createIndex({ createdAt: -1 }); // Descending index for sorting

Use Cases:

  • Unique constraints (email, orderId)
  • Frequently queried fields (Estado, category)
  • Sorting fields (createdAt, updatedAt)

Compound Indexes

Propósito: Optimize queries on multiple fields

Examples:

db.auction.createIndex({ orderId: 1, "auctionBids.providerId": 1 });
db.notifications.createIndex({ userId: 1, status: 1 });
db["order-history"].createIndex({ orderId: 1, timestamp: -1 });

Best Practices:

  • Order matters: Most selective field first
  • Include sort fields at the end
  • Cover common query patterns

Query Coverage:

// This compound index covers both queries:
db.orders.createIndex({ status: 1, createdAt: -1 });
// Query 1: Filter by status
db.orders.find({ status: "NEW" });
// Query 2: Filter by status and sort by createdAt
db.orders.find({ status: "NEW" }).sort({ createdAt: -1 });

Sparse Indexes

Propósito: Index only Documentos with the indexed field

Examples:

db.assets.createIndex({ serialId: 1 }, { sparse: true });

Use Case: Optional fields that are only present in some Documentos

Benefit: Reduces index size and improves performance

Geospatial Indexes

Propósito: Enable location-based queries

Examples:

db.locations.createIndex({ coordinates: "2dsphere" });

Queries Supported:

  • Find nearby locations
  • Find locations within radius
  • Find locations within polygon

Query Example:

db.locations.find({
coordinates: {
$near: {
$geometry: { type: "Point", coordinates: [-74.0721, 4.7110] },
$maxDistance: 5000 // 5 km
}
}
});

Text Indexes

Propósito: Full-text search

Examples:

db.orders.createIndex({ description: "text", service: "text" });

Query Example:

db.orders.find({ $text: { $search: "air conditioning repair" } });

TTL Indexes

Propósito: Automatically delete Documentos after expiration

Examples:

db.notifications.createIndex({ createdAt: 1 }, { expireAfterSeconds: 7776000 }); // 90 days

Use Cases:

  • Notification retention (90 days)
  • Session data expiration
  • Temporary data cleanup

7. Data Relationships

MongoDB offers flexible relationship patterns through embedding and referencing.

Embedded Documentos (Denormalization)

When to Use:

  • Data accessed together
  • Small data size
  • One-to-few relationships
  • Strong ownership

Examples:

  • assignedProvider in Order (provider info accessed with order)
  • workProcess in Order (workflow data is part of order)
  • auctionBids in Auction (bids belong to auction)

Advantages:

  • Single query retrieval
  • Atomic updates
  • Better performance for reads

Disadvantages:

  • Documento size limits (16 MB)
  • Data duplication
  • Update complexity

Example:

{
"orderId": "ORDER-12345",
"assignedProvider": {
"providerId": "PROV-001",
"providerName": "AC Experts SAS",
"providerEmail": "contact@acexperts.com"
}
}

References (Normalization)

When to Use:

  • Large Documentos
  • Many-to-many relationships
  • Data updated frequently
  • Shared data

Examples:

  • assetIds array in Order (references assets collection)
  • providerId in Auction (references providers collection)

Advantages:

  • No data duplication
  • Smaller Documentos
  • Easier updates

Disadvantages:

  • Multiple queries or $lookup
  • Application-level joins

Example:

{
"orderId": "ORDER-12345",
"assetIds": ["ASSET-001", "ASSET-002"]
}

Query with $lookup:

db.orders.aggregate([
{ $match: { orderId: "ORDER-12345" } },
{
$lookup: {
from: "assets",
localField: "assetIds",
foreignField: "serialId",
as: "assets"
}
}
]);

8. Data Consistency Strategies

Ensuring data consistency across Microservicios and collections.

Strong Consistency (Within Service)

Multi-Documento Transactions: MongoDB supports ACID transactions for Operaciones across multiple collections within a Base de datos.

Example:

const session = await mongoose.startSession();
session.startTransaction();
try {
// Create order
const order = await Order.create([orderData], { session });
// Create order history
await OrderHistory.create([{
orderId: order.orderId,
action: 'CREATED',
userId: user.userId
}], { session });
await session.commitTransaction();
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
session.endSession();
}

Use Cases:

  • Order creation + history record
  • Provider assignment + order update
  • Payment registration + order update

Eventual Consistency (Cross-Service)

Event-Driven Synchronization: Data synchronized across services via Kafka/Redis events.

Example Flow:

  1. Orders MS creates order
  2. Publishes order.created event
  3. Provider MS receives event
  4. Updates denormalized order data

Trade-offs:

  • Temporary inconsistency (acceptable for most use cases)
  • Better performance and scalability
  • Services remain loosely coupled

9. Backup and Recovery

Comprehensive backup strategy ensures data safety.

Backup Schedule

Full Backups:

  • Frequency: Daily at 2:00 AM
  • Retention: 30 days
  • Storage: Azure Blob Storage + on-premises

Incremental Backups:

  • Frequency: Every 6 hours
  • Retention: 7 days
  • Storage: Azure Blob Storage

Point-in-Time Recovery:

  • Enabled via oplog (replica set required)
  • Recovery window: Last 7 days

Backup Métodos

mongodump (Logical Backup):

Ventana de terminal
mongodump --uri="mongodb://localhost:27017/orders" \
--gzip \
--archive="/backups/orders-$(date +%Y%m%d).gz"

Filesystem Snapshot (Physical Backup):

  • Use with MongoDB Cloud or volume snapshots
  • Faster backup and restore
  • Requires filesystem with snapshot support

Recovery Procedures

Restore from Backup:

Ventana de terminal
mongorestore --uri="mongodb://localhost:27017/orders" \
--gzip \
--archive="/backups/orders-20251119.gz" \
--drop

Point-in-Time Recovery:

  1. Restore latest full backup
  2. Replay oplog entries to desired timestamp
  3. Validate data integrity

Disaster Recovery:

  • Failover to replica set secondary
  • Geographic replication for disaster recovery
  • Regular DR Pruebas (quarterly)

10. Performance Optimization

Strategies to maximize Base de datos performance.

Query Optimization

Use Explain:

db.orders.find({ status: "NEW" }).explain("executionStats");

Check for:

  • Index usage (should use index, not COLLSCAN)
  • Documentos examined vs returned
  • Execution time

Optimization Techniques:

  • Add indexes for frequently queried fields
  • Use projection to limit returned fields
  • Avoid $where and $regex without indexes
  • Use covered queries (index-only queries)

Documento Design

Best Practices:

  • Keep Documentos under 1 MB when possible
  • Avoid unbounded arrays (use pagination or separate collection)
  • Denormalize frequently accessed data
  • Use subDocumentos for grouped data

Anti-Patterns:

  • Massive arrays (> 1000 items)
  • Deep nesting (> 5 levels)
  • Storing files in Documentos (use GridFS or external storage)

Connection Management

Connection Pool Best Practices:

  • Size pool based on concurrent connections needed
  • Monitor pool utilization
  • Avoid creating new connections per request

Configuration:

{
maxPoolSize: 20, // Adjust based on load
minPoolSize: 5, // Keep minimum connections ready
}

Caching

Redis Cache Strategy:

  • Cache frequently accessed data (order details, provider profiles)
  • Cache query results
  • TTL-based expiration (5-60 minutes)
  • Cache invalidation on updates

Cache Patterns:

  • Read-Through: Check cache, then Base de datos
  • Write-Through: Update cache and Base de datos
  • Cache-Aside: Application manages cache

11. Monitoring and Métricas

Track Base de datos health and performance.

Key Métricas

Performance Métricas:

  • Query execution time (p50, p95, p99)
  • Slow query count (> 100ms)
  • Operaciones per second (reads, writes)
  • Index hit ratio

Resource Métricas:

  • Connection count (current, available)
  • Memory usage (resident, virtual)
  • Disk I/O (reads/sec, writes/sec)
  • CPU usage

Replication Métricas (if using replica sets):

  • Replication lag
  • Oplog window
  • Member health

Alerting

Critical Alerts:

  • High connection count (> 80% of max)
  • Slow queries (> 1000ms)
  • Replication lag (> 10 seconds)
  • Disk space (> 80% used)

Warning Alerts:

  • Connection count (> 60% of max)
  • Query time (> 500ms)
  • Memory usage (> 70%)

Slow Query Logging

Enable Profiling:

db.setProfilingLevel(1, { slowms: 100 }); // Log queries > 100ms

View Slow Queries:

db.system.profile.find().sort({ ts: -1 }).limit(10);

12. Migration Strategy

Handle schema changes and data migrations.

Schema Versioning

Version Field: Add version field to Documentos for backward compatibility.

{
"orderId": "ORDER-12345",
"schemaVersion": 2,
...
}

Migration Script:

db.orders.find({ schemaVersion: { $lt: 2 } }).forEach(doc => {
db.orders.updateOne(
{ _id: doc._id },
{
$set: { schemaVersion: 2, newField: defaultValue },
$unset: { oldField: "" }
}
);
});

Migration Process

1. Create Migration Script:

  • Documento changes
  • Write migration code
  • Test on copy of production data

2. Test on Staging:

  • Run migration on staging environment
  • Verify data integrity
  • Check application functionality

3. Run on Production:

  • Schedule during maintenance window
  • Take backup before migration
  • Run migration script
  • Monitor for errors

4. Verify and Monitor:

  • Verify data integrity
  • Check application logs
  • Monitor performance

Rollback Plan

Preparation:

  • Full backup before migration
  • Reverse migration script
  • Tested rollback procedure

Rollback Steps:

  1. Stop application
  2. Restore from backup (if needed)
  3. Run reverse migration script
  4. Restart application
  5. Verify functionality

14. References

Entity Files:

  • Orders MS: algesta-ms-orders-nestjs/src/domain/entities/
  • Notifications MS: algesta-ms-notifications-nestjs/src/domain/entities/
  • Provider MS: algesta-ms-provider-nestjs/src/domain/entities/

Base de datos Configuration:

  • Orders MS: algesta-ms-orders-nestjs/src/shared/infrastructure/Base de datos/
  • Notifications MS: algesta-ms-notifications-nestjs/src/shared/infrastructure/Base de datos/
  • Provider MS: algesta-ms-provider-nestjs/src/shared/infrastructure/Base de datos/