Skip to content

Event Catalog

This document provides a comprehensive reference for all domain events in the Forma3D Connect system. Events are the primary mechanism for decoupled communication between services, enabling loose coupling and high cohesion.

Overview

The system uses NestJS EventEmitter2 for internal event-driven communication. Events flow between domains to orchestrate complex workflows like order processing, print job management, fulfillment, and shipping.

uml diagram


Orders Domain

Events related to order lifecycle management.

Source: apps/api/src/orders/events/order.events.ts

Event Name Constant Description
order.created ORDER_EVENTS.CREATED Emitted when a new order is created from a Shopify webhook
order.status_changed ORDER_EVENTS.STATUS_CHANGED Emitted when an order's status transitions to a new state
order.cancelled ORDER_EVENTS.CANCELLED Emitted when an order is cancelled (via Shopify or manually)
order.ready-for-fulfillment ORDER_EVENTS.READY_FOR_FULFILLMENT Emitted when all print jobs complete and order is ready to ship
order.fulfilled ORDER_EVENTS.FULFILLED Emitted when the order has been fulfilled in Shopify
order.failed ORDER_EVENTS.FAILED Emitted when an order processing fails permanently

OrderCreatedEvent

Emitted when a new order is ingested from Shopify.

interface OrderCreatedEvent {
  orderId: string; // Internal UUID of the order
  shopifyOrderId: string; // Shopify's order ID (e.g., "gid://shopify/Order/123")
  lineItemCount: number; // Number of line items in the order
}

Triggered by: OrdersService.createFromWebhook()
Consumed by: OrchestrationService.handleOrderCreated(), EventsGateway

OrderStatusChangedEvent

Emitted when an order transitions between statuses.

interface OrderStatusChangedEvent {
  orderId: string; // Internal UUID of the order
  previousStatus: OrderStatus; // Status before the change
  newStatus: OrderStatus; // New status after the change
}

Triggered by: OrdersService.updateStatus()
Consumed by: EventsGateway

OrderCancelledEvent

Emitted when an order is cancelled.

interface OrderCancelledEvent {
  orderId: string; // Internal UUID of the order
  reason?: string; // Optional cancellation reason
}

Triggered by: OrdersService.cancel(), ShopifyWebhookController (cancellation webhook)
Consumed by: CancellationService.handleOrderCancelled(), EventsGateway

OrderReadyForFulfillmentEvent

Emitted when all print jobs for an order are complete.

interface OrderReadyForFulfillmentEvent {
  orderId: string; // Internal UUID of the order
  shopifyOrderId: string; // Shopify's order ID for fulfillment API
}

Triggered by: OrchestrationService.markOrderReadyForFulfillment()
Consumed by: FulfillmentService, SendcloudService, EventsGateway

OrderFulfilledEvent

Emitted when the order fulfillment is created in Shopify.

interface OrderFulfilledEvent {
  orderId: string; // Internal UUID of the order
  shopifyOrderId: string; // Shopify's order ID
  fulfillmentId: string; // Shopify's fulfillment ID
}

Triggered by: FulfillmentService (after successful Shopify API call)
Consumed by: EventsGateway

OrderFailedEvent

Emitted when an order cannot be processed.

interface OrderFailedEvent {
  orderId: string; // Internal UUID of the order
  reason: string; // Failure reason
}

Triggered by: OrchestrationService (when all print jobs fail)
Consumed by: EventsGateway, NotificationsService


Events related to 3D print job lifecycle.

Source: apps/api/src/print-jobs/events/print-job.events.ts

Event Name Constant Description
printjob.created PRINT_JOB_EVENTS.CREATED Emitted when a new print job is created and queued
printjob.status-changed PRINT_JOB_EVENTS.STATUS_CHANGED Emitted when a print job's status changes
printjob.completed PRINT_JOB_EVENTS.COMPLETED Emitted when a print job finishes successfully
printjob.failed PRINT_JOB_EVENTS.FAILED Emitted when a print job fails
printjob.cancelled PRINT_JOB_EVENTS.CANCELLED Emitted when a print job is cancelled
printjob.retry-requested PRINT_JOB_EVENTS.RETRY_REQUESTED Emitted when a retry is requested for a failed job

PrintJobCreatedEvent

Emitted when a print job is created for a line item.

interface PrintJobCreatedEvent {
  printJob: PrintJob; // Full PrintJob entity
  orderId: string; // Parent order ID
  lineItemId: string; // Parent line item ID
  simplyPrintJobId?: string; // SimplyPrint job ID (if already submitted)
}

Triggered by: PrintJobsService.createPrintJobsForLineItem()
Consumed by: EventsGateway

PrintJobStatusChangedEvent

Emitted on any status transition (general-purpose event).

