Design Decisions
Key design constraints, architectural rationale, and tradeoffs for the Integration Dashboard system.
Design Decisions
These decisions prioritize decoupling, observability, and resilience — ensuring that integration failures never affect core product workflows and that every execution is fully traceable.
1. Event-Driven Configuration via Action IDs
The Problem
Hardcoding integration logic (URLs, headers, auth) inside business controllers (e.g., billing.save) makes each controller brittle. Adding a new vendor requires modifying core business logic.
The Decision
Use stable, business-event-based identifiers — Action IDs (e.g., 57 for Patient Registration, 1 for Bill Generation) — defined in crm.integrations.constants. Each labIntegration row maps an Action ID to a specific endpoint configuration.
Why:
- Dynamic mapping — a single business event can fan out to multiple integrations based purely on database configuration. No code change needed to add a vendor.
- Separation of concerns — the business controller only declares that an event occurred. It does not know or care what happens next.
- Standardized context — metadata (lab ID, bill ID, patient ID) is consistently extracted using the same mechanism for every action type.
2. Centralized Trigger Functions (Common Functions)
The Decision
All integration triggers are routed through named "common functions" (e.g., commomFunctionForIntegrationBillCategory, common_integration_sample_receive) rather than calling integration APIs directly.
Why:
- Consistency — whether a record is created via UI, bulk upload, or API, the same integration logic runs. No parallel code paths.
- Code reuse — payload preparation,
labIntegrationlookup, and Fusion queueing all live in one place per action family. - Retryability — the
_prepare_kwargs_*pattern can map any failed log back to the same function, making retry reconstruction predictable.
3. Async Dispatch through Fusion
The Decision
All outbound integrations are queued through Fusion async workers rather than being dispatched inline.
Why:
- Latency isolation — user actions (signing a report, finalizing a bill) complete immediately. Third-party network delays have zero impact on the product experience.
- Reliability — Fusion provides built-in queuing and isolation from transient failures.
- Throughput control — high-volume report events don't overwhelm downstream partners; workers consume the queue at a controlled rate.
4. Decorator-Based Logging (IntegrationDirectoryLogger)
The Decision
Logging is implemented as Python decorator classes applied to specific API views and webhook handlers, not as imperative logging calls inside business logic.
Why:
- Non-invasive — the business function doesn't know logging is happening. Adding observability requires one line (
@IntegrationDirectoryLogger()), not changes to the function body. - Guaranteed capture — the decorator always runs after the function, even if the function raises. The log reflects what actually happened.
- Lifecycle tracking — inbound decorators capture the full request → execute → response cycle. Outbound decorators update a pre-created
QUEUEDlog toSUCCESSorFAIL. - Fault tolerance — if the active DB transaction is in a broken state (
needs_rollback=True), the decorator falls back to a Fusion webhook so the log record is never silently lost.
5. DocumentDB for Integration Logs
The Decision
IntegrationDirectory and IntegrationRetryLog are stored in DocumentDB (MongoDB-compatible), not relational tables.
Why:
- Schema flexibility — every vendor returns a different JSON shape. A fixed relational schema would require migrations for every new integration or response structure change.
- Append-only retry history —
IntegrationRetryLoggrows without modifying the parent record. Relational normalization would add unnecessary join complexity for an operational read pattern. - Payload size — webhook payloads (base64 PDFs, HL7 messages, large JSON bodies) don't belong as RDBMS columns.
- Read pattern fit — dashboard queries are operational log reads and aggregations, not transactional joins. DocumentDB handles this efficiently.
6. Centralized Internal Dispatcher (integrationWebhookAPI)
The Decision
All Fusion-queued integration tasks are routed back through a centralized internal API (integrationWebhookAPI in labs/pyhtonRQAPI.py) rather than calling third-party endpoints directly from trigger functions.
Why:
- Just-in-time payload enrichment — binary content (base64 PDFs of reports or bills) is injected at dispatch time, not at trigger time. This avoids holding large payloads in the Fusion queue.
- Unified auth handling — token refreshes and auth header construction happen in one place.
- Callback coordination — the dispatcher manages the
integration_status_callbacklifecycle, keeping status update logic out of individual trigger functions.
7. Intent Reconstruction for Retries (_prepare_kwargs_*)
The Decision
Retries do not replay the original serialized HTTP request body. Instead, the system re-fetches live domain objects from the database and calls the original trigger function with fresh arguments.
Why:
- Stateless workers — background workers cannot safely reuse stale Python objects from a prior execution context. Only DB IDs and primitive values are safely serializable into a retry context.
- Data accuracy — if a patient record, bill, or report was corrected between the first failure and the retry, the retry should use the corrected data. Replaying a stale payload would re-dispatch incorrect information.
Summary
| Decision | Rationale |
|---|---|
| Action IDs | Decouples business events from integration handlers — no code change to add a vendor |
| Common functions | Ensures consistent trigger logic regardless of how an event was initiated |
| Fusion workers | Isolates user workflows from third-party network instability |
| Decorator logging | Keeps business code clean, guarantees capture, supports fault-tolerant fallback |
| DocumentDB | Handles schema-variable payloads and append-only retry history at scale |
| Central dispatcher | Handles just-in-time enrichment, unified auth, and callback coordination |
| Intent reconstruction | Ensures retries use fresh, accurate domain data — not stale serialized state |