Architecture¶
Technical overview of FHIR Electronic Medical Records system architecture.
System Overview¶
graph TB
subgraph "Presentation Layer"
A[Web Browser]
B[Thymeleaf Templates]
C[JavaScript/CSS]
end
subgraph "Application Layer"
D[Spring Boot 3.2]
E[Spring MVC Controllers]
F[Service Layer]
end
subgraph "FHIR Layer"
G[HAPI FHIR Server]
H[Resource Providers]
I[FHIR Validators]
end
subgraph "Data Layer"
J[JPA Repositories]
K[Hibernate ORM]
L[(PostgreSQL)]
end
subgraph "External Services"
M[Clinicaltables API]
N[SNOMED Local Library]
end
A --> B
B --> E
C --> M
C --> N
E --> F
F --> G
G --> H
H --> J
J --> K
K --> L
style D fill:#14b8a6
style G fill:#0ea5e9
style L fill:#8b5cf6
Technology Stack¶
Backend¶
Framework: Spring Boot 3.2.x - Dependency Injection - Auto-configuration - Embedded Tomcat - Production-ready features
FHIR: HAPI FHIR 6.x - R4 implementation - JPA server - Resource validation - Search parameters
ORM: Hibernate 6.x - Entity mapping - Lazy loading - Caching - Query optimization
Database: PostgreSQL 14+ - ACID compliance - JSON support - Full-text search - Replication
Frontend¶
Template Engine: Thymeleaf 3.x - Server-side rendering - Fragment support - Layout dialects - Security integration
JavaScript: - Vanilla JS (no framework) - Fetch API for AJAX - Medical autocomplete engine - SNOMED local library
CSS: - Custom modern CSS - Teal medical theme - Responsive design - Print optimization
External APIs¶
ICD-10 Codes: - NIH Clinicaltables API - No authentication - CORS-enabled - 200-400ms latency
SNOMED CT: - Local JavaScript library - 300+ common codes - <10ms response - Offline-capable
Project Structure¶
medicare-erp/
├── src/
│ ├── main/
│ │ ├── java/com/emr/fhir/
│ │ │ ├── controllers/ # MVC Controllers
│ │ │ │ ├── PatientController.java
│ │ │ │ ├── ConditionController.java
│ │ │ │ └── ...
│ │ │ ├── services/ # Business Logic
│ │ │ │ ├── PatientService.java
│ │ │ │ ├── FhirService.java
│ │ │ │ └── ...
│ │ │ ├── repositories/ # Data Access
│ │ │ │ └── (HAPI FHIR handles)
│ │ │ ├── dto/ # Data Transfer Objects
│ │ │ │ ├── PatientDTO.java
│ │ │ │ └── ...
│ │ │ ├── config/ # Configuration
│ │ │ │ ├── FhirConfig.java
│ │ │ │ └── SecurityConfig.java
│ │ │ └── MedicareErpApplication.java
│ │ └── resources/
│ │ ├── templates/ # Thymeleaf Templates
│ │ │ ├── fragments/
│ │ │ │ ├── nav.html
│ │ │ │ ├── patient-bar.html
│ │ │ │ ├── allergy-banner.html
│ │ │ │ └── clinical-tabs.html
│ │ │ ├── patients/
│ │ │ ├── conditions/
│ │ │ ├── prescriptions/
│ │ │ └── ...
│ │ ├── static/
│ │ │ ├── css/
│ │ │ │ └── style.css
│ │ │ └── js/
│ │ │ ├── medical-autocomplete.js
│ │ │ └── snomed-codes.js
│ │ └── application.properties
│ └── test/ # Unit Tests
└── pom.xml # Maven Configuration
Design Patterns¶
MVC (Model-View-Controller)¶
Controllers: Handle HTTP requests
@Controller
@RequestMapping("/patients")
public class PatientController {
@GetMapping("/{id}")
public String viewPatient(@PathVariable String id, Model model) {
PatientDTO patient = patientService.getPatient(id);
model.addAttribute("patient", patient);
return "patients/view";
}
}
Services: Business logic
@Service
public class PatientService {
public PatientDTO getPatient(String id) {
Patient fhirPatient = fhirClient.read()
.resource(Patient.class)
.withId(id)
.execute();
return convertToDTO(fhirPatient);
}
}
Views: Thymeleaf templates
Repository Pattern¶
HAPI FHIR provides repositories:
IGenericClient client = fhirContext.newRestfulGenericClient(serverBase);
// Create
Patient patient = new Patient();
MethodOutcome outcome = client.create()
.resource(patient)
.execute();
// Read
Patient patient = client.read()
.resource(Patient.class)
.withId("123")
.execute();
// Search
Bundle results = client.search()
.forResource(Patient.class)
.where(Patient.NAME.matches().value("John"))
.returnBundle(Bundle.class)
.execute();
DTO Pattern¶
Convert between FHIR resources and application DTOs:
public PatientDTO convertToDTO(Patient fhirPatient) {
PatientDTO dto = new PatientDTO();
dto.setId(fhirPatient.getIdElement().getIdPart());
dto.setFirstName(fhirPatient.getNameFirstRep().getGivenAsSingleString());
dto.setLastName(fhirPatient.getNameFirstRep().getFamily());
// ... more mappings
return dto;
}
Component Interaction¶
Request Flow¶
- Browser → HTTP GET /patients/123
- Controller → Receive request, extract ID
- Service → Call FHIR client
- FHIR Client → Query HAPI FHIR server
- HAPI FHIR → JPA query to database
- Database → Return patient record
- Service → Convert FHIR to DTO
- Controller → Add to model
- Thymeleaf → Render template
- Browser → Display HTML
Autocomplete Flow¶
- User types → "diab"
- JavaScript → Waits 300ms (debounce)
- Fetch API → GET to Clinicaltables
- API → Returns ICD-10 matches
- JavaScript → Displays dropdown
- User selects → E11.9
- JavaScript → Auto-fills code field
- Form submit → POST to controller
- Service → Creates FHIR Condition
- HAPI FHIR → Saves to database
Data Flow¶
FHIR Resource Creation¶
sequenceDiagram
User->>Browser: Fill form
Browser->>Controller: POST /conditions/save
Controller->>Service: save(ConditionDTO)
Service->>FHIR Client: create Condition
FHIR Client->>HAPI Server: POST /Condition
HAPI Server->>Validator: validate(Condition)
Validator->>HAPI Server: valid
HAPI Server->>JPA: persist(Condition)
JPA->>PostgreSQL: INSERT
PostgreSQL->>JPA: success
JPA->>HAPI Server: Condition ID
HAPI Server->>Service: MethodOutcome
Service->>Controller: success
Controller->>Browser: redirect
Browser->>User: Show success
Configuration¶
Application Properties¶
# Server
server.port=8080
# Database
spring.datasource.url=jdbc:postgresql://localhost:5432/medicare_erp
spring.datasource.username=medicare_user
spring.datasource.password=secure_password
# JPA
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
# HAPI FHIR
hapi.fhir.server_address=http://localhost:8080/api/fhir
hapi.fhir.default_page_size=20
hapi.fhir.max_page_size=100
# Logging
logging.level.ca.uhn.fhir=INFO
logging.level.com.emr.fhir=DEBUG
Performance Considerations¶
Database Optimization¶
- Connection pooling: HikariCP
- Index strategy: On search fields
- Lazy loading: For related entities
- Query optimization: JPA criteria queries
Caching¶
- HTTP caching: ETag support
- Application caching: Spring Cache
- Autocomplete caching: Browser localStorage
Scalability¶
- Horizontal scaling: Stateless application
- Database replication: Read replicas
- Load balancing: Nginx/HAProxy
- CDN: Static assets
Security Architecture¶
Authentication¶
- Spring Security
- Session-based auth
- Role-based access control (RBAC)
Authorization¶
Roles: - ADMIN - Full access - DOCTOR - Clinical records - NURSE - Limited clinical access - STAFF - Demographics only
HTTPS¶
Production deployment:
Next Steps¶
- FHIR Integration - FHIR details
- Autocomplete System - Autocomplete architecture
- API Reference - REST endpoints