Saltearse al contenido

Diagrama de Componentes - Microservicio de Proveedores

Descripción General

El Microservicio de Proveedores gestiona el ciclo de vida completo del proveedor, incluyendo registro, validación de documentos, participación en subastas e informes de ejecución. Implementa Clean Architecture con patrón CQRS y maneja flujos de trabajo de negocio complejos como el sistema de subastas del marketplace.

El microservicio maneja:

  • Registro e incorporación de proveedores
  • Carga, validación y gestión de expiración de documentos
  • Creación y gestión de subastas
  • Envío de ofertas y selección del ganador
  • Envío de informes de ejecución
  • Calificación y estadísticas de proveedores

Diagrama de Componentes

graph TB
    subgraph "Capa de Aplicación"
        ProviderController["ProviderController<br/>[REST Controller]<br/>Endpoints de gestión de proveedores"]
        AuctionController["AuctionController<br/>[REST Controller]<br/>Endpoints de subasta y marketplace"]

        CreateProviderHandler["CreateProviderHandler<br/>[Command Handler]<br/>Registra nuevo proveedor"]
        UploadDocumentoHandler["UploadDocumentoHandler<br/>[Command Handler]<br/>Sube documento del proveedor a Azure Blob Storage"]
        UpdateDocumentoEstadoHandler["UpdateDocumentoEstadoHandler<br/>[Command Handler]<br/>Aprueba/rechaza documentos del proveedor"]
        PublishAuctionHandler["PublishAuctionHandler<br/>[Command Handler]<br/>Publica orden en subasta del marketplace"]
        SubmitOfferHandler["SubmitOfferHandler<br/>[Command Handler]<br/>Proveedor envía oferta de subasta"]
        SelectWinnerHandler["SelectWinnerHandler<br/>[Command Handler]<br/>Agente selecciona ganador de subasta"]
        SubmitQuotationHandler["SubmitQuotationHandler<br/>[Command Handler]<br/>Proveedor envía cotización de ítem"]
        SubmitExecutionReportHandler["SubmitExecutionReportHandler<br/>[Command Handler]<br/>Proveedor envía informe de ejecución"]

        GetProviderByIdHandler["GetProviderByIdHandler<br/>[Query Handler]<br/>Recupera detalles del proveedor"]
        ListProvidersHandler["ListProvidersHandler<br/>[Query Handler]<br/>Lista proveedores con filtros"]
        GetProviderDocumentosHandler["GetProviderDocumentosHandler<br/>[Query Handler]<br/>Recupera documentos del proveedor"]
        ListAuctionsHandler["ListAuctionsHandler<br/>[Query Handler]<br/>Lista subastas disponibles"]
        GetAuctionOffersHandler["GetAuctionOffersHandler<br/>[Query Handler]<br/>Recupera ofertas para subasta"]

        ProviderCreatedEventHandler["ProviderCreatedEventHandler<br/>[Event Handler]<br/>Maneja evento ProviderCreated"]
        DocumentoEstadoChangedEventHandler["DocumentoEstadoChangedEventHandler<br/>[Event Handler]<br/>Maneja cambios de estado de documento"]
        AuctionPublishedEventHandler["AuctionPublishedEventHandler<br/>[Event Handler]<br/>Maneja evento AuctionPublished"]
        OfferSubmittedEventHandler["OfferSubmittedEventHandler<br/>[Event Handler]<br/>Maneja evento OfferSubmitted"]
        OrderClosedEventHandler["OrderClosedEventHandler<br/>[Event Handler]<br/>Actualiza calificación de proveedor al cerrar orden"]

        DocumentoExpiryCheckTask["DocumentoExpiryCheckTask<br/>[Tarea Programada]<br/>Verificación diaria de documentos que expiran/expirados"]
    end

    subgraph "Capa de Dominio"
        ProviderEntity["Provider Entity<br/>[Aggregate Root]<br/>Modelo de dominio de proveedor"]
        ProviderDocumentoVO["ProviderDocumento<br/>[Objeto de Valor]<br/>Metadata de documento"]
        ProviderEstadoEnum["ProviderEstado<br/>[Enum]<br/>PENDING_DOCUMENTS, ACTIVE, SUSPENDED, REJECTED"]
        AuctionEntity["Auction Entity<br/>[Aggregate Root]<br/>Modelo de dominio de subasta"]
        AuctionOfferVO["AuctionOffer<br/>[Objeto de Valor]<br/>Oferta del proveedor en subasta"]
        AuctionEstadoEnum["AuctionEstado<br/>[Enum]<br/>DRAFT, PUBLISHED, BIDDING, CLOSED, CANCELLED"]
        ProviderRepositorio["ProviderRepositorio<br/>[Interface]<br/>Abstracción de repositorio"]
        AuctionRepositorio["AuctionRepositorio<br/>[Interface]<br/>Abstracción de repositorio"]
    end

    subgraph "Capa de Infraestructura"
        ProviderRepositorioImpl["ProviderRepositorioImpl<br/>[Implementación Mongoose]<br/>Persistencia MongoDB para proveedores"]
        AuctionRepositorioImpl["AuctionRepositorioImpl<br/>[Implementación Mongoose]<br/>Persistencia MongoDB para subastas"]
        AzureBlobStorageService["AzureBlobStorageService<br/>[Servicio Externo]<br/>Sube/descarga documentos desde Azure Blob"]
        DocumentoValidationService["DocumentoValidationService<br/>[Servicio de Dominio]<br/>Valida tipos de documento y expiración"]
        PdfGenerationService["PdfGenerationService<br/>[Servicio de Infraestructura]<br/>Genera informes de proveedor usando Puppeteer"]
        MessageBrokerService["MessageBrokerService<br/>[Publicador de Eventos]<br/>Publica eventos a Redis/Kafka"]
    end

    subgraph "Dependencias Externas"
        MongoDB[("MongoDB<br/>[Base de datos]<br/>Base de datos de proveedores")]
        BlobStorage[("Azure Blob Storage<br/>[Almacenamiento]<br/>Almacenamiento de documentos")]
        RedisKafka[("Redis/Kafka<br/>[Message Broker]<br/>Transmisión de eventos")]
    end

    ProviderController --> CreateProviderHandler
    ProviderController --> UploadDocumentoHandler
    ProviderController --> UpdateDocumentoEstadoHandler
    ProviderController --> SubmitQuotationHandler
    ProviderController --> SubmitExecutionReportHandler
    ProviderController --> GetProviderByIdHandler
    ProviderController --> ListProvidersHandler
    ProviderController --> GetProviderDocumentosHandler

    AuctionController --> PublishAuctionHandler
    AuctionController --> SubmitOfferHandler
    AuctionController --> SelectWinnerHandler
    AuctionController --> ListAuctionsHandler
    AuctionController --> GetAuctionOffersHandler

    CreateProviderHandler --> ProviderEntity
    CreateProviderHandler --> ProviderRepositorio
    CreateProviderHandler --> MessageBrokerService
    UploadDocumentoHandler --> ProviderEntity
    UploadDocumentoHandler --> ProviderDocumentoVO
    UploadDocumentoHandler --> ProviderRepositorio
    UploadDocumentoHandler --> AzureBlobStorageService
    UploadDocumentoHandler --> DocumentoValidationService
    UpdateDocumentoEstadoHandler --> ProviderRepositorio
    UpdateDocumentoEstadoHandler --> MessageBrokerService

    PublishAuctionHandler --> AuctionEntity
    PublishAuctionHandler --> AuctionRepositorio
    PublishAuctionHandler --> MessageBrokerService
    SubmitOfferHandler --> AuctionEntity
    SubmitOfferHandler --> AuctionOfferVO
    SubmitOfferHandler --> AuctionRepositorio
    SubmitOfferHandler --> MessageBrokerService
    SelectWinnerHandler --> AuctionRepositorio
    SelectWinnerHandler --> MessageBrokerService

    GetProviderByIdHandler --> ProviderRepositorio
    ListProvidersHandler --> ProviderRepositorio
    GetProviderDocumentosHandler --> ProviderRepositorio
    GetProviderDocumentosHandler --> AzureBlobStorageService
    ListAuctionsHandler --> AuctionRepositorio
    GetAuctionOffersHandler --> AuctionRepositorio

    ProviderCreatedEventHandler --> MessageBrokerService
    DocumentoEstadoChangedEventHandler --> ProviderRepositorio
    AuctionPublishedEventHandler --> MessageBrokerService
    OfferSubmittedEventHandler --> MessageBrokerService
    OrderClosedEventHandler --> ProviderRepositorio

    DocumentoExpiryCheckTask --> ProviderRepositorio
    DocumentoExpiryCheckTask --> MessageBrokerService

    ProviderRepositorio --> ProviderRepositorioImpl
    AuctionRepositorio --> AuctionRepositorioImpl
    ProviderRepositorioImpl --> MongoDB
    AuctionRepositorioImpl --> MongoDB

    AzureBlobStorageService --> BlobStorage
    MessageBrokerService --> RedisKafka

    style ProviderController fill:#438dd5,stroke:#2e6295,color:#ffffff
    style AuctionController fill:#438dd5,stroke:#2e6295,color:#ffffff
    style CreateProviderHandler fill:#85bbf0,stroke:#5a8bc4,color:#000000
    style UploadDocumentoHandler fill:#85bbf0,stroke:#5a8bc4,color:#000000
    style UpdateDocumentoEstadoHandler fill:#85bbf0,stroke:#5a8bc4,color:#000000
    style PublishAuctionHandler fill:#85bbf0,stroke:#5a8bc4,color:#000000
    style SubmitOfferHandler fill:#85bbf0,stroke:#5a8bc4,color:#000000
    style SelectWinnerHandler fill:#85bbf0,stroke:#5a8bc4,color:#000000
    style SubmitQuotationHandler fill:#85bbf0,stroke:#5a8bc4,color:#000000
    style SubmitExecutionReportHandler fill:#85bbf0,stroke:#5a8bc4,color:#000000
    style GetProviderByIdHandler fill:#a8d5ba,stroke:#7cb99a,color:#000000
    style ListProvidersHandler fill:#a8d5ba,stroke:#7cb99a,color:#000000
    style GetProviderDocumentosHandler fill:#a8d5ba,stroke:#7cb99a,color:#000000
    style ListAuctionsHandler fill:#a8d5ba,stroke:#7cb99a,color:#000000
    style GetAuctionOffersHandler fill:#a8d5ba,stroke:#7cb99a,color:#000000
    style ProviderCreatedEventHandler fill:#ffd93d,stroke:#ccad30,color:#000000
    style DocumentoEstadoChangedEventHandler fill:#ffd93d,stroke:#ccad30,color:#000000
    style AuctionPublishedEventHandler fill:#ffd93d,stroke:#ccad30,color:#000000
    style OfferSubmittedEventHandler fill:#ffd93d,stroke:#ccad30,color:#000000
    style OrderClosedEventHandler fill:#ffd93d,stroke:#ccad30,color:#000000
    style DocumentoExpiryCheckTask fill:#ff9ff3,stroke:#cc7fc2,color:#000000
    style ProviderEntity fill:#6bcf7f,stroke:#56a466,color:#ffffff
    style AuctionEntity fill:#6bcf7f,stroke:#56a466,color:#ffffff
    style ProviderRepositorioImpl fill:#ff6b6b,stroke:#cc5555,color:#ffffff
    style AuctionRepositorioImpl fill:#ff6b6b,stroke:#cc5555,color:#ffffff
    style MongoDB fill:#ff6b6b,stroke:#cc5555,color:#ffffff
    style BlobStorage fill:#feca57,stroke:#cb9f46,color:#000000
    style RedisKafka fill:#48dbfb,stroke:#2e9cba,color:#000000

