Skip to main content

Editions (DEPLOY-002)

The platform ships in more than one shape from one tree. A pharmacy does not need the lab, imaging or workforce schemas; a standalone laboratory does not need pharmacy. An edition is a dependency list, and nothing more.

BASE (every edition) core + erp + reporting + fhir + hl7
+ every module platform-app the hospital edition, the default deployable
+ pharmacy pharmacy-edition
+ lab lab-edition

The base is declared once, in backend/dist/edition-base, together with the PlatformApplication launcher and edition-base.yml. Listing it module-by-module in each edition is how one edition quietly loses reporting.

What an edition module contains

Three files, and they must agree with each other:

FileDecides
pom.xmlwhich module code is in the jar
src/main/resources/db/changelog/<edition>.db.changelog-master.yamlwhich schemas the deployment creates
src/main/resources/application.ymlwhich cross-module Feign clients are registered

EditionCompositionTest fails the build when they disagree, naming the edition and the module. Each disagreement otherwise fails in a way that does not name its cause:

  • in the pom but not the changelog: the jar boots and 500s on the first query, against tables nobody created;
  • in the changelog but not the pom: a schema no release will ever migrate again, in a database an operator will later be asked to explain;
  • a module flag left unstated: DEPLOY-001 registers a domain's clients when the property is absent, so the edition demands a <module>.service.url for a module it does not ship, and the context fails at load.

Declining a module

false must be stated. matchIfMissing = true is deliberate — this landed on a working platform and a default of "off" would have silently removed clients from every deployment that had not yet heard of the flag. Saying false is how an edition declines a module:

platform:
module:
pharmacy:
enabled: true
lab:
enabled: false

The eight gated domains are dhis2, hl7, imaging, insurance, lab, pharmacy, reporting, workforce. The other client packages are core engines, and core is in every edition.

What a module does when its neighbour is not deployed

The answer is per case, and "carry on quietly" is usually wrong. Absent a module, a validator has nothing to validate — which is not the same as having found nothing wrong:

CallerAbsent moduleBehaviour
ErpStaffReferenceValidatorworkforcerefuses the reference: no staff profile in this deployment can be verified
ErpClinicalProductGuardpharmacyrefuses the line: there is no catalogue for the id to name
WorkforceLabourAccrualAdapter, WorkforcePayrollPostingAdapter, PharmacyCogsAdapterworkforce / pharmacyisConfigured() is false — the posting source is skipped, as it is when its flag or URL is unset
FhirCoverageClaimServiceImplinsuranceempty Bundle: a deployment with no insurance module holds no Coverage, so the search succeeds and finds nothing
OruMessageHandlerimaging and labrejects the message with a reason, through the path an unknown accession already takes — never accepted and dropped
PatientMergeModuleOrchestratorServiceImplanyNOT_DEPLOYED: nothing is being asserted, there is simply no record to re-point

Configuration layering

Since CONF-002 there is exactly one application.yml on a deployable's classpath. An edition's own application.yml is small, and everything shared is imported:

PrecedenceSource
1 (wins)environment variables and .env
2the edition's application.yml — its changelog and its module flags
3edition-base.yml, imported by it
4each module's uhp-module-defaults.yml

edition-base.yml is deliberately not called application.yml: a second one on the classpath would make which of them the jar reads depend on jar order.

Building and running an edition

./mvnw -o -pl dist/pharmacy-edition -am -DskipTests install
java -jar backend/dist/pharmacy-edition/target/pharmacy-edition-0.0.1-SNAPSHOT.jar

⚠ Use APP_PORT, never --server.port: the latter moves only the listener, and the loopback Feign clients keep calling the old port.

What an edition is not

⚠ It is not a smaller download. The hospital jar is 180 MB and the pharmacy jar 177 MB — nearly all of both is shared third-party code. The saving is in the schemas created, the HTTP surface exposed and the modules an operator has to run and patch, not in bytes.

Topology is a separate axis

An edition can run bundled (one process) or distributed (one per module). See Topology, and note that under distributed a missing <module>.service.url is fatal at startup rather than quietly defaulted to loopback.