Coding Service - Domain Design
1. Coding Orchestrator sub-domain
Aggregate Root: CodingOrchestrator
a. Description
The Coding Orchestrator domain represents the orchestrator of the optimization workflow and will further be the orchestrator of the primo-coding workflow. It is the central aggregate that orchestrates the workflows to handle an optimization or a primo-coding consistent (state-machine).
b. States & commands
The Coding Orchestrator progresses through the following states:
CREATED → CODING_REQUESTED → [CODING_SUGGESTED | CODING_FAILED] → REVIEWED → CERTIFIED → VALIDATED
↓
IGNOREDState Definitions:
| State | Description |
|---|---|
CREATED | Medical stay imported from RSS file, ready for processing |
CODING_REQUESTED | Coding has been requested |
CODING_FAILED | Coding process encountered errors |
CODING_SUGGESTED | Successfully suggested codes |
REVIEWED | TIM coder validated the suggestions |
CERTIFIED | Internal Medical DIM reviewer validated the coding |
VALIDATED | Final validation by the customer DIM completed |
IGNORED | Stay has zero optimization gain, temporarily set aside |
Internal Commands and State Transitions:
Commands are methods on the CodingOrchestrator aggregate that enforce business rules and transition states:
| Command | Triggered By | From States | To State |
|---|---|---|---|
requestCoding() | Human (Admin) | CREATED, CODING_FAILED, CODING_SUGGESTED, CODING_VALIDATED, REVIEWED | CODING_REQUESTED |
failCoding() | System (Events from Optim or PromoCoding aggregates | CODING_REQUESTED, CODING_FAILED, CODING_SUGGESTED | CODING_FAILED |
suggestCoding() | System (Events from Optim or PromoCoding aggregates | CODING_REQUESTED, CODING_FAILED, CODING_SUGGESTED | CODING_SUGGESTED |
validateCoding() | Human (Tim) | CODING_SUGGESTED, CODING_FAILED | CODING_VALIDATED or IGNORED |
reviewCoding() | Human (Internal Dim) | CODING_VALIDATED | REVIEWED or IGNORED |
validateMedicalStay() | Human (Customer Dim) | REVIEWED, IGNORED | VALIDATED |
cancelAction() | Human (various roles) | VALIDATED, REVIEWED, IGNORED, CODING_VALIDATED | Previous state (from RollBackStatus) |
evaluateOptimization() | System (mostly after code changes) | IGNORED | Previous state if gain > 0 |
c. Domain Entry Points
| Static Method | Purpose | Triggered By |
|---|---|---|
createFromImport() | Create new MedicalStay from RSS import | System during RSS file processing |
d. Domain Boundaries and Vision
The Coding Orchestrator governs the lifecycle of a medical stay across optimization and primo-coding journeys while staying agnostic of any AI-specific mechanics. Its responsibility is to react to domain-level signals—principally from the Optimization and Coding aggregates—and maintain consistent orchestration state (currentOptimId, currentPrimoCodingId).
Inbound domain events & commands
- createFromImport() initializes a CodingOrchestrator as soon as an RSS file imports a medical stay. Creation immediately pairs the orchestrator with an Optimization aggregate because RSS imports always yield an optimization need.
- When business users or automated policies want a fresh optimization cycle, they now issue
requestCoding(). This command will instantiate a new Optimization aggregate instance. The Optimization domain then emits its own OptimizationStarted event, which downstream adapters (e.g. the AI Coding application service) listen to in order to launch AI Coding. The orchestrator still transitions into a "waiting for optimization" posture but never calls AI itself. - Completion flows arrive strictly via events: once AICoding finishes, it raises AICodingFinalized. Optimization handles the AI response through succeed(), mutates its own state, and emits OptimizationSuggested. The Coding Orchestrator subscribes to that success event to run its own succeed() or a future failure complement, recording the new currentOptimId and moving its finite-state machine forward.
Change states for the CodingOrchestrator Aggregate:
- AI_CODING_TRIGGERED → CODING_REQUESTED
- AI_CODING_SUCCEEDED → CODING_SUGGESTED
- AI_CODING_FAILED → CODING_FAILED
Vision
- Preserve a single source-of-truth for orchestration: Optimization and Coding aggregates retain ownership of AI integration. The orchestrator acts as a stateful coordinator that waits for their verdicts to decide subsequent human workflows (validation, review, final customer dim validation).
- Decouple future expansions: Primo-coding will mirror the optimization path. When introduced, a
triggerNewPrimoCoding()command will create a dedicated Primo Coding aggregate whose events mirror today's optimization flow. The orchestrator will track currentPrimoCodingId the same way, without gaining any AI-specific knowledge.
2. Optimization sub-domain
Aggregate Root: Optimization
a. Description
The Optimization domain represents the core value proposition: comparing a reference coding against a working coding to calculate optimization gain. Each optimization contains two complete Coding entities: the reference (baseline) and the working (improved) coding.
b. Current states & commands
The Optimization aggregate doesn't have explicit state enums, but has implicit lifecycle states based on its data:
| Implicit State | Indicators | Description |
|---|---|---|
| Created | optimizationGain === null | Just created, not yet evaluated |
| Evaluated | optimizationGain !== null | Has been grouped and priced |
| CodeUpdated | updatedAt !== null | Codes have been modified since creation |
Key Properties:
referenceCoding: The baseline coding from RSS import or previous stateworkingCoding: The coding being optimized with AI suggestionsoptimizationGain: Calculated price difference (working - reference)updatedAt: Last modification timestamp
Commands and Operations:
| Command | Triggered By | Purpose | Business Logic |
|---|---|---|---|
suggestCode() | System (Consumption of event AiCoding.Finalized) | AI suggests a new medical code | Updates workingCoding and emits event OptimizationSuggested |
acceptCode() | Human (Coder) | Coder accepts suggestion | Updates code status to ACCEPTED |
rejectCode() | Human (Coder) | Coder rejects suggestion | Updates code status to REJECTED |
changeCodeCategory() | Human (Coder) | Coder changes code category (DP/DR/DAS) | Updates code category |
groupAndPriceReference() | System (Evaluation use case) | Re-group and re-price reference coding | Validates GHM consistency, handles mismatches |
evaluate() | System (Evaluation use case) | Group, price working coding, calculate gain | Emits OptimizationEvaluatedEvent |
c. Domain Entry Points
The Optimization aggregate provides static factory methods that serve as entry points for creating new instances:
| Static Command | Triggered By | Purpose |
|---|---|---|
startFromRSS() | Human (RSS import) | Creates both reference and working coding from single source (RSS import) |
startFromReference(isExperiment: boolean) | 1. System (CodingOrchestrator.CodingRequestedEvent consumption) | |
| 2. Human (to trigger experiments) | Creates optimization with working coding using only selected documents and reference |
👉 All those commands will emit the OptimizationStarted event
Key Aggregate Invariants:
- Reference Immutability: Reference coding should not be modified after creation (except for re-grouping)
- Working Coding Mutability: Working coding can be modified through code operations
- Gain Calculation:
optimizationGain = round((workingPrice - referencePrice) * 100) / 100
d. Domain Boundaries and Vision
Optimization owns coding comparison and pricing logic while remaining unaware of higher-level orchestration or AI internals. Its job is to expose commands and events that other domains consume to drive workflows.
Integration boundaries
- ↔ Coding Orchestrator: The orchestrator emits en event CodingRequestedEvent to create a new Optimization aggregate (using
startFromReference()). Optimization emits OptimizationStarted, OptimizationSuggested, OptimizationFailed, etc. The orchestrator subscribes to those events to update its state (currentOptimId) but never mutates Optimization directly. - → AICoding domain: AICoding listens to OptimizationStarted, runs its AI workflows, and manipulates the aggregate exclusively through public commands (suggestCode(), acceptCode(), rejectCode(), evaluate()). When AI work finishes, AICoding raises AICodingFinalized; Optimization consumes that to invoke succeedAiCoding()/failAiCoding() internally, updating gain and queueing its own domain events.
- ← Document Domain: listens to AllDocumentsReadyEvent to know when to automatically
startFromReference()and emit a OptimizationStarted to start the AICoding pipeline
Vision
- Optimization supports experiment runs: experiment triggers instantiate the aggregate, produce AI work, and emit the usual success/failure events. When raising OptimizationSuggested, the domain includes a boolean isExperiment so downstream listeners (Coding Orchestrator) can decide whether to treat the result as canonical.
3. AI Coding sub-domain
Aggregate Root: AiCoding
a. Description
The AI Coding domain orchestrates the AI-driven medical coding suggestion workflow starting from ready documents. It is responsible for transforming processed medical documents into validated code suggestions that feed the Optimization aggregate.
Core Responsibility: Coordinate medical context standardization, invoke AI models to generate code suggestions for each medical unit stay, track suggestion completeness, and emit finalization events—while maintaining a clear audit trail of AI pipeline state and artifacts.
Key Principle: AI Coding acts as a producer of AI-specific artifacts (code candidates, citations, model versions) that other domains consume. It consumes ready documents from the Document domain and produces code suggestions for the Optimization domain.
b. Current states & commands
The AI Coding aggregate progresses through AI-specific processing stages:
CREATED → PENDING_CODING_SUGGESTIONS → COMPLETED
↓ ↓
FAILED FAILEDState Definitions:
| State | Description | Indicates |
|---|---|---|
| CREATED | AI Coding process initialized | Waiting for documents to be ready |
| PENDING_CODING_SUGGESTIONS | Code suggestions being generated | AI models suggesting codes for each unit stay |
| COMPLETED | All unit stays coded successfully | Ready to finalize optimization |
| FAILED | Process failed at any stage | Contains errorType and errorDetails |
Error Types:
- DOCUMENTS_NOT_READY_ERROR: Required documents not in READY state
- CODING_SUGGESTION_ERROR: AI model invocation failure
Internal Commands and State Transitions:
| Command | Triggered By | From States | To State | Business Rules |
|---|---|---|---|---|
completeUnit() | System (SuggestMedicalUnitStayCodingUseCase) | CREATED | PENDING_CODING_SUGGESTIONS | PENDING_CODING_SUGGESTIONS |
succeed() | System (FinalizeMedicalStayUseCase) | PENDING_CODING_SUGGESTIONS | COMPLETED | All medical unit stays must be completed |
fail() | System (various error handlers) | Any except COMPLETED | FAILED | Must provide errorType and errorDetails |
Key Aggregate Properties:
- id: Unique identifier for this AI coding run
- optimizationId: Reference to the optimization being built
- modelVersion: AI model version used for suggestions
- medicalUnitStayStatuses: Map tracking completion status of each unit stay
- selectedDocumentIds: Which documents to use for this coding run
c. Domain Entry Points
The AI Coding domain provides a single static factory method as its entry point:
| Static Method | Purpose | Triggered By |
|---|---|---|
create() | Initialize AI Coding process | OptimizationStarted event consumption |
d. Domain Boundaries and Vision
The AI Coding domain serves as a specialized orchestrator for AI-driven workflows, consuming artifacts from the Document domain and producing artifacts for the Optimization domain.
Integration Boundaries
← Document Domain:
- Consumes: Ready documents (OCR'd and pseudonymized) via query interface
- Boundary: Never manipulates document state; only reads ready artifacts
→ Optimization Domain:
- Consumes: referenceCoding from previous optimization (immutable baseline)
- Produces: Code suggestions via optimization.suggestCode() command
- TriggersBy: OptimizationStarted event at creation
- Finalizes: Signals completion via AICodingFinalized event (or more AiCodingSucceeded event ?)
// 1. AI Coding completes
AICoding.succeed()
→ emits AICodingFinalized { aiCodingId, optimizationId }
// 2. Optimization aggregate subscribes and validates
Optimization (subscriber to AICodingFinalized)
→ receives AICodingFinalized
→ calls this.suggest()
→ emits OptimizationSuggested {
optimizationId,
medicalStayId,
isExperiment
}
// 3. Coding Orchestrator subscribes and updates state
CodingOrchestrator (subscriber to OptimizationSuggested)
→ receives OptimizationSuggested
→ if (!isExperiment) {
this.setCurrentOptimId(optimizationId)
}
→ calls this.succeed()
→ transitions to CODING_SUCCEEDED state4. Primo-Coding Domain
Entity: currently Coding (not an aggregate root in current implementation)
a. Description
The Primo-Coding domain represents a complete medical coding snapshot: a collection of medical unit stays, each containing medical codes and medical acts. It also stores the grouping results (GHM, GHS, price, errors).
Current Architecture Note: Coding is an entity owned by the Optimization aggregate. It doesn't have its own lifecycle or persistence—it's always part of a reference or working coding within an Optimization.
b. Structure and business rules
Coding
├── Basic Properties
│ ├── id: string
│ ├── durationInDays: number
│ ├── dischargeDate: Date | null
│ ├── rawMedicalInformationsIds: string[] (documents used)
│ └── createdAt: Date
│
├── Grouping Results (set by evaluate)
│ ├── ghm: string | null (Groupe Homogène de Malades)
│ ├── ghs: string | null (Groupe Homogène de Séjours)
│ ├── ghmsSeverity: number | null
│ ├── ghmsMajorDiagnosisCategory: string | null
│ ├── ghmsMedicalStayType: string | null
│ ├── ghmsMedicalStaySubtype: string | null
│ ├── price: number | null
│ └── groupingError: GroupingError | null
│
└── Medical Unit Stays (entities)
└── MedicalUnitStay[]
├── generalInformation
├── medicalActs[]
└── medicalCodes[]Business Rules for Medical Unit Stay Validation:
- Exactly one Principal Diagnosis (DP) per unit stay
- At most one Related Diagnosis (DR) per unit stay
- Any number of Associated Diagnoses (DAS)
d. Domain Boundaries and Vision
The Coding entity is currently owned by the Optimization aggregate, representing an internal entity without independent lifecycle:
🔗 Coding ⊂ Optimization Interface
- Responsibility: Coding encapsulates a complete medical coding snapshot with grouping results
- Direction: Coding is owned and managed by Optimization (composition relationship)
- Boundary Rule: Coding has NO independent persistence or lifecycle outside of Optimization. This will change for sure if we start PrimoCoding
Key Encapsulation Rules:
- External actors CANNOT directly manipulate Coding entities
- All Coding operations MUST go through Optimization aggregate commands
- Coding entities contain rich business logic but lack independent identity outside their Optimization