Figura: Diagrama de Componentes del Microservicio de Proveedores - Desglose detallado de componentes mostrando las capas de aplicación, dominio e infraestructura con Clean Architecture y patrón CQRS


Auction State Machine Diagram

stateDiagram-v2
    [*] --> DRAFT: Agent creates auction
    DRAFT --> PUBLISHED: Agent publishes to marketplace
    PUBLISHED --> BIDDING: First offer submitted
    BIDDING --> BIDDING: More offers submitted
    BIDDING --> CLOSED: Agent selects winner
    PUBLISHED --> CLOSED: Agent selects winner (early closure)
    CLOSED --> [*]: Auction Completod

    DRAFT --> CANCELLED: Agent cancels auction
    PUBLISHED --> CANCELLED: Agent cancels auction
    BIDDING --> CANCELLED: Agent cancels auction
    CANCELLED --> [*]: Auction cancelled

    note right of PUBLISHED
        Auction is visible to eligible providers
        Duration: 24-72 hours (configurable)
    end note

    note right of BIDDING
        Anonymous bidding
        Providers can submit only one offer
    end note

    note right of CLOSED
        Winner assigned to order
        Other providers notified of rejection
    end note

Figura: Auction State Machine - State transitions for the auction lifecycle, from draft creation through winner selection or cancellation


