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.jsonpara 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.jsonalgesta-ms-notifications-nestjs/package.jsonalgesta-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ón | Propósito | Tamaño Prom/Doc | Cantidad Registros (Est.) | Índices |
|---|---|---|---|---|
| orders | Documentos principales de órdenes | 5-50 KB | 10,000+ | 8 índices |
| assets | Inventario de activos | 2-5 KB | 5,000+ | 12 índices |
| locations | Datos de ubicación | 0.5-1 KB | 2,000+ | 3 índices |
| messages | Mensajes de comunicación | 0.5-1 KB | 50,000+ | 4 índices |
| technicians | Perfiles de técnicos | 1-2 KB | 100+ | 2 índices |
| users | Cuentas de usuario | 1-2 KB | 1,000+ | 3 índices |
| companies | Información de empresas | 1-3 KB | 500+ | 2 índices |
| order-history | Rastro de auditoría | 0.5-2 KB | 100,000+ | 3 índices |
| forms | Plantillas de formularios | Variable | 50+ | 2 índices |
| category-assets | Categorías de activos | 0.2-0.5 KB | 20+ | 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 MongoDBorderId(string, único): Identificador de negocio (ej., ORDER-12345)channel(enum): Origen de la orden (CALL_CENTER, WEB, MOBILE, ADMIN)service(string): Servicio solicitadoaddress(string): Ubicación del serviciocity(string): CiudadDescripción(string): Detalles de la ordenEstado(enum): Estado actual (NEW, QUOTE_SENT, ASSIGNED, COMPLETED, etc.)typeOrder(enum): Tipo de orden (WARRANTY, MAINTENANCE, REPAIR, INSTALLATION)phoneNumber(string): Teléfono de contactoemailContact(string): Email de contactoassignedProvider(objeto embebido): Detalles de asignación del proveedorworkProcess(objeto embebido): Seguimiento del ciclo de vida del trabajoadvance(objeto embebido): Seguimiento de pago anticipadoquote(objeto embebido): Gestión de cotizaciónguarantees(objeto embebido): Contratos y pólizasassetIds(arreglo de strings): Referencias a la colección de activostechnicalVisit(objeto embebido): Flujo de visita técnicaproviderRating(objeto embebido): Retroalimentación del clientecreatedAt(Date): Marca de tiempo de creaciónupdatedAt(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 keyserialNumber(string): Manufacturer serial numberserialId(string, sparse): Internal asset IDcategory(string): Asset categoryname(string): Asset namebrand(string): Manufacturer brandmodel(string): Model numberassetEstado(enum): operational, operational_with_faults, maintenance, out_of_service, retiredcity(string): Asset location cityaddress(string): Asset addressassetLocation(string): Specific locationlocation(object): Geolocation {latitude, longitude}clientPrioridad(enum): high, medium, lowclientImportance(enum): high, medium, lowassetValorPrioridad(enum): high, medium, lowacquisitionDate(Date): Purchase datepurchasePrice(number): Original pricecurrentAssetValor(number): Current ValorpreventiveMaintenanceValor(number): Preventive maintenance costcorrectiveMaintenanceValor(number): Corrective maintenance costcreatedAt(Date): Creation timestampupdatedAt(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 keyorderId(string): Associated order IDaction(string): Action performed (CREATED, UPDATED, Estado_CHANGED, ASSIGNED, etc.)userId(string): User who performed actionuserName(string): User nameuserEmail(string): User emailtimestamp(Date): Action timestampchanges(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ón | Propósito | Tamaño Prom/Doc | Cantidad Registros (Est.) | Índices |
|---|---|---|---|---|
| notifications | Registros de notificaciones | 0.5-1 KB | 100,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 keyuserId(string): User ID receiving notificationtitle(string): Notification title/subjectmessage(string): Notification contentEstado(enum): NOT_VIEWED, VIEWEDcreatedAt(Date): Creation timestampupdatedAt(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 indexTTL Index (Auto-delete old notifications):
db.notifications.createIndex({ createdAt: 1 }, { expireAfterSeconds: 7776000 }); // 90 daysDocumento 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ón | Propósito | Tamaño Prom/Doc | Cantidad Registros (Est.) | Índices |
|---|---|---|---|---|
| providers | Perfiles de proveedores | 1-2 KB | 500+ | 3 índices |
| auction | Registros de subastas con ofertas | 10-100 KB | 10,000+ | 14 índices |
| Documentos | Registros de documentos | 0.5-1 KB | 5,000+ | 4 índices |
| services | Servicios disponibles | 0.5-1 KB | 50+ | 3 índices |
| service_providers | Mapeo proveedor-servicio | 0.2 KB | 2,000+ | 2 índices |
| users | Cuentas de usuario | 1-2 KB | 1,500+ | 3 índices |
| orders | Datos de órdenes desnormalizados | Variable | 10,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 keyname(string): Provider first namelastName(string): Provider last nameemail(string, unique): Provider emailidentification(string): Tax IDphone(string): Contact phonecity(string): Provider citycountry(string): Provider countryaddress(string): Physical addresslocation(object): {latitude, longitude}bank(string): Bank nametypeAccount(enum): SAVINGS, CHECKINGnumberAccount(string): Account numberpassword(string): Hashed passwordrole(enum): PROVIDERisActivo(boolean): Account Activo EstadocreatedAt(Date): Registration timestampupdatedAt(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 keyauctionId(string, unique): Business identifierorderId(string): Associated order IDassetId(string): Associated asset IDduration(object): {days, hours, minutes}expiredAuction(Date): Expiration timestampdurationInHours(number): Total durationquestions(array): Custom questions for providersauctionBids(array): Provider bids (see detailed structure below)dynamicQuotations(array): Dynamic quotation structuresauctionInflations(array): Price inflations by adminprice(object): Final price DocumentoationcreatedAt(Date): Creation timestampupdatedAt(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 }); // Compounddb.auction.createIndex({ orderId: 1, "auctionBids.providerId": 1, "auctionBids.isCurrent": 1 }); // Compounddb.auction.createIndex({ "dynamicQuotations.providerId": 1 });db.auction.createIndex({ orderId: 1, "dynamicQuotations.providerId": 1 }); // Compounddb.auction.createIndex({ "auctionInflations.bidId": 1 });db.auction.createIndex({ "auctionInflations.providerId": 1 });db.auction.createIndex({ orderId: 1, "auctionInflations.bidId": 1 }); // Compounddb.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 keyproviderId(string): Associated providerDocumentoType(enum): RUT, CAMARA_COMERCIO, CEDULA, CERT_BANCARIA, POLIZA, ARLfileUrl(string): Documento URL (Azure Blob Storage)Estado(enum): Pendiente, APPROVED, REJECTED, EXPIREDexpirationDate(Date): Documento expirationvalidatedBy(string): Admin who validatedvalidatedAt(Date): Validation timestampcreatedAt(Date): Upload timestampupdatedAt(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 keyproviderId(string): Provider IDserviceId(string): Service IDassignedAt(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 duplicatesDocumento 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/ordersmongodb://user:pass@mongodb.example.com:27017/orders?replicaSet=rs0Connection 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
autoIndexfor 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 indexdb.orders.createIndex({ status: 1 }); // Regular indexdb.orders.createIndex({ createdAt: -1 }); // Descending index for sortingUse 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 statusdb.orders.find({ status: "NEW" });
// Query 2: Filter by status and sort by createdAtdb.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 daysUse 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:
assignedProviderin Order (provider info accessed with order)workProcessin Order (workflow data is part of order)auctionBidsin 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:
assetIdsarray in Order (references assets collection)providerIdin 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:
- Orders MS creates order
- Publishes
order.createdevent - Provider MS receives event
- 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):
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:
mongorestore --uri="mongodb://localhost:27017/orders" \ --gzip \ --archive="/backups/orders-20251119.gz" \ --dropPoint-in-Time Recovery:
- Restore latest full backup
- Replay oplog entries to desired timestamp
- 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 > 100msView 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:
- Stop application
- Restore from backup (if needed)
- Run reverse migration script
- Restart application
- Verify functionality
13. Related Documentoation
- Backend Microservicios Descripción General
- Orders Microservicio
- Notifications Microservicio
- Provider Microservicio
- Inter-Service Communication
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/