interface PrintJobStatusChangedEvent {
  printJob: PrintJob; // Full PrintJob entity
  previousStatus: PrintJobStatus; // Status before change
  newStatus: PrintJobStatus; // New status
  orderId: string; // Parent order ID
}

Triggered by: PrintJobsService.updateJobStatus()
Consumed by: OrchestrationService.handlePrintJobStatusChanged(), EventsGateway

PrintJobCompletedEvent

Emitted when a print job completes successfully.

interface PrintJobCompletedEvent {
  printJob: PrintJob; // Full PrintJob entity
  orderId: string; // Parent order ID
}

Triggered by: PrintJobsService.updateJobStatus() (when status becomes COMPLETED)
Consumed by: OrchestrationService.handlePrintJobCompleted(), EventsGateway

PrintJobFailedEvent

Emitted when a print job fails.

interface PrintJobFailedEvent {
  printJob: PrintJob; // Full PrintJob entity
  orderId: string; // Parent order ID
  errorMessage?: string; // Error description from the printer
}

Triggered by: PrintJobsService.updateJobStatus() (when status becomes FAILED)
Consumed by: OrchestrationService.handlePrintJobFailed(), NotificationsService, EventsGateway

PrintJobCancelledEvent

Emitted when a print job is cancelled.

interface PrintJobCancelledEvent {
  printJob: PrintJob; // Full PrintJob entity
  orderId: string; // Parent order ID
  reason?: string; // Cancellation reason
}

Triggered by: PrintJobsService.cancelJob()
Consumed by: EventsGateway

PrintJobRetryRequestedEvent

Emitted when an operator requests a retry for a failed job.

interface PrintJobRetryRequestedEvent {
  printJob: PrintJob; // Full PrintJob entity
  orderId: string; // Parent order ID
  retryCount: number; // Current retry attempt number
}

Triggered by: PrintJobsService.retryJob()
Consumed by: Logging, monitoring systems


Orchestration Domain

Events for workflow coordination between orders and print jobs.

Source: apps/api/src/orchestration/orchestration.service.ts

Event Name Constant Description
order.ready-for-fulfillment ORCHESTRATION_EVENTS.ORDER_READY_FOR_FULFILLMENT Emitted when all print jobs complete successfully
order.partially-completed ORCHESTRATION_EVENTS.ORDER_PARTIALLY_COMPLETED Emitted when some jobs succeed but others fail
order.all-jobs-failed ORCHESTRATION_EVENTS.ORDER_ALL_JOBS_FAILED Emitted when all print jobs for an order fail

OrderReadyForFulfillmentEvent (Orchestration)

Same structure as the Orders domain event - emitted when orchestration determines all jobs are complete.

interface OrderReadyForFulfillmentEvent {
  orderId: string;
  shopifyOrderId: string;
}

Triggered by: OrchestrationService.markOrderReadyForFulfillment()
Consumed by: FulfillmentService, SendcloudService

OrderPartiallyCompletedEvent

Emitted when an order has mixed results (some jobs succeeded, some failed).

interface OrderPartiallyCompletedEvent {
  orderId: string;
  completedCount: number; // Number of successful jobs
  failedCount: number; // Number of failed jobs
}

Triggered by: OrchestrationService.markOrderPartiallyCompleted()
Consumed by: NotificationsService (operator alert)

OrderAllJobsFailedEvent

Emitted when all print jobs for an order have failed.

interface OrderAllJobsFailedEvent {
  orderId: string;
}

Triggered by: OrchestrationService.markOrderFailed()
Consumed by: NotificationsService (operator alert)

uml diagram


Fulfillment Domain

Events related to Shopify fulfillment creation.

Source: apps/api/src/fulfillment/events/fulfillment.events.ts

Event Name Constant Description
fulfillment.created FULFILLMENT_EVENTS.CREATED Emitted when a Shopify fulfillment is successfully created
fulfillment.failed FULFILLMENT_EVENTS.FAILED Emitted when fulfillment creation fails
fulfillment.retrying FULFILLMENT_EVENTS.RETRYING Emitted when a failed fulfillment is scheduled for retry

FulfillmentCreatedEvent

Emitted after successfully creating a fulfillment in Shopify.

class FulfillmentCreatedEvent {
  constructor(
    public readonly order: Order, // Full Order entity
    public readonly fulfillmentId: string // Shopify fulfillment ID
  ) {}
}

Triggered by: FulfillmentService.createFulfillment()
Consumed by: Logging, notifications

FulfillmentFailedEvent

Emitted when Shopify fulfillment API call fails.

class FulfillmentFailedEvent {
  constructor(
    public readonly order: Order, // Full Order entity
    public readonly error: string, // Error message
    public readonly willRetry: boolean, // Whether retry is scheduled
    public readonly shopifyOrderNumber?: string // Shopify order number for display
  ) {}
}

Triggered by: FulfillmentService.createFulfillment() (on error)
Consumed by: NotificationsService (operator alert when willRetry is false)