Stack Tecnológico

DependencyVersionPurpose
@nestjs/core11.xNestJS framework core
@nestjs/cqrs11.xCQRS pattern Implementación
@nestjs/mongoose11.xMongoDB integration via Mongoose
@nestjs/Microservicios11.xRedis/Kafka client for messaging
@nestjs/schedule4.xScheduled tasks for Documento expiry checks
mongoose8.xMongoDB ODM
@azure/storage-blob12.xAzure Blob Storage SDK
class-validator0.14.xDTO validation
class-transformer0.5.xDTO transformation
puppeteer23.xPDF generation for provider reports

Key Design Patterns

Clean Architecture with CQRS

Similar to Orders MS, the Provider MS implements Clean Architecture with clear layer separation:

Commands:

  • CreateProviderCommand - Registers new provider
  • UploadDocumentoCommand - Uploads provider Documento
  • UpdateDocumentoEstadoCommand - Approves/rejects Documento
  • PublishAuctionCommand - Publishes auction to marketplace
  • SubmitAuctionOfferCommand - Provider submits offer
  • SelectAuctionWinnerCommand - Agent selects winner
  • SubmitQuotationCommand - Provider submits quotation
  • SubmitExecutionReportCommand - Provider submits execution report

Queries:

  • GetProviderByIdQuery - Retrieves provider details
  • ListProvidersQuery - Lists providers with filters
  • GetProviderDocumentosQuery - Retrieves provider Documentos
  • ListAuctionsQuery - Lists available auctions
  • GetAuctionOffersQuery - Retrieves auction offers

Events:

  • ProviderCreatedEvent - Provider registered
  • DocumentoUploadedEvent - Document uploaded
  • DocumentoEstadoChangedEvent - Documento approved/rejected
  • AuctionPublishedEvent - Auction published to marketplace
  • OfferSubmittedEvent - Provider submitted offer
  • AuctionClosedEvent - Auction closed, winner selected
  • ProviderSelectedEvent - Winner selected (sent to Orders MS)
  • DocumentoExpiringEvent - Documento expiring soon
  • DocumentoExpiredEvent - Documento expired

State Machine Pattern for Auctions

The auction lifecycle is modeled as a state machine with well-defined states and transitions:

States:

  • DRAFT - Auction created but not published
  • PUBLISHED - Auction visible to providers, accepting offers
  • BIDDING - At least one offer submitted
  • CLOSED - Winner selected, auction Completod
  • CANCELLED - Auction cancelled by agent

Transitions:

  • DRAFT → PUBLISHED (publishAuction)
  • PUBLISHED → BIDDING (submitFirstOffer)
  • BIDDING → CLOSED (selectWinner)
  • PUBLISHED → CLOSED (selectWinner - early closure)
  • Any → CANCELLED (cancelAuction)

Business Rules:

  • Only Activo providers with valid Documentos can submit offers
  • Provider can submit only one offer per auction
  • Auction has configurable duration (24-72 hours)
  • Anonymous bidding (provider names hidden until agent views offer details)
  • Minimum 3 offers recommended (configurable)
  • Agent can manually close auction early

Documento Validation Strategy

Document validation is handled by a dedicated domain service:

class DocumentValidationService {
validateDocumentType(type: DocumentType, file: Express.Multer.File): ValidationResult {
// Validate file extension
// Validate MIME type
// Validate file size
return result;
}
validateExpiryDate(type: DocumentType, expiryDate: Date): ValidationResult {
// Check if document type requires expiry date
// Validate expiry date is in future
// Check minimum validity period
return result;
}
isDocumentExpiringSoon(expiryDate: Date, daysThreshold: number): boolean {
const daysUntilExpiry = differenceInDays(expiryDate, new Date());
return daysUntilExpiry <= daysThreshold;
}
}

Documento Types and Validation:

Documento TypeRequiredHas ExpiryValidation Criteria
RUT (Tax Registration)YesNoValid NIT format, PDF only
Cámara de ComercioYesYesIssued within last 30 days, PDF only
ARL (Occupational Risk Insurance)YesYesValid policy number, min 1 year validity
Póliza de Responsabilidad CivilYesYesMinimum coverage amount, min 1 year validity
Certificado BancarioYesNoValid account number, PDF only

Scheduled Tasks

Documento Expiry Check Task

The DocumentoExpiryCheckTask runs daily (via @nestjs/schedule) to check for expiring or expired Documentos:

@Injectable()
export class DocumentExpiryCheckTask {
@Cron('0 0 * * *') // Daily at midnight
async handleDocumentExpiry() {
// 1. Query all documents with expiryDate
const documents = await this.providerRepository.findDocumentsWithExpiry();
// 2. Check documents expiring in 30, 15, 7 days
const expiringDocuments = documents.filter(doc =>
this.isExpiringSoon(doc.expiryDate, [30, 15, 7])
);
// 3. Publish DocumentExpiringEvent for each
for (const doc of expiringDocuments) {
await this.eventBus.publish(new DocumentExpiringEvent(doc));
}
// 4. Check expired documents
const expiredDocuments = documents.filter(doc =>
isAfter(new Date(), doc.expiryDate)
);
// 5. Update document status to EXPIRED
// 6. Suspend provider if critical document expired
for (const doc of expiredDocuments) {
await this.commandBus.execute(
new UpdateDocumentStatusCommand(doc.id, 'EXPIRED')
);
await this.eventBus.publish(new DocumentExpiredEvent(doc));
}
}
}

Referencias Cruzadas