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.
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
Print Jobs Domain¶
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)
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
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()
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¶
- Event Naming: Use lowercase with dots as separators (e.g.,
order.created) - Payload Design: Include enough context for consumers to act without additional queries
- Idempotency: Event handlers should be idempotent where possible
- Error Handling: Always wrap event handlers in try-catch with proper logging
- Async Processing: Use
{ async: true }for handlers that don't need to block the emitter