FulfillmentRetryingEvent

Emitted when a failed fulfillment is queued for retry.

class FulfillmentRetryingEvent {
  constructor(
    public readonly orderId: string, // Order being retried
    public readonly attemptNumber: number, // Which attempt this is
    public readonly nextAttemptAt: Date // When retry will occur
  ) {}
}

Triggered by: RetryQueueService
Consumed by: Logging


Shipment Domain

Events related to SendCloud shipping integration.

Source: apps/api/src/sendcloud/events/shipment.events.ts

Event Name Constant Description
shipment.created SHIPMENT_EVENTS.CREATED Emitted when a shipping label is created in SendCloud
shipment.label-ready SHIPMENT_EVENTS.LABEL_READY Emitted when the shipping label PDF is available
shipment.failed SHIPMENT_EVENTS.FAILED Emitted when shipment creation fails
shipment.updated SHIPMENT_EVENTS.UPDATED Emitted when shipment tracking status changes

ShipmentCreatedEvent

Emitted when a new shipment is created in SendCloud.

class ShipmentCreatedEvent {
  constructor(
    public readonly shipment: Shipment, // Full Shipment entity with tracking info
    public readonly order: Order // Associated order
  ) {}
}

Triggered by: SendcloudService.createShipment()
Consumed by: FulfillmentService.handleShipmentCreated() (triggers Shopify fulfillment with tracking)

ShipmentLabelReadyEvent

Emitted when the shipping label PDF URL is available.

class ShipmentLabelReadyEvent {
  constructor(
    public readonly shipment: Shipment, // Full Shipment entity
    public readonly labelUrl: string // URL to download the label PDF
  ) {}
}

Triggered by: SendcloudService.createShipment()
Consumed by: Dashboard display, operator workflows

ShipmentFailedEvent

Emitted when SendCloud API call fails.

class ShipmentFailedEvent {
  constructor(
    public readonly orderId: string, // Associated order ID
    public readonly error: string, // Error message
    public readonly willRetry: boolean // Whether retry is scheduled
  ) {}
}

Triggered by: SendcloudService.createShipment() (on error)
Consumed by: NotificationsService (operator alert when willRetry is false)

ShipmentUpdatedEvent

Emitted when tracking status changes via webhook.

class ShipmentUpdatedEvent {
  constructor(
    public readonly shipment: Shipment, // Updated Shipment entity
    public readonly previousStatus: string // Status before update
  ) {}
}

Triggered by: SendcloudWebhookController
Consumed by: Tracking display updates

uml diagram


SimplyPrint Integration

Events bridging the external SimplyPrint print farm with internal state.

Source: apps/api/src/simplyprint/simplyprint.service.ts

Event Name Constant Description
simplyprint.job-status-changed SIMPLYPRINT_EVENTS.JOB_STATUS_CHANGED Emitted when SimplyPrint reports a job status change

SimplyPrintJobStatusChangedEvent

Emitted when a status update is received from SimplyPrint (via webhook or polling).

interface SimplyPrintJobStatusChangedEvent {
  simplyPrintJobId: string; // SimplyPrint's job UID
  previousStatus?: PrintJobStatus; // Previous internal status (if known)
  newStatus: PrintJobStatus; // New internal status (mapped from SimplyPrint)
  printerId?: string; // Printer ID performing the job
  printerName?: string; // Printer name for display
  errorMessage?: string; // Error message if job failed
  timestamp: Date; // When the status change occurred
}

Triggered by:

  • SimplyPrintService.handleWebhook() (from SimplyPrint webhook)
  • SimplyPrintService.pollJobStatuses() (from scheduled polling)

Consumed by: PrintJobsService.handleSimplyPrintStatusChange()

uml diagram


WebSocket Events

The EventsGateway listens to internal events and broadcasts them to connected clients via Socket.IO.

Source: apps/api/src/gateway/events.gateway.ts

Internal Event WebSocket Event Description
order.created order:created New order notification
order.status_changed order:updated Order status update
order.ready-for-fulfillment order:ready-for-fulfillment Order ready to ship
order.fulfilled order:fulfilled Order shipped
order.cancelled order:cancelled Order cancelled
order.failed order:failed Order processing failed
printjob.created printjob:created New print job
printjob.status-changed printjob:updated Print job status update
printjob.completed printjob:completed Print job finished
printjob.failed printjob:failed Print job failed
printjob.cancelled printjob:cancelled Print job cancelled

Event Best Practices

  1. Event Naming: Use lowercase with dots as separators (e.g., order.created)
  2. Payload Design: Include enough context for consumers to act without additional queries
  3. Idempotency: Event handlers should be idempotent where possible
  4. Error Handling: Always wrap event handlers in try-catch with proper logging
  5. Async Processing: Use { async: true } for handlers that don't need to block the emitter