Explore OpenMRS

Comprehensive current-state documentation of OpenMRS Core 3.0.0-SNAPSHOT. 109 verified claims across ~2,600 Java files — architecture, dependencies, entity relationships, service maps, and infrastructure. Includes user guides, test cases, and API documentation. Extracted via the Dryad knowledge pipeline in Phase 1 of a SimplifyingSoftware sprint.

OpenMRS Architecture

Current-state system documentation — Phase 1 deliverable

Entity Relationship Graph

Click and drag to explore. Scroll to zoom. Click a node for details.

Service Dependency Map

Service layer (purple) to DAO layer (green). Node size = code volume. Dashed lines = cross-service calls.

Request Processing Pipeline

HTTP Request | v CharacterEncodingFilter (UTF-8) |-> StartupErrorFilter (if startup failed -> error page) |-> InitializationFilter (if DB not initialized -> setup wizard) |-> UpdateFilter (if schema upgrade needed -> migration wizard) |-> MultipartFilter (file uploads) |-> OpenSessionInViewFilter (Hibernate session per request) |-> CookieClearingFilter (security) |-> OpenmrsFilter (attach thread-local UserContext) |-> CSRFGuard (OWASP CSRF token) |-> ModuleFilter (route to module filters) |-> GZIPFilter (response compression) |-> JspClassLoaderFilter (module classloader) | v Spring DispatcherServlet | v AOP Chain (per @Service method call): 1. AuthorizationAdvice (@Authorized privilege check) 2. LoggingAdvice (TRACE/DEBUG method logging) 3. RequiredDataAdvice (audit field population) 4. CacheInterceptor (@Cacheable/@CacheEvict) 5. TransactionInterceptor (@Transactional) | v Service Layer (21 services, 880 methods) | v DAO Layer (25 DAOs, Criteria API / HQL / Native SQL / Hibernate Search) | v Hibernate ORM |-> Interceptor Chain: | AuditableInterceptor (creator/dateCreated/changedBy) | DropMillisecondsInterceptor (MySQL compat) | ImmutableObsInterceptor (void-and-replace) | ImmutableOrderInterceptor (immutable after creation) |-> Envers (audit trail) |-> Second-level Cache (Infinispan) | v Database (MySQL / MariaDB / PostgreSQL / H2)

Domain Entity Graph

Patient (extends Person) |-- identifiers[] --> PatientIdentifier --> PatientIdentifierType |-- names[] -------> PersonName |-- addresses[] ---> PersonAddress |-- attributes[] --> PersonAttribute --> PersonAttributeType | |-- encounters[] --> Encounter | |-- type ---------> EncounterType | |-- location -----> Location --> LocationTag[] | |-- providers[] --> EncounterProvider --> Provider, EncounterRole | |-- obs[] --------> Obs (CASCADE ALL) | | |-- concept ----> Concept --> ConceptName[], ConceptMap[] | | |-- valueCoded -> Concept | | |-- valueDrug --> Drug --> DrugIngredient[] | | |-- groupMembers[] -> Obs (recursive) | | |-- order ------> Order | | '-- referenceRange -> ObsReferenceRange | |-- orders[] -----> Order (@Inheritance JOINED) | | |-- DrugOrder (dose, frequency, route, drug) | | |-- TestOrder | | |-- ServiceOrder | | '-- ReferralOrder | |-- diagnoses[] -> Diagnosis --> CodedOrFreeText | |-- conditions[] -> Condition --> CodedOrFreeText | '-- allergies[] -> Allergy --> AllergyReaction[], Allergen | |-- visits[] ------> Visit --> VisitType |-- programs[] ----> PatientProgram --> Program | |-- states[] -> PatientState --> ProgramWorkflowState | '-- attributes[] -> PatientProgramAttribute |-- relationships[] -> Relationship --> RelationshipType '-- cohorts[] -----> Cohort --> CohortMembership Concept Dictionary (90 classes, largest subsystem) Concept (@Cacheable, @Audited) |-- names[] --------> ConceptName (multi-locale, @Indexed) |-- descriptions[] -> ConceptDescription |-- answers[] ------> ConceptAnswer --> Concept (coded values) |-- conceptSets[] --> ConceptSet (hierarchical grouping) |-- mappings[] -----> ConceptMap --> ConceptReferenceTerm --> ConceptSource | '-- ConceptReferenceTermMap (term-to-term: LOINC<->SNOMED) |-- attributes[] ---> ConceptAttribute |-- referenceRanges[] -> ConceptReferenceRange '-- datatype/class -> ConceptDatatype, ConceptClass 85 @Entity classes | 45 data entities | 25 metadata entities | 4 security

Service-to-DAO Dependency Map

Service (lines) DAO (lines) Complexity JPA Migration -------------------- ------------------ ---------- ------------- ConceptService (2,343) ConceptDAO (2,405) VERY HIGH HARD OrderService (1,399) OrderDAO (1,027) VERY HIGH HARD PatientService (1,638) PatientDAO (1,020) VERY HIGH HARD EncounterService (1,029) EncounterDAO (765) HIGH HARD ObsService (690) ObsDAO (356) HIGH HARD PersonService (1,043) PersonDAO (827) MEDIUM-HIGH MEDIUM UserService (844) UserDAO (809) MEDIUM-HIGH MEDIUM AdminService (1,120) AdminDAO (450) MEDIUM MEDIUM FormService (838) FormDAO (704) MEDIUM MEDIUM ProgramService (683) ProgramDAO (493) MEDIUM MEDIUM LocationService (541) LocationDAO (432) LOW-MEDIUM EASY VisitService (450) VisitDAO (395) LOW-MEDIUM EASY ProviderService (408) ProviderDAO (457) LOW EASY CohortService (302) CohortDAO (207) LOW EASY DiagnosisService (290) DiagnosisDAO (242) LOW EASY + 6 more simple services... Total: 21 services (17,627 lines) | 25 DAOs (11,800 lines)

External API Surface

REST Module (/ws/rest/v1/*) FHIR2 Module (/ws/fhir2/*) 163 resources 19 R4 resources DelegatingCrudResource pattern Translator pattern (HAPI FHIR 5.7.9) Swagger 2.0 documentation No OpenAPI documentation HTTP Basic auth HTTP Basic auth 3 representations (REF/DEFAULT/FULL) FHIR standard representations 21 search handlers 18 search parameter classes Java 21, Jackson 2.19.1 Java 8, HAPI structures R4+DSTU3 Independent modules — same data, different representations REST serves OpenMRS frontend (O3) FHIR2 serves interoperability

Security Model

Authentication Flow: HTTP Basic Header -> AuthorizationFilter -> Context.authenticate() -> UsernamePasswordAuthenticationScheme -> HibernateContextDAO.authenticate() -> Parameterized JPA query (no SQL injection) -> SHA-512 + per-user salt (128-char) password verification -> Brute-force lockout after 8 failed attempts RBAC Model: User --> Role (Set, recursive inheritance) --> Privilege (Set, string-based, 250 char max) Superuser role bypasses ALL privilege checks @Authorized annotation on service methods AuthorizationAdvice (AOP) enforces at runtime No OAuth 2.0 / No SMART on FHIR in core or modules

User Guides

Step-by-step instructions for common OpenMRS tasks, with screenshots

Test Results

Clinical workflow test cases — validation of OpenMRS functionality

Modernization Roadmap

Findings from automated knowledge extraction — what the system reveals about itself

30-Day Sprint

One sprint. Four controlled phases. Clear day-30 result.

Week 1: StabilizeEnvironment access, build verification, initial knowledge base
Week 2: MapDependency graph, service map, FHIR gap analysis, technical debt inventory
Week 3: RebuildTargeted modernization on highest-leverage components
Week 4: HardenTesting, validation, migration playbook, knowledge handoff

Priority Matrix

High Impact, Low Risk

Spring Boot migrationReady
Spring Data JPA (15 simple DAOs)Ready
OpenAPI documentationReady
SpotBugs failOnError=trueReady
Legacy CI config cleanupReady

High Impact, Requires Coordination

Module system replacementEcosystem-wide
FHIR R4 native integrationBlocked by modules
SMART on FHIR + Bulk DataRequires OAuth 2.0
Context.getService() removalCross-module

Dependency Risk Assessment

Groovy 2.4 — EOL, upgrade to 4.x
XStream 1.4 — recurring CVE history
Commons Collections 3.2 — CVE-2015-7501 deserialization vuln
C3P0 — replace with HikariCP (Spring Boot default)
Joda-Time — replace with java.time (already on Java 21)
SpotBugs failOnError=false — security findings don't block builds

Data Integrity Risks

Patient merge uses move-not-copy — 3 TODOs in PatientServiceImpl confirm data loss risk
ObsServiceImpl: cascade purge not implemented (TODO line 327)
HL7v2 handlers: 10+ TODOs for incomplete segment processing
UserServiceImpl: "TODO Check required fields for user" — incomplete validation

Sequencing Constraints

Spring Boot migration must precede Spring Data JPA conversion (boot auto-configures DataSource, EntityManagerFactory)
Module system replacement blocks FHIR native integration (modules currently loaded via custom classloader)
C3P0 to HikariCP migration included in Spring Boot conversion (HikariCP is Boot default)
Context.getService() removal requires Spring DI refactor across all modules
SMART on FHIR requires OAuth 2.0, which requires security model upgrade

Ready to Execute

This analysis was produced by AI-orchestrated knowledge extraction in a single sprint. The full engagement includes targeted modernization, migration playbooks, and a living knowledge base your team inherits on day 30.

Learn More at SimplifyingSoftware

One sprint. Four phases. Your team owns what remains.