Design Decisions
Key architectural choices, trade-offs, and extensibility considerations for Reflex Management.
Design Decisions
Rule-Driven Reflex Engine
Reflex Management is implemented as a rule-driven system where configurations define when and how reflex tests should be triggered.
Why:
- Decouples business logic from execution
- Allows non-code configuration of reflex behavior
- Supports extensibility for new parameter types
Trade-off:
- Increased complexity in rule evaluation
- Requires strong validation to avoid conflicting rules
Mitigation:
- Centralized validation layer (
validate_payload) - Structured rule storage (JSON + relational rules)
Separation of Configuration and Execution
The system separates:
- ReflexTestConfiguration → Defines rules
- ReflexTestDetails → Stores execution results
Why:
- Keeps configuration immutable during execution
- Enables auditability of triggered reflex tests
- Avoids recomputation of past triggers
Implication:
- Trigger results are persisted even if configurations change later
Parameter-Type-Based Rule Evaluation
Rules are evaluated differently based on parameter type:
- Range → Numeric comparison
- List / Descriptive → String-based matching
- Multi-value → Logical AND/OR evaluation
Why:
Different parameter types require fundamentally different evaluation strategies.
Design Choice:
- Use separate handlers:
get_range_parameter_trigger_test_ids()get_list_descriptive_parameter_trigger_test_ids()evaluate_multi_value_trigger()
Benefit:
- Clean separation of logic
- Easier extensibility
Multi-Value Condition as Structured JSON
Complex conditions (AND / OR) are stored as JSON:
{
"condition": "AND",
"contains": ["Positive"],
"does_not_contains": ["Negative"]
}Multi-Value Condition Design
Why
- Flexible structure for future expansion
- Avoids rigid schema changes
Trade-off
- Requires runtime parsing and validation
Idempotent Trigger Execution
Reflex triggering ensures that tests are not triggered multiple times for the same report.
Implementation
- Check in
ReflexTestDetails:is_triggered = True
- Prevent duplicate execution
Why
- Avoids duplicate billing
- Maintains data integrity
Bill Integration Strategy (New vs Existing)
Reflex tests can be added:
- To existing bill
- Or a new bill
Controlled via
create_new_billflag
Why
- Some reflex tests require separate billing
- Others should be grouped with original test
Design Choice
- Split tests internally:
- Existing bill bucket
- New bill bucket
Billing Execution via Controller
Reflex test addition is delegated to:
commonBillAddTestController()
Why
- Reuse existing billing logic
- Avoid duplication of billing rules
Trade-off
- Tight coupling with billing system
Async Triggering via Queue
Manual/API-based reflex triggering uses:
enqueue_reflex_trigger()
Why
- Avoid blocking API response
- Supports high-volume processing
Implication
- Execution is asynchronous
- Requires monitoring (queue + failures)
Optimistic Execution with Failure Logging
If reflex addition fails:
System Behavior
- Logs failure (
ActivityLog) - Adds comment in report
Why
- Do not silently fail
- Provide visibility to user
Activity Logging as First-Class Concern
Every trigger action logs:
- Test triggered
- Trigger type (manual/interfacing)
- Patient + bill
- Parameter
Why
- Critical for audit and debugging
- Required for clinical traceability
Centralized Validation Layer
All validations are handled before execution:
- Payload validation
- Rule validation
- Duplicate detection
- Range conflicts
Why
- Prevent invalid configurations early
- Reduce runtime errors
Reflex Trigger Flow
User / Device Input
│
▼
TriggerReflexTestInBill API
│
▼
Payload Validation
│
▼
Check Duplicate Trigger (ReflexTestDetails)
│
▼
Fetch Reflex Configurations
│
▼
Rule Evaluation Engine
├── Range Evaluation
├── List/Descriptive Evaluation
└── Multi-Value (AND/OR)
│
▼
Eligible Reflex Tests
│
▼
Split by Billing Strategy
├── Existing Bill
└── New Bill
│
▼
Prepare Billing Payload
│
▼
commonBillAddTestController()
│
▼
Persist Trigger Details
├── ReflexTestDetails
└── Activity Logs
│
▼
Response to Client