SDD Architecture
Product design
Space for SDD and related design spec pages.
Context
Purpose of the System
The purpose of the application is to enable diagnostic infection tests to be run in a more efficient manner. By combining modern medical technologies with a highly accurate AI algorithm to automatically highlight any testing issues, better QC and results are provided with less time spent manually reviewing data.
The audience of this application is meant for clinical laboratories, and the personnel (CLS) including. Also, IT team (e.g., for setting up new users), and managers looking at trends and performing quality control.
No PII (Personally Identifiable Information) or PHI (Protected Health Information) is intended to be stored anywhere in the solution. All information handled and analyzed is anonymized prior to the analysis, meaning that no data can be attributed to any particular person.
The architecture is divided into frontend and backend, where the frontend is the client-facing part with user interfaces and connected APIs, whilst in the backend all required algorithms can perform the necessary actions.
System Architecture
The server-side infrastructure is based on serverless Compute service AWS Lambda, using the Laravel-based deployment platform Vapor. Vapor is used for managing Laravel infrastructures using Lambda in a more convenient way. AWS Cognito is used for all user authentication.
The Web client side is based on Laravel and Vue. When information is sent by the client from the frontend, it arrives in an AWS Lambda which sends information back to the client. For authentication, each client gets their own, monitored AWS account with AWS S3 and AWS Cognito enabled, as well as API functionality (on request) for S3. For authentication, clients can either use AWS Cognito-managed identities or, optionally, integrate their corporate Identity Provider into their Cognito account to achieve identity federation. Both OpenID Connect SAML are supported for corporate user directory integration.
The AWS S3 API provides a convenient method for automating upload to and download from the system to enable automated workflow integration with thermocyclers and LIMS/LIS systems. Optionally, AWS File Transfer service can be used in place of the direct S3 API, this enables FTP, SFTP and FTPS file transfer protocols. When using AWS Transfer clients can use their own software for uploading and downloading files using software that supports the aforementioned protocols (e.g., WinSCP, which also supports direct S3 connections, and FileZilla).
Key features of PCR.AI
- High accuracy fully automated analysis, QC and reporting for any real-time PCR data
- Ease regulatory compliance to ISO 15189, UKAS, etc.
- Machine-learning setup, inference based routine analysis
- Accessible through any web browser
- Secure, Standards compliant
- Clinically validated
- Review, comment and amend functionality (with secure access controls)
- Result and statistical control performance tracking in real-time
- Tracking results across multiple devices and sites
Workflow using PCR.AI

Workflow Description:
The diagram illustrates the end-to-end processing workflow for run files in PCRI.AI:
-
Run File Import: A run file from the thermocycler is imported into PCRI.AI via S3 upload (automated or manual).
-
Classification Path:
- If the laboratory has already analyzed the run, PCRI.AI compares the machine's classifications and CT values against its own analysis
- If not pre-analyzed, PCRI.AI classifies all readings using configured rules
-
QC Rules Evaluation: Quality control rules are applied to validate:
- Inhibition detection
- Control validity (positive/negative controls)
- Target-specific thresholds
-
Westgard Calculation: Statistical QC is performed using configured Westgard limits (mean, SD) to detect systematic or random errors in control samples.
-
Error Handling Decision:
- No Errors: Results are exported to LIMS
- Errors Present: Laboratory scientist follows the corrective action workflow:
- Reviews error codes and applies resolutions
- Determines if results can be reported after resolution
- If reportable: exports to LIMS
- If not reportable: data is stored for audit and further analysis
-
Audit Storage: All processed data is stored in Aurora MySQL for audit trail and subsequent analysis regardless of final disposition.
Architecture Patterns
Domain-Driven Design (DDD) Elements
The system implements several DDD patterns for organizing business logic:
Domain Services
| Directory | Purpose |
|---|---|
app/Analyzer/ | Core analysis engine - coordinates rule execution and result aggregation |
app/Services/ | Business logic services - encapsulates complex operations |
app/Actions/ | Single-responsibility actions - one action class per use case |
Domain Models
| Model | Contract | Description |
|---|---|---|
Well | AnalyzableWell | Core well entity with observations |
Observation | AnalyzableObservation | Target observation entity |
Run | - | Run file entity |
Target | - | Target configuration entity |
Value Objects
| Class | Purpose |
|---|---|
WellNumber | Immutable well position (A1-H12) |
HasUuid | UUID trait for entity identification |
Repository Pattern
Configuration data is accessed through repository contracts, enabling testing and alternative implementations:
| Contract | Implementation | Purpose |
|---|---|---|
ConfigurationRepository | EloquentConfigurationRepository | Main configuration access |
ClientConfigurationRepository | EloquentClientConfigurationRepository | Client-specific settings |
Location: app/Analyzer/Contracts/ (contracts), app/Analyzer/ (implementations)
Strategy Pattern
Analysis rules are implemented as pluggable strategies, allowing new rules to be added without modifying the engine:
| Component | Location | Purpose |
|---|---|---|
| Rule Contract | app/Analyzer/Contracts/AnalyzerRuleContract.php | Defines rule interface |
| Rule Classes | app/Analyzer/Rules/*.php | Concrete rule implementations |
| Rule Model | app/Rule.php | Database model with strategy mapping |
Mapping: The programmatic_rule_name field maps to PHP class names via App\Analyzer\Rules\{StudlyCase}Rule.
Factory Pattern
| Factory | Location | Purpose |
|---|---|---|
| Analyzer Factory | app/Analyzer/ | Creates analysis components |
| RunFileConverter Factories | app/RunFileConverter/ | Creates file conversion components |
| Normalizer Factory | app/Analyzer/Normalizer/ | Creates data normalizers |
Analysis Flow (6-Stage Pipeline)
1. DATA IMPORT
Raw PCR data imported from run file (.sds, .pcrd, .eds, .ixo)
│
▼
2. NORMALIZATION
Data normalized to standard format via JsonTemplateNormalizer
│
▼
3. VALIDATION
Data validated against business rules (accession, labels, dates)
│
▼
4. ANALYSIS
Rules applied in precedence order to analyze results
│
▼
5. QUALITY CONTROL
Westgard rules applied to control observations
│
▼
6. RESULT GENERATION
Final outcomes, error codes, and export status determined
AWS Services Summary
The following AWS services and third-party platforms form the infrastructure backbone of PCRI.AI:
| Service | Purpose in PCRI.AI | Key Benefit |
|---|---|---|
| AWS Lambda | Serverless compute for API and processing | Auto-scaling, pay-per-use |
| Amazon Cognito | User authentication (native + SAML/SSO) | Enterprise auth features, MFA |
| Amazon ElastiCache | Redis caching layer | Improved page load performance |
| Aurora Serverless v2 | Primary MySQL database | Auto-scaling, cost optimization |
| Amazon S3 | Run file storage, LIMS exports, archives | Durable, scalable object storage |
| Amazon SQS | Message queuing for async processing | Decoupled import processing |
| Amazon SES | Email delivery (notifications, alerts) | High deliverability |
| Amazon DynamoDB | Session storage (Vapor default) | Low-latency session management |
| Pusher | Real-time WebSocket communication | Live notifications, progress updates |
Detailed Rationale: Technology selection rationale with comparison tables (Lambda vs EC2, Cognito vs Laravel Native, Aurora Serverless vs Standard Aurora) is documented in SDS Architecture Overview.
Rules Engine
The Rules Engine is the core analytical component responsible for evaluating run data against configured rules.
Responsibility Boundaries:
- Rule Mapper: Resolves programmatic rule names to PHP handler classes
- Execution Engine: Processes rules in precedence order per configuration
- Error Propagator: Assigns error codes at well/target/mix levels based on rule outcomes
Dependencies:
- Aurora MySQL: Rule configuration, execution state
- Configuration schemas: Rule precedence, error mappings (see
sdd-configuration.md)
Interfaces:
- Input: Parsed run file data (JSON), configuration context
- Output: Well/target/mix classifications, error codes, Westgard violations
See: sdd-algorithms.md "Rule Mapping Architecture" for execution details.
Alert Notification System
Alerts configured in sdd-configuration.md are delivered through a dual-channel architecture:
Delivery Channels:
- Email: Amazon SES for threshold violation notifications
- Real-time: Pusher WebSocket for in-app notifications
Evaluation Trigger: Alert evaluation runs on scheduled intervals (configurable per alert type).
Ownership Separation:
- Alert configuration and trigger rules: See
sdd-configuration.md"Alerts Configuration" - Alert evaluation logic: Scheduled Lambda function queries trends data
- Alert delivery: SES (email) and Pusher (real-time) based on user preferences
See: sdd-configuration.md for alert trigger definitions and aggregation modes.
Progress Feedback System
The Progress Feedback System provides real-time visual feedback during long-running operations (e.g., Reanalysis).
Design Decisions:
| Decision | Rationale |
|---|---|
| 10-second display threshold | Operations under 10s complete fast enough that progress UI adds no value |
| 2-second update interval | Balances responsiveness with server load; configurable per deployment |
| Blocking overlay with navigation | Prevents conflicting user actions while allowing escape to other screens |
| Background tracking on navigate-away | Users should not lose progress visibility by navigating; state persists |
Architecture:
[Long-running Operation]
│
▼
[Server calculates duration]
│
┌─────┴─────┐
│ < 10s │ ≥ 10s
│ │
▼ ▼
No progress [Pusher WebSocket]
display │
┌──────┴──────┐
▼ ▼
[User A session] [User B session]
(progress UI) (progress UI)
Multi-User Synchronization:
- Pusher broadcasts progress updates to all sessions viewing the affected resource
- Users joining mid-operation see current progress state
- No distinction between initiator and observers in the UI
Navigation Persistence:
- Progress tracking continues in background when user navigates away
- On return to originating screen, progress indicator resumes with current state
- Multiple concurrent operations tracked independently
Configuration (environment-level):
| Property | Default | Description |
|---|---|---|
progress.display_threshold_seconds | 10 | Minimum calculated duration to show progress |
progress.update_interval_seconds | 2 | Frequency of percentage updates |
Related SRS Requirements:
- REQ-PROGRESS-001 through REQ-PROGRESS-004 (progress display, navigation, concurrency, multi-user)
See: sdd-configuration.md for deployment-level configuration options.
Comment Notification System
The Comment Notification System enables user mentions (@-tagging) in comments with dual-channel delivery and role-based access control.
Delivery Architecture:
[User submits comment with @mention]
│
▼
[Server processes tags]
│
┌───────────┴───────────┐
▼ ▼
[Check recipient [External email?]
preferences] │
│ ▼
│ [SES Email]
│ (always sent)
│
├── "In-app" ──→ [Pusher only]
│
├── "Email" ───→ [SES only]
│
└── "Both" ────→ [Pusher + SES]
(default)
Dual-Channel Delivery:
| Channel | Technology | Use Case |
|---|---|---|
| In-app | Pusher WebSocket | Real-time notification badge, pane updates |
| Amazon SES | Offline notification, deep link to comment |
Role-Based Mention Access:
| Initiator Role | Can Mention |
|---|---|
| SUPER_ADMIN | All roles |
| MANAGER | MANAGER, SENIOR, JUNIOR |
| SENIOR | MANAGER, SENIOR, JUNIOR |
| JUNIOR | MANAGER, SENIOR, JUNIOR |
| CLIENT_ADMIN | (none) |
Notification State Machine:
┌─────────┐ ┌────────┐
│ UNREAD │───[navigate to]───→│ READ │
│ │ comment │ │
└────┬────┘ └───┬────┘
│ │
└──────[toggle action]────────┘
- Unread → Read: Automatic on navigation to linked comment; explicit toggle
- Read → Unread: Explicit toggle only
- Mark All Read: Bulk action, resets unread count to zero
External User Invitation:
- Users not in system can be invited by email
- Email invitation bypasses preference check (always sends email)
- Validates email format before submission
Email Content Structure:
- Subject: Identifies requesting user
- Body: Run ID, Well ID (if applicable), comment text, direct link
Configuration:
| Property | Default | Description |
|---|---|---|
notifications.default_preference | Both | Default delivery method for new users |
notifications.comment_preview_length | 200 | Characters shown in notification preview |
Related SRS Requirements:
- REQ-COMMENTS-001 through REQ-COMMENTS-010 (tagging, delivery, preferences, display, navigation, access control)
See: sdd-configuration.md for notification configuration options.
Data Ownership and Persistence
Authoritative Sources:
- Aurora MySQL: All transactional data (runs, wells, observations, configurations)
- S3: Archived run files, LIMS exports, problem files
Derived Data:
- ElastiCache (Redis): Session cache, temporary computation results
- DynamoDB: Session tokens (managed by Laravel/Vapor)
Data is authoritative in Aurora MySQL. Cache layers (ElastiCache, DynamoDB) contain derived or temporary data that can be regenerated from authoritative sources.
Server Database Type Migration
Two Possible Solutions
| Approach | Pros | Cons |
|---|---|---|
| * Moving from Aurora Serverless v1 to Aurora Serverless v2 From Aws https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2.upgrade.html#aurora-serverless-v2.move-from-serverless-v1 | Loses ability of database management from vapor | |
| * use Vapor to provision a new serverless v2 | Need to migrate data manually |
Diagram/s and explanations
High Level Use Case Diagrams
User Management Use Case Diagram
The User Management subsystem enables role-based access control for user lifecycle operations.
Role Access Matrix:
| Use Case | Super Admin | Client Admin | Junior/Senior/Manager |
|---|---|---|---|
| Create User | Yes | Yes | No |
| Modify User | Yes | Yes (except Super Admin) | No |
| Block/Unblock User | Yes | Yes | No |
| Delete User | Yes | Yes | No |
| Reset Password (Admin) | Yes | Yes | No |
| Reset Password (Self) | Yes | Yes | Yes |
| Restore Deleted User | Yes | No | No |
| Configure MFA | Yes | Yes | Yes (own account) |
Design Notes:
- User deletion is soft delete (uses
deleted_attimestamp); audit trail preserved - Password reset supports both self-service (Cognito "Forgot Password") and admin-initiated flows
- Client Admin cannot modify Super Admin accounts (REQ-USERMGMT-013 AC-09)
See: sdd-security.md for authentication flow details.
Audit Use Case Diagram
The Audit subsystem provides audit trail visibility with site-based access control.
Access Control:
| Role | Audit Access Level | Sites Visible |
|---|---|---|
| Junior | View, Filter, Export | Assigned site only |
| Senior | View, Filter, Export | Assigned site only |
| Manager | View, Filter, Export | All assigned sites |
| Client Admin | View, Filter, Export | All client sites |
Design Notes:
- All roles can view audit logs (REQ-AUDIT-004)
- Audit data is site-filtered based on user's site access permissions
- Export format: Excel only (XLSX) per REQ-AUDIT-003
- Audit records are immutable; referencing deleted users preserved
See: REQ-AUDIT-001 for audit trail requirements.
Sequence Diagrams
Login Sequence Diagram
The authentication system supports two login paths: native application login and Cognito Hosted UI. Both integrate with AWS Cognito for session management.
Native Login Flow:
SAML/SSO Login Flow:
Session Management Details:
| Aspect | Implementation |
|---|---|
| Session Storage | DynamoDB (Laravel Vapor default, SESSION_DRIVER=dynamodb) |
| Session Identifier | JWT token from Cognito |
| Single-Device Enforcement | Cognito GlobalSignOut on new login (REQ-USERMGMT-011 AC-02) |
| Lockout Threshold | 5 failed attempts (configurable via max_login_attempts) |
| Lockout Duration | ~15 minutes (Cognito default, time-based auto-unlock) |
| Lockout Management | Cognito-managed (not application-level tracking) |
Design Notes:
- Native login is the default; Cognito Hosted UI available at client request
- Both OpenID Connect and SAML supported for corporate IdP federation
- Callback URL pattern:
${APP_URL}/login/samlcallback
See: sdd-security.md for detailed authentication architecture.
State Diagrams
Well State Diagram
Wells transition through mutually exclusive states based on analysis results, error assignment, resolution application, and export.
State Definitions:
| State | Database Condition | Description |
|---|---|---|
| UNANALYZED | error_code_id=NULL, lims_status=NULL | Newly imported, pending analysis |
| HAS_ERROR | error_code_id set | Error assigned by rules (mutually exclusive with LIMS status) |
| HAS_LIMS_STATUS | lims_status set | LIMS outcome assigned by rules |
| RESOLVED | resolution_codes set | Resolution code(s) applied |
| EXPORTED | export_date set | Exported to LIMS (terminal state) |
LIMS Status Values (from lims_statuses table):
| Code | Type | Meaning |
|---|---|---|
DETECTED | Information (4) | Target successfully detected |
NOT DETECTED | Information (4) | Target not detected |
INCONCLUSIVE | Information (4) | Result inconclusive |
REAMP | Warning (1) | Re-amplify required |
REXCT | Warning (1) | Re-extraction required |
TNP | Warning (1) | Test Not Performed |
EXCLUDE | Exclude (2) | Well excluded from analysis |
Error Type Constants (from ErrorCode.php):
| Type ID | Name | Outcome Color |
|---|---|---|
| 0 | Label Error | RED |
| 1 | Error | RED |
| 2 | Warning | YELLOW |
| 3 | Information | GREEN |
| 4 | Associate Control Error | RED |
Key Implementation Files:
app/Well.php- Core well model with state methodsapp/ErrorCode.php- Error type constantsapp/LimsStatus.php- LIMS status typesapp/Analyzer/Rules/Concerns/SetLimsStatusToWell.php- State transitionsapp/Analyzer/Rules/Concerns/SetErrorToWell.php- Error assignment
High Level Entity Relationship Diagram
Core Entity Relationships:
Entity Descriptions:
| Entity | Purpose | Key Relationships |
|---|---|---|
| Client | Organization/tenant | Has many Sites |
| Site | Physical laboratory location | Has many Runs, Users |
| User | System user with role | Belongs to Site(s), performs Audit actions |
| Run | Single instrument run file | Contains many Wells |
| Well | Sample position (A1-H12) | Contains Observations, assigned to Mix, has one Error OR LIMS status |
| Observation | Single target measurement | Links to Target, stores CT/quantity |
| Mix | Combination of targets/dyes | Many-to-many with Targets |
| Target | Analytical target (gene, marker) | Uses Dye for detection |
| Dye | Fluorescent reporter | Used by Targets |
| Error_Code | Error code definition | One-to-one with Well via error_code_id |
| LIMS_Status | LIMS outcome definition | One-to-one with Well via lims_status (code-based join) |
| Audit_Log | Immutable audit record | Links to Run, User |
Verified Storage Patterns (from code review 2026-01-20):
| Aspect | Implementation | Column |
|---|---|---|
| Error code storage | Single UUID FK (not junction table) | wells.error_code_id |
| LIMS status storage | String FK (code-based join) | wells.lims_status → lims_statuses.code |
| Export tracking | Datetime column | wells.export_date (null = not exported) |
| Resolution tracking | Text field (pipe-delimited) | wells.resolution_codes |
Additional Entities (per Q-012):
control_labels- Control type definitionsroles- User role configurationwestgard_limits- Westgard QC thresholdslots- Reagent lot tracking
See SDD dev questions Q-011, Q-012 for full entity details.
Application Feasibility Test
Historical (2026-01-18): Aurora MySQL 8.0 was selected and deployed as the database platform. Test result: PASSED - System operational in production. See: Runtime Dependencies table in
sdd-algorithms.mdfor current database configuration.
Cost Analysis
Historical (2026-01-18): Cost analysis completed; Aurora Serverless v2 selected. Decision: On-demand scaling provides cost optimization for variable workloads. See: "Amazon RDS with Aurora Serverless v2" section in this document for architecture details.
Vue Component Architecture
The frontend is built as a Vue.js Single Page Application (SPA) with the following structure:
Directory Structure
resources/frontend/src/
├── components/ # Reusable UI components
├── mixins/ # Shared component logic
│ ├── resolution/ # Error resolution workflows
│ ├── well-multiselect/# Well selection handling
│ └── client-configurations/
├── store/modules/ # Vuex state management
│ ├── auth.js # Authentication state
│ ├── runs.js # Run file tracking
│ ├── features.js # Feature flags
│ └── auditOptions.js # Audit filtering
├── views/ # Page-level components
└── router/ # Vue Router configuration
State Management
Vuex stores manage application state:
- auth: User authentication and session
- runs: Recent runs and upload progress
- features: Feature flag toggles
- auditOptions: Audit trail filtering
Key Component Patterns
| Pattern | Purpose | Example |
|---|---|---|
| Mixins | Shared logic across components | Wells.js, FilterWells.js |
| Store Modules | Centralized state | auth.js, runs.js |
| Resolution Components | Error handling workflows | WellResolutionProposalForm.js |
Related SRS References
- REQ-RUNFILE-001 - Run file display
- REQ-USERMGMT-001 - Authentication
- REQ-REPORTS-001 - Reporting views
Related SRS Requirements
The following SRS requirements are implemented by the design described in this document:
Authentication and User Management (Amazon Cognito)
| Requirement | Domain | Description | Relevance |
|---|---|---|---|
| REQ-USERMGMT-009 | User Management | Authenticate Users | Cognito provides native and federated (SAML) authentication as described in SDD |
| REQ-USERMGMT-011 | User Management | Manage User Sessions | DynamoDB used for session management; Cognito global sign-out for session termination |
| REQ-USERMGMT-004 | User Management | Configure User MFA | Cognito MFA/TOTP implementation |
| REQ-USERMGMT-006 | User Management | Control User Account Status | Cognito global sign-out for session termination on user disable |
| REQ-USERMGMT-015 | User Management | Enforce Authentication Security Policies | Cognito enforces password complexity, history, expiry, and lockout policies |
File Storage and Import (Amazon S3)
| Requirement | Domain | Description | Relevance |
|---|---|---|---|
| REQ-FILEIMPORT-001 | File Import | Import Run Files from Monitored Folder | S3 triggers on toPcrai folder initiate Lambda processing |
| REQ-FILEIMPORT-010 | File Import | Manage Import Folder Structure | S3 folder structure (toPcrai, Processing, Problem_Files, archive bucket) |
| REQ-FILEIMPORT-003 | File Import | Analyze Run Data Using DXAI Analyser | Calibration files stored in S3 bucket (chill-rabbit-calibrations) |
| REQ-SITE-001 | Site Management | Provision Storage on Site Creation | S3 folder creation for LIMS_Reports per site |
| REQ-SITE-002 | Site Management | Configure S3 Structure Preservation | S3 folder structure configuration options |
| REQ-SITE-003 | Site Management | Specify Custom S3 Folder Name | S3 path customization for LIMS_Export and Runs folders |
| REQ-UPLOAD-001 | Upload Runs | Accept Run File Uploads | Files uploaded to S3 via application or S3 API |
Performance and Scalability (AWS Lambda, Aurora Serverless)
| Requirement | Domain | Description | Relevance |
|---|---|---|---|
| REQ-NFR-001 | Non-Functional | Page Load Time | Lambda auto-scaling ensures consistent performance under load |
| REQ-NFR-002 | Non-Functional | Reanalysis Operation Time | Lambda compute for analysis operations |
| REQ-NFR-003 | Non-Functional | Concurrent User Capacity | Serverless architecture (Lambda, Aurora Serverless) enables 30+ concurrent users with graceful degradation |
| REQ-USERMGMT-016 | User Management | Ensure System Readiness Before Login | Aurora Serverless cold start handling (database wakeup) |
Email Services (Amazon SES)
| Requirement | Domain | Description | Relevance |
|---|---|---|---|
| REQ-AUDIT-003 | Audit Log | Export Audit Data | SES delivers export download links via email |
| REQ-USERMGMT-005 | User Management | Request Email Verification | SES sends verification emails |
| REQ-USERMGMT-008 | User Management | Create User Account | SES sends notification emails on account creation |
Real-Time Communication (Pusher)
| Requirement | Domain | Description | Relevance |
|---|---|---|---|
| REQ-NOTIF-001 | Notifications | Display Runfile Status Notifications | Pusher enables real-time notification updates |
| REQ-UPLOAD-003 | Upload Runs | Display Upload Progress | Real-time progress updates via Pusher |
Message Queue Processing (Amazon SQS)
| Requirement | Domain | Description | Relevance |
|---|---|---|---|
| REQ-FILEIMPORT-001 | File Import | Import Run Files from Monitored Folder | SQS decouples S3 triggers from Lambda processing |
| REQ-AUDIT-003 | Audit Log | Export Audit Data | Background job queuing for large exports |
Caching (Amazon ElastiCache)
| Requirement | Domain | Description | Relevance |
|---|---|---|---|
| REQ-NFR-001 | Non-Functional | Page Load Time | ElastiCache improves response times for frequently accessed data |
Database (Amazon RDS/Aurora Serverless)
| Requirement | Domain | Description | Relevance |
|---|---|---|---|
| REQ-AUDIT-001 | Audit Log | Display Audit Trail | Aurora stores immutable audit records |
| REQ-ANALYTICS-001 | Analytics | Execute Rules on Error Wells | Aurora stores rule execution state and well data |
Data Privacy (No PII/PHI Storage)
| Requirement | Domain | Description | Relevance |
|---|---|---|---|
| All Domains | System-wide | Data handling | Architecture explicitly states no PII/PHI storage; all data anonymized prior to analysis |
High-Level Entity Relationship Diagram
Core Entity Relationships
Wells Table Schema (Key Columns)
| Column | Type | Description |
|---|---|---|
id | UUID | Primary key |
run_id | UUID | FK to runs |
run_mix_id | UUID | FK to run_mixes (nullable) |
specimen_id | UUID | FK to specimens (nullable) |
error_code_id | UUID | FK to error_codes (nullable) - single error per well |
lims_status | VARCHAR | LIMS status code string (not FK, lookup to lims_statuses.code) |
export_date | DATETIME | Null = not exported |
exclude | BOOLEAN | Manual exclusion flag |
is_flagged | BOOLEAN | Flagged for review |
resolution_codes | TEXT | Pipe-delimited resolution codes applied |
Error Code Storage Pattern
Architecture Decision: Simple foreign key relationship, not a junction table.
wells.error_code_id→error_codes.id(BelongsTo)- One error code per well maximum
- Error codes are site-scoped (
error_codes.site_id) - Soft-deleted error codes preserved for historical wells
LIMS Status Storage Pattern
Architecture Decision: String column with lookup table for metadata.
wells.lims_statusstores the status code directly (e.g., "DETECTED", "RPT", "RXT")lims_statusestable provides: code, message template, type (Warning/Exclude/Information), result (DETECTED/NOT_DETECTED)- Lookup is via string match (
lims_statuses.code), not FK constraint - Allows flexibility for site-specific LIMS status definitions
Implementation
| Component | Location |
|---|---|
| Well Model | app/Well.php |
| ErrorCode Model | app/ErrorCode.php |
| LimsStatus Model | app/LimsStatus.php |
| Wells Migration | database/migrations/app/2020_02_01_064128_create_wells_table.php |
| Error Codes Migration | database/migrations/app/2020_01_24_113210_create_error_codes_table.php |
Well State Model
State Composition
Wells do not have a single status enum. State is computed from multiple indicators:
Error Code Types (from ErrorCode::ERROR_TYPE)
| Type | Value | Outcome Type | Blocks Export |
|---|---|---|---|
| Label Error | 0 | Error | Yes |
| Error | 1 | Error | Yes |
| Warning | 2 | Warning | No |
| Information | 3 | Information | No |
| Associate Control Error | 4 | Error | Yes |
LIMS Status Types (from LimsStatus::TYPES)
| Type | Value | Description |
|---|---|---|
| Warning | 1 | Re-test recommended (RPT, RXT) |
| Exclude | 2 | Well excluded from export |
| Information | 4 | Normal result (DETECTED, NOT_DETECTED) |
LIMS Status Results (from LimsStatus::RESULTS)
| Result | Value | Description |
|---|---|---|
| DETECTED | 1 | Target detected |
| NOT_DETECTED | 2 | Target not detected |
Computed Run Status (from GetRunStatus.php)
The overall run status is computed from well states:
| Run Status | Condition |
|---|---|
ALL_WELLS_EXPORTED | All wells have export_date set |
ALL_WELLS_READY_FOR_EXPORT | No wells with blocking errors, none exported yet |
NO_EXPORT_ERRORS_TO_RESOLVE | Wells have errors but no LIMS status assigned |
SOME_WELLS_READY_FOR_EXPORT_WITH_ERRORS_TO_RESOLVE | Mixed state: some ready, some with errors |
Implementation
| Component | Location |
|---|---|
| Run Status Calculator | app/GetRunStatus.php |
| Outcome Type Logic | Well@outcomeType() |
| Error Type Constants | ErrorCode::ERROR_TYPE |
| LIMS Type Constants | LimsStatus::TYPES |
Complete Database Schema Inventory
Core Data Tables
| Table | Key Columns | Foreign Keys | Notes |
|---|---|---|---|
| users | id (UUID), username, email, user_type, mfa_secret, blocked, logged_in_site_id | logged_in_site_id → sites | Soft deletes; Core user authentication |
| runs | id (UUID), run_name, uploaded_user_id, modified_user_id, thermocycler_id, num_label_errors, num_pending_resolutions | uploaded_user_id, modified_user_id, thermocycler_id → FK | Root data entity; Tracks error/resolution counts |
| wells | id (UUID), run_id, run_mix_id, specimen_id, error_code_id, lims_status, export_date, exclude, is_flagged, resolution_codes | run_id, run_mix_id, specimen_id, error_code_id → FK | Primary analytical unit; Microsecond timestamps |
| observations | id (UUID), well_id, role_id, target_id, dye_id, machine_cls, dxai_cls, final_cls, machine_ct, final_ct, readings | well_id, role_id, target_id, dye_id → FK | Per-well results; Multiple classification systems |
| error_codes | id (UUID), error_code, error_message, error_level, error_type, lims_status | None | Error enumeration; Soft deleted for history |
| lims_statuses | id (UUID), code, message, type | None | LIMS integration status codes |
| resolution_codes | id (UUID), error_code_id, resolution_message_id, is_patient, is_default, lims_status | error_code_id, resolution_message_id → FK | Error → resolution workflow mapping |
Configuration Tables
| Table | Key Columns | Notes |
|---|---|---|
| mixes | id, mix_name, use_passive_dye | PCR reaction mix catalog |
| dyes | id, dye_name, quencher, colour | Fluorophore catalog |
| targets | id, mix_id, dye_id, target_name, type, is_passive | Analyte definitions |
| roles | id, role_name | Sample classification (positive control, patient, etc.) |
| role_to_target_mappings | id, role_id, target_id, mix_id, role_alias | Junction table for role-mix-target |
| control_labels | id, role_id, mix_id, role_alias, is_strict, site_id, backup_mixes_ids | v3.0.0+ enhanced role mapping |
| specimens | id, specimen_name | Sample type catalog |
| thermocyclers | id, thermocycler_type, thermocycler_serial_number, thermocycler_plate_size | Instrument registry |
| extraction_instruments | id, extraction_model_id, extraction_instrument_name | Extraction instrument instances |
| lots | id, lot_name, in_use | Reagent lot tracking |
Quality Control Tables
| Table | Key Columns | Notes |
|---|---|---|
| westgard_events | id, code, message | Westgard rule violation catalog |
| westgard_limits | id, extraction_instrument_id, lot_id, role_to_target_mapping_id, sd, mean, in_error | QC statistics per instrument-lot-target |
| control_range_settings | id, role_to_target_mapping_id, lot_id, low_bound, up_bound, quant_or_ct | Control acceptance limits |
Multi-Site & Workflow Tables
| Table | Key Columns | Notes |
|---|---|---|
| sites | id, name | Physical lab locations; Soft deletes |
| user_visible_sites | user_id, site_id | Junction for site access control |
| run_mixes | id, run_id, mix_id, num_wells, export_status | Mix composition per run |
| run_targets | id, run_id, target_id, error_codes, std_curve_grad | Per-target metrics |
| client_configurations | id, name, value | Key-value application settings |
| tags | id, name, archive, site_id | Run categorization |
| comments | id, user_id, parent_id, commentable_id, commentable_type, text | Polymorphic threaded comments |
Key Schema Characteristics
- UUIDs for all primary keys (ordered UUIDs)
- Decimals for QC statistics:
decimal(32,10)for precision - Smallint enums for status fields
- JSON text fields for arrays (error_codes, mix_ids)
- Soft deletes on users, sites, control_labels, tags
Audit Trail System
Overview
PCRI.AI logs all significant user actions to an audits table via the LogIntoDatabase listener.
Audit Table Schema
| Column | Type | Description |
|---|---|---|
id | UUID | Primary key |
username | String | User who performed action |
area | String | Functional area (Configuration, User Management, Runfile Report) |
change_type | String | Type of change (User Account, Mix, Well) |
action | String | Specific action (Created, Edit, Delete) |
change_location | String | Context (resource name, location) |
value_before | String | Previous value |
value_after | String | New value |
site_name | String | Site context (nullable) |
created_at | Timestamp | Microsecond-precision timestamp |
Audit Event Categories (71 Total)
User Management (11 events):
UserLoggedIn,UserLoggedOutUserAccountCreated,UserAccountDeleted,UserAccountDisabled,UserAccountEnabledUserAssociatedWithRole,RoleChangedForUserPasswordChanged,EmailVerificationSent,EmailVerified
Configuration (42 events):
- Dye:
DyeCreated,DyeMappedToTarget - Target:
TargetCreated,EcSetToTarget,IcSetToTarget,RoleToTargetMapCreated - Mix:
MixCreated,MixEnabled,MixDisabled,MixMappedToTarget,SpecimenMappedToMix,RoleToMixMapCreated - Westgard:
LotSetToWestgardLimits,MeanSetToWestgardLimits,SDSetToWestgardLimits,HistoricCvSetToWestgardLimits - Rules:
RuleCreated,RuleMapped - Instruments:
ThermocyclerCreated,ExtractionInstrumentCreated,ExtractionModelCreated
Runfile Report (15 events):
- Well edits:
AccessionSetToWell,BatchNumberSetToWell,SpecimenNameSetToWell,SampleNameSetToWell,RoleAliasSetToWell,ExtractionDateSetToWell - Resolution:
ResolutionCodeAppendToWell - Run operations:
RunFileImported,RunFileOpened,RunFileExported,WellExported
Implementation
| Component | Location |
|---|---|
| Event Interface | app/Events/AuditableEvent.php |
| Database Listener | app/Listeners/LogIntoDatabase.php |
| Audit Model | app/Audit.php |
External Integrations Inventory
Integration Summary
| Service | Type | Status | Primary Use | Config File |
|---|---|---|---|---|
| Amazon S3 | Cloud Storage | Active | File storage, run imports/exports | config/filesystems.php |
| Amazon SES | Available | Alternative email delivery | config/mail.php | |
| SendGrid | Primary | Email notifications | config/mail.php | |
| Pusher | Real-time | Active | WebSocket broadcasting | config/broadcasting.php |
| Amazon Cognito | Authentication | Active | User auth, MFA, SSO | config/aws-cognito.php |
| Microsoft Graph | Document Mgmt | Active | SharePoint integration | config/microsoft-graph.php |
| Sentry | Error Tracking | Active | Error/performance monitoring | config/sentry.php |
Amazon S3 (Object Storage)
Configuration: config/filesystems.php
Operations:
- Run file storage and retrieval
- LIMS export file storage
- Multi-site folder management
- Lambda trigger management for automated imports
Environment Variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION, AWS_BUCKET
Amazon Cognito (Authentication)
Configuration: config/aws-cognito.php, config/aws-super-admin-cognito.php
Operations:
- ADMIN_USER_PASSWORD_AUTH flow
- User CRUD (create, update attributes, disable, delete)
- MFA management (enable/disable software token)
- SAML/SSO integration
Key Files: app/Support/CognitoAuthenticator.php, app/CognitoUserManager.php
Custom Attributes: custom:UserGroup, custom:VisibleSites
Pusher (Real-time Messaging)
Configuration: config/broadcasting.php
Channels (already documented):
Run.Create,Run.Analyse,Run.Calibraterun-export,run-file-importcontrol-label-updated,notificationsApp.User.{userId}(private)
SendGrid (Email)
Configuration: config/mail.php
Purpose: Primary email delivery for notifications, alerts, audit exports
From Address: hello@ivd.ai / Pcr.ai
Microsoft Graph (SharePoint)
Configuration: config/microsoft-graph.php
Purpose: SharePoint drive operations for document management
Key Files: app/Support/MicrosoftGraph/GraphService.php
Sentry (Error Tracking)
Configuration: config/sentry.php
Features:
- Error/exception tracking
- Performance monitoring
- Client replay transfer
Breadcrumb Capture: Laravel logs, SQL queries, queue jobs
Note: This traceability section maps architecture components to functional requirements. Requirements are linked to their SRS domain documents for detailed specifications.