Architecture
1. System Context (C4 L1)
Helpdesk is a VerifierApplication — it verifies JWTs against the identity issuer's JWKS but never issues tokens. It has no synchronous outbound calls to sister microservices; cross-domain data (products, sale orders) is read through repositories shipped by
@nx/core.
2. Container View (C4 L2)
| Process | RUN_MODE | Runs |
|---|---|---|
| API | startup (default) | Hono server, controllers, EventBus, queue producers |
| Worker | worker | BullMQ workers consuming the 6 queues |
| Migration | migrate | One-shot Drizzle migration processes |
3. Component View (C4 L3) — Internal Layering
| Layer | Responsibility |
|---|---|
| Controllers | Merchant-access gate (assertMerchantAccess() + useRequestContext()), DTO mapping; delegate to use-cases. No @authenticate decorator |
| Use-Cases | Single-responsibility business operations, registered as DI services; emit events / dispatch jobs |
| Listeners | Subscribe to in-process events, fan out to BullMQ jobs / side effects |
| Workers | Consume BullMQ queues; run use-cases for async work (assignment, SLA, escalation, notification, enrichment, survey) |
| Repositories | Drizzle queries; schemas centralized in @nx/core |
| Components | Cross-cutting: EventBus, Queue, Worker, Mail, WebSocket, Redis cache |
4. State Machines Index
| Entity | States | Diagram |
|---|---|---|
Ticket | OPEN, ASSIGNED, IN_PROGRESS, WAITING_USER, WAITING_AGENT, WAITING_INTERNAL, PENDING, ASSIGNMENT_FAILED, ESCALATED, RESOLVED, CLOSED, REOPENED | → jump |
SlaTracker | ON_TRACK → WARNING → BREACHED → CRITICAL | → SLA & Escalation |
Ticket (XState v5)
TicketStatuses source: packages/core/src/models/schemas/helpdesk/ticket/schema.ts. 12 statuses; transitions enforced by UpdateTicketStatusUseCase.
PENDINGis a holding state defined inTicketStatusesfor not-yet-categorized work. Detailed transition table: Ticket System → Lifecycle.
5. Runtime Scenarios
5.1 Ticket creation → enrichment + assignment
| Step | Detail |
|---|---|
| 3 | Ticket + audit TicketEvent written in one unit of work |
| 4 | In-process emit — never blocks the HTTP response |
| 7-8 | Listener fans out to BullMQ; workers (worker process) pick them up |
5.2 SLA breach → escalation → notification
| Step | Detail |
|---|---|
| 1 | Cron is registered by QueueComponent.scheduleSlaMonitoring() (WORKER_CONFIG.SLA_MONITOR_INTERVAL) |
| 4-5 | Warning thresholds from SLA_WARNING_THRESHOLDS (75 / 90 / 100 / 150) |
| reassign | Level-2+ reassignment to senior agent is currently disabled in code (see Operations → Known Issues) |
5.3 Ticket resolved → survey trigger
6. Crosscutting Concerns
| Concern | How this service handles it |
|---|---|
| AuthN | JWT verified against identity JWKS (VerifierApplication). No issuance |
| AuthZ | Per-merchant access via assertMerchantAccess() + useRequestContext() from @nx/core; no per-route @authenticate decorator. PermissionService for helpdesk-specific checks |
| i18n | jsonb columns, { en, vi } shape (i18n() column helper) — e.g. ticket subject, description, SLA policy name |
| Logging | console.log/console.error prefixed with [Component] scope (no structured logger wired in workers/listeners) |
| Tracing | None wired |
| Idempotency | BullMQ jobs use idempotency helpers (worker-idempotency.helper.ts, idempotent-worker-deps.factory.ts); SLA cron uses fixed jobId: 'sla-monitor-cron' to prevent duplicates |
| Soft-delete | deletedAt via centralized generateCommonColumnDefs(); default reads exclude deleted rows |
| IDs | Snowflake via IdGenerator, worker node 12 |
| DLQ | HandleDeadLetterUseCase + dlq.helper.ts for failed jobs |