ID: S202606091026
Status: imported

Tags: avans 2-4 LU1

2026-05-11 todolist

  • Tool: Claude AI
  • Gebruiker:

Situatie

Ik wilde een duidelijk concreet todo lijstje die ik af kon werken.

Gebruikt

Ik gebruikte Claude AI met de volgende prompt:

I have this beautiful ADR, can you turn it into a (markdown) todo list? in order of things that I would most likely do first. Keep it simple.

ADR5_OpenMRS_Plugin-05-11

ID: S202606091024
Status: imported

Tags: avans 2-4 LU1

ADR5_OpenMRS_Plugin-05-11

Context and Problem Statement

De communicatiemodule heeft een mechanisme nodig waarmee individuele OpenMRS-instanties afspraakgebeurtenissen kunnen doorzenden naar de centrale externe RabbitMQ exchange op de VPS. Dit is de ingang van de gehele pipeline: zonder correcte events vanuit OpenMRS ontvangt de inbound processor (container 1) geen data en worden er geen notificaties verstuurd.

De vraag is hoe deze integratie technisch wordt gerealiseerd aan de OpenMRS-kant. De oplossing moet passen binnen de OpenMRS module-architectuur, werken met de Bahmni Appointment Scheduling module die in de referentie-installatie aanwezig is, en de communicatiemodule volledig ontkoppelen van de beschikbaarheid van individuele OpenMRS-instanties.


Considered Options

Optie A: Bestaande OpenMRS Event Module uitbreiden

De OpenMRS Event Module vuurt al events via Apache ActiveMQ wanneer domeinobjecten worden aangemaakt, gewijzigd of verwijderd. Een bestaande listener zou uitgebreid kunnen worden om events ook naar de externe RabbitMQ te forwarden.

Voordelen:

  • Maakt gebruik van bestaande infrastructuur
  • Events worden al gefired door de OpenMRS kern

Nadelen:

  • De OpenMRS Event Module gebruikt Apache ActiveMQ als interne broker, niet RabbitMQ; een brug tussen de twee is complex en foutgevoelig
  • De bestaande events zijn generieke OpenMRS domein-events, niet Bahmni appointment-specifiek
  • Aanpassen van een bestaande module introduceert het risico van onbedoelde bijwerkingen op andere functionaliteit

Optie B: Zelfgebouwde OpenMRS OMOD plugin

Een nieuwe plugin wordt gebouwd als een standaard OpenMRS module. Deze plugin registreert zichzelf als listener op Bahmni appointment events en publiceert deze rechtstreeks naar de externe RabbitMQ exchange van de communicatiemodule.

Voordelen:

  • Volledige controle over welke events worden gepubliceerd en in welk formaat
  • Geen risico van bijwerkingen op bestaande modules
  • Enkelvoudige verantwoordelijkheid: luisteren op Bahmni appointment events en doorsturen naar RabbitMQ
  • Het OMOD formaat is de standaard distributiemethode voor OpenMRS modules
  • Configuratie via OpenMRS global properties past binnen de bestaande beheersstructuur

Nadelen:

  • Vereist kennis van de OpenMRS module development API
  • Voegt een extra codebase toe naast de drie Spring Boot applicaties
  • Moet bij elke tenant apart geïnstalleerd en geconfigureerd worden

Optie C: Polling vanuit container 1

Container 1 pollt periodiek de Bahmni Appointments REST API per tenant in plaats van events te ontvangen.

Voordelen:

  • Geen installatie aan OpenMRS-kant nodig buiten de Bahmni module

Nadelen:

  • Eerder onderzocht en afgevallen als primaire integratiemethode vanwege annuleringsproblemen en temporele koppeling
  • Zie ADR 3 voor de volledige afweging

Decision Outcome

Gekozen: Optie B, Zelfgebouwde OpenMRS OMOD plugin

Justification De plugin-aanpak geeft volledige controle over het event formaat en de routing naar RabbitMQ zonder afhankelijk te zijn van de interne ActiveMQ infrastructuur van de OpenMRS Event Module. De plugin heeft bewust een minimale scope: hij doet niets anders dan luisteren op Bahmni appointment events en deze doorsturen naar de externe RabbitMQ exchange. Dit houdt de codebase klein, de verantwoordelijkheid enkelvoudig, en het risico op fouten laag.

De configuratie via OpenMRS global properties past binnen de vertrouwde beheersstructuur van OpenMRS en vereist geen technische kennis van RabbitMQ van de hospital IT-beheerder. Container 1 is de enige consumer van de externe queue en hoeft niets te weten over de plugin implementatie, wat de twee codebases volledig ontkoppeld houdt.


Plugin architectuur

De plugin bestaat uit drie onderdelen.

De activator is de entry point van de OMOD module. Hij wordt aangeroepen door OpenMRS bij het opstarten, valideert de global properties, en registreert de event listeners. Als een verplichte global property ontbreekt logt hij een foutmelding en registreert geen listeners zodat het systeem geen berichten verstuurt naar een onbekende bestemming. Bij afsluiten ruimt hij de listeners en de RabbitMQ verbinding netjes op.

De event listener luistert op drie event types die door de Bahmni Appointment Scheduling module worden gefired via de OpenMRS Event Module: CREATED wanneer een nieuwe afspraak wordt aangemaakt, UPDATED wanneer een bestaande afspraak wordt gewijzigd, en VOIDED wanneer een afspraak wordt geannuleerd. Voor elk ontvangen event haalt de listener de volledige afspraakdata op inclusief het telefoonnummer van de patiënt via GET /openmrs/ws/rest/v1/patient/{uuid}, bouwt een JSON bericht op, en geeft dit door aan de publisher.

De publisher beheert de verbinding met de externe RabbitMQ op de VPS en publiceert berichten naar appointment.exchange met de routing key appointment.{tenantId}.{eventType}. De publisher implementeert retry-logica: als RabbitMQ tijdelijk niet bereikbaar is worden berichten tot drie keer opnieuw geprobeerd met exponential backoff tussen pogingen.


Berichtformaat

Elk bericht dat de plugin publiceert bevat de volgende velden:

{
  "tenantId": "hospital-amsterdam",
  "eventType": "CREATED",
  "timestamp": "2025-01-22T13:00:00Z",
  "appointmentUuid": "395a61e4-83a0-4ed0-9697-568e7196be02",
  "appointmentNumber": "APT-001",
  "appointmentKind": "Scheduled",
  "patientUuid": "f97cbd02-73cd-4318-a8e0-e6e8d958d17d",
  "patientName": "Mark Williams",
  "patientIdentifier": "P-12345",
  "phoneNumber": "06-12345678",
  "emailAddress": "mark@example.com",
  "startDateTime": "2025-01-23T14:00:00Z",
  "endDateTime": "2025-01-23T14:30:00Z",
  "serviceName": "General Medicine service",
  "serviceUuid": "service-uuid-5678",
  "status": "Scheduled",
  "comments": null,
  "voided": false
}

Configuratie per tenant

De plugin leest twee verplichte global properties uit OpenMRS bij het opstarten.

communicatie.tenantId is een unieke identifier voor deze OpenMRS-instantie, ingesteld door de hospital IT-beheerder bij onboarding. Dit is de identifier die in de routing key en in elk bericht wordt opgenomen.

communicatie.rabbitmq.url is de verbindingsURL naar de centrale RabbitMQ instantie op de VPS inclusief gebruikersnaam en wachtwoord. De credentials zijn uniek per tenant en worden verstrekt door de beheerder van de communicatiemodule bij onboarding.


Consequences

Good, because:

  • De plugin heeft een enkelvoudige verantwoordelijkheid en een minimale codebase
  • Container 1 ontvangt events in real-time zonder polling-lag
  • Annuleringen worden direct als VOIDED event ontvangen zodat container 1 de database direct kan updaten
  • Het OMOD formaat is vertrouwd bij OpenMRS beheerders en past in de standaard installatieprocedure
  • Foutieve configuratie wordt bij het opstarten gedetecteerd en gelogd zodat het systeem niet stil faalt

Neutraal, because:

  • De plugin moet bij elke nieuwe tenant worden geïnstalleerd en geconfigureerd; dit is gedocumenteerd in de onboarding procedure voor technisch beheerders
  • Het berichtformaat moet stabiel blijven zolang er tenants actief zijn; wijzigingen vereisen een gecoördineerde update van zowel de plugin als container 1

Bad, because:

  • Als de externe RabbitMQ tijdelijk niet bereikbaar is kunnen events verloren gaan als de retry-pogingen uitgeput zijn; dit wordt gemonitord via de observability stack
  • De plugin introduceert een extra codebase naast de drie .NET Worker Services die onderhouden moet worden
  • Foutieve configuratie van de global properties leidt tot een plugin die geen berichten verstuurt zonder dat dit direct zichtbaar is in de OpenMRS interface; monitoring op de externe RabbitMQ exchange is noodzakelijk om dit te detecteren

More information

Implementatieaandachtspunten:

  • De plugin wordt gebouwd als een Maven project met de OpenMRS module archetype als basis
  • Dependencies: OpenMRS core API, Bahmni Appointments module API, RabbitMQ AMQP client library
  • Het OMOD bestand wordt geplaatst in de OpenMRS modules directory en wordt automatisch geladen bij herstart van OpenMRS
  • De plugin verbindt met de externe RabbitMQ via TLS 1.3 op poort 5671
  • Zie ADR 4 voor het volledige queue en exchange ontwerp
  • Zie ADR 3 voor de motivatie achter de keuze voor event-driven integratie boven polling
  • Zie ADR 6 voor de drie-container architectuur die de plugin als ingang gebruikt
  • Zie ADR 8 voor de beveiligingseisen rondom de RabbitMQ verbinding vanuit de plugin
Link to original

Resultaat

OpenMRS RabbitMQ Plugin - Todo List

  • Set up Maven project with OpenMRS OMOD archetype
  • Add dependencies (OpenMRS core, Bahmni Appointments API, RabbitMQ client)
  • Build the Activator class (lifecycle & listener registration)
  • Build the Event Listener (handle CREATED, UPDATED, VOIDED events)
  • Build the Publisher (RabbitMQ connection, retry logic, routing)
  • Implement message formatting (JSON structure)
  • Configure TLS 1.3 connection to RabbitMQ (port 5671)
  • Add tenant configuration via global properties
  • Add patient phone number retrieval via REST API call
  • Unit & integration tests
  • Document onboarding procedure
  • Set up RabbitMQ exchange monitoring
  • Package as OMOD file
  • Deploy to test environment

Verbeterpunten

Het was een redelijk todo lijtje, maar ik had het liever specefieker verwacht, dus ik had het beter specefieker kunnen aangeven, of gewoon zelf kunnen doen.