HL7 vs FHIR: Sekilas tentang protocol di dunia HealthTech

📋 Panduan Praktis untuk Java Backend Engineer

HL7 v2 & FHIR: Dua Standar yang Wajib Anda Kuasai untuk Masuk ke HealthTech

Dari ISO 8583 ke SATUSEHAT — panduan lengkap bagi Java developer yang ingin memahami standar interoperabilitas di industri kesehatan Indonesia.

✍️ Mohamad Yusuf Ibrahim · Senior Java Backend Engineer · 10+ tahun di Banking & Financial Services
HealthTech HL7 v2 FHIR R4 Spring Boot HAPI FHIR SATUSEHAT Interoperabilitas Java Backend

Saat saya pertama kali mendengar istilah "HL7" dan "FHIR" dalam konteks proyek migrasi sistem rekam medis, reaksi pertama saya: "Ini pasti mirip ISO 8583 tapi untuk rumah sakit." Ternyata analogi itu cukup akurat — dan menjadi jembatan yang membantu saya memahami ekosistem ini dengan jauh lebih cepat.

Artikel ini saya tulis sebagai starting point yang saya harap ada waktu saya pertama terjun ke healthcare tech: tidak terlalu dangkal, tidak terlalu teoritis — langsung ke inti yang penting bagi seorang Java developer.

Mengapa Interoperabilitas Jadi Masalah Besar?

Bayangkan seorang pasien yang menjalani operasi di Rumah Sakit A, melanjutkan rawat jalan di Klinik B, menebus obat di Apotek C, dan mengklaim asuransi ke BPJS. Empat entitas ini — tanpa standar komunikasi yang sama — tidak bisa berbagi data pasien secara otomatis. Dokter di Klinik B tidak tahu riwayat operasi lengkapnya. Apotek C tidak tahu alergi obatnya. BPJS meminta berkas manual.

⚠️ Dampata nyata tanpa interoperabilitas: Pemeriksaan laboratorium yang diulang tidak perlu, kesalahan dosis karena riwayat obat tidak lengkap, klaim asuransi yang memakan minggu, dan rekam medis kertas yang hilang saat pasien berpindah fasilitas.

Inilah mengapa HL7 International (Health Level Seven) hadir sejak 1987: menyusun standar agar semua sistem kesehatan bisa "berbicara" dalam bahasa yang sama. Mereka menghasilkan dua generasi standar yang kini hidup berdampingan: HL7 v2 dan FHIR.

HL7 v2 — Sang Veteran yang Masih Produktif

HL7 v2 (Health Level Seven Version 2) lahir pada 1987 dan merilis versi terakhirnya (2.9) pada 2019. Usianya hampir empat dekade, namun hingga hari ini ia masih menjalankan jutaan transaksi medis setiap harinya di seluruh dunia — termasuk di banyak rumah sakit Indonesia.

Struktur Pesan HL7 v2

Pesan HL7 v2 adalah teks biasa dengan format pipe-delimited. Setiap baris disebut Segment, setiap kolom dipisahkan dengan karakter |, dan subfield dipisahkan dengan ^.

Anatomi pesan ADT^A04 (Pasien Baru Mendaftar):

-- MSH: Message Header (wajib ada di semua pesan)
MSH|^~\&|SIMRS_BANDUNG|RS_HARAPAN|BPJS_GATEWAY|BPJS_PUSAT|20250622170000||ADT^A04^ADT_A01|MSG-2025-001|P|2.5.1

-- PID: Patient Identification
PID|1||12345678^^^RS_HARAPAN^MR||Ibrahim^Mohamad Yusuf||19850101|M|||Jl. Merdeka No.1^^Bandung^JB^40111^ID

-- PV1: Patient Visit (informasi kunjungan)
PV1|1|O|POLI-UMUM^Poliklinik Umum^RS_HARAPAN||||dr.SANJAYA^Sanjaya^Budi^^^dr.|||MED

-- IN1: Insurance (data BPJS)
IN1|1|BPJS|BPJS|BPJS Kesehatan||||0001987654321

Jenis-Jenis Pesan HL7 v2 yang Sering Ditemui

Message Type Nama Event Trigger
ADT^A01 Admit Patient Pasien rawat inap
ADT^A04 Register Patient Pendaftaran rawat jalan / IGD
ADT^A08 Update Patient Info Data demografis berubah
ORM^O01 General Order Message Order lab / radiologi dari dokter
ORU^R01 Unsolicited Observation Hasil lab dikirim ke SIMRS
MDM^T02 Medical Document Dokumen rekam medis / discharge summary
💡 Tip untuk Java Developer: HL7 v2 dikirim melalui protokol MLLP (Minimum Lower Layer Protocol) di atas TCP, bukan HTTP. Port default adalah 2575. Jadi jangan cari REST endpoint-nya — Anda perlu membuka socket connection. Library HAPI HL7v2 menangani ini untuk Anda.

FHIR — Generasi Baru yang Ramah Developer

FHIR (Fast Healthcare Interoperability Resources, dibaca "fire") adalah standar modern yang dikembangkan HL7 International sejak 2011. Filosofi dasarnya: buat standar kesehatan yang bisa langsung dipakai oleh web developer tanpa perlu belajar 500 halaman spesifikasi terlebih dahulu.

Versi aktif saat ini adalah FHIR R4 (Release 4, 2019), dan Indonesia menjadikannya fondasi platform SATUSEHAT milik Kemenkes.

Konsep Utama: Resources

Di FHIR, semua entitas direpresentasikan sebagai Resource — objek JSON mandiri yang punya struktur standar dan bisa diakses via REST API. Ada 140+ resource dalam FHIR R4, namun Anda cukup menguasai yang sering dipakai:

Resource Representasi Analogi di Banking
Patient Data demografis pasien Customer / Nasabah
Encounter Episode kunjungan / rawat inap Sesi transaksi / kontrak
Observation Hasil pemeriksaan (lab, vital sign) Transaction record
Condition Diagnosis / penyakit Account status / flag
MedicationRequest Resep dokter Payment instruction
Practitioner Data dokter / tenaga medis Merchant / counterparty
Organization Rumah sakit / klinik / apotek Bank / financial institution
Bundle Kumpulan resources (response batch) Batch file / settlement report
DiagnosticReport Laporan pemeriksaan lengkap Account statement
AllergyIntolerance Alergi dan intoleransi obat Customer restriction / blacklist

Contoh FHIR Resource: Patient (Lengkap)

{
  "resourceType": "Patient",
  "id": "pat-12345678",
  "meta": {
    "versionId": "1",
    "lastUpdated": "2025-06-22T17:00:00+07:00",
    "profile": ["https://fhir.kemkes.go.id/r4/StructureDefinition/Patient"]
  },
  "identifier": [
    {
      "use": "official",
      "system": "https://fhir.kemkes.go.id/id/nik",
      "value": "3273011501850001"
    },
    {
      "use": "local",
      "system": "http://rs-harapan.example.id/mrn",
      "value": "MRN-2025-12345"
    }
  ],
  "name": [
    {
      "use": "official",
      "family": "Ibrahim",
      "given": ["Mohamad", "Yusuf"]
    }
  ],
  "telecom": [
    { "system": "phone", "value": "+6281234567890", "use": "mobile" }
  ],
  "gender": "male",
  "birthDate": "1985-01-01",
  "address": [
    {
      "use": "home",
      "line": ["Jl. Merdeka No. 1"],
      "city": "Bandung",
      "postalCode": "40111",
      "country": "ID"
    }
  ]
}

FHIR REST API — Operasi Dasar

Operasi FHIR mengikuti konvensi REST yang sudah familiar bagi setiap backend developer:

-- CREATE: Daftarkan pasien baru
POST   /fhir/Patient

-- READ: Ambil data pasien berdasarkan ID
GET    /fhir/Patient/pat-12345678

-- UPDATE: Perbarui data lengkap
PUT    /fhir/Patient/pat-12345678

-- PATCH: Perbarui sebagian data (JSON Patch)
PATCH  /fhir/Patient/pat-12345678

-- SEARCH: Cari dengan query parameters
GET    /fhir/Patient?identifier=3273011501850001
GET    /fhir/Patient?name=Ibrahim&birthdate=1985-01-01
GET    /fhir/Observation?patient=pat-12345678&code=15074-8&_sort=-date

-- BATCH: Kirim beberapa resource sekaligus (via Bundle)
POST   /fhir  (dengan Bundle berisi multiple resources)
-- HISTORY: Riwayat versi resource GET /fhir/Patient/pat-12345678/_history

Perbandingan Lengkap: HL7 v2 vs FHIR

Aspek HL7 v2 FHIR R4
Format Data Pipe-delimited text JSON (default), XML, Turtle
Transport Protocol MLLP/TCP (port 2575) HTTP/HTTPS (REST)
Arsitektur Event-driven messaging Resource-centric REST API
Autentikasi Network-level (VPN/VNet) OAuth 2.0 / SMART on FHIR
Kemudahan Parsing Butuh library khusus (HAPI HL7) Standard JSON parser cukup
Query Kemampuan Terbatas, tidak fleksibel Search parameters yang powerful
Extensibility Z-segments (tidak standar) Extension mechanism (tetap valid)
Versi v2.1 s/d v2.9 DSTU2, STU3, R4, R4B, R5
Status di Indonesia Legacy — masih dominan di RS lama Standar SATUSEHAT Kemenkes
Cocok untuk Integrasi sistem lama, real-time event API modern, mobile apps, analytics

Implementasi di Spring Boot — Praktis dan Langsung

Setup: HL7 v2 dengan HAPI HL7

// build.gradle
dependencies {
    // HAPI HL7 v2 — library de facto untuk HL7 v2 di Java
    implementation 'ca.uhn.hapi:hapi-base:2.5.1'
    implementation 'ca.uhn.hapi:hapi-structures-v25:2.5.1'
    // MLLP transport via Spring Integration atau Apache Camel
    implementation 'org.apache.camel.springboot:camel-spring-boot-starter:4.6.0'
    implementation 'org.apache.camel.springboot:camel-hl7-starter:4.6.0'
    implementation 'org.apache.camel.springboot:camel-mllp-starter:4.6.0'
}
// Listener MLLP — menerima pesan HL7 v2 dari SIMRS
@Component
public class HL7v2MllpRoute extends RouteBuilder {

    @Override
    public void configure() {
        // Terima di port 2575, parse otomatis
        from("mllp://0.0.0.0:2575")
            .routeId("hl7v2-inbound")
            .unmarshal(HL7.hl7())                    // parse pipe-delimited → HAPI object
            .process(exchange -> {
                Message hl7Msg = exchange.getIn().getBody(Message.class);
                String msgType = hl7Msg.getName();   // "ADT_A04", "ORM_O01", dll

                // Kirim ke Kafka untuk diproses async
                exchange.setProperty("msgType", msgType);
            })
            .choice()
                .when(exchangeProperty("msgType").startsWith("ADT"))
                    .to("kafka:hl7-adt-events")
                .when(exchangeProperty("msgType").startsWith("ORU"))
                    .to("kafka:hl7-lab-results")
                .otherwise()
                    .to("kafka:hl7-other-events")
            .end();
    }
}

Setup: FHIR R4 dengan HAPI FHIR

// build.gradle
dependencies {
    // HAPI FHIR — toolkit FHIR paling lengkap untuk Java
    implementation 'ca.uhn.hapi.fhir:hapi-fhir-base:7.4.0'
    implementation 'ca.uhn.hapi.fhir:hapi-fhir-structures-r4:7.4.0'
    implementation 'ca.uhn.hapi.fhir:hapi-fhir-client:7.4.0'
    // Jika ingin run FHIR server sendiri:
    implementation 'ca.uhn.hapi.fhir:hapi-fhir-server:7.4.0'
    implementation 'ca.uhn.hapi.fhir:hapi-fhir-jpaserver-base:7.4.0'
}
// Service untuk berinteraksi dengan SATUSEHAT API
@Service
public class SatuSehatFhirService {

    private final IGenericClient fhirClient;
    private final FhirContext fhirContext;

    public SatuSehatFhirService() {
        // FHIR R4 context — inisialisasi sekali, thread-safe
        this.fhirContext = FhirContext.forR4();
        // Base URL SATUSEHAT (sandbox)
        this.fhirClient = fhirContext.newRestfulGenericClient(
            "https://api-satusehat.kemkes.go.id/fhir-r4/v1"
        );
        // Tambahkan interceptor untuk OAuth 2.0
        fhirClient.registerInterceptor(new BearerTokenAuthInterceptor("your-token"));
    }

    /**
     * Kirim data kunjungan pasien ke SATUSEHAT
     */
    public MethodOutcome createEncounter(Encounter encounter) {
        return fhirClient.create()
            .resource(encounter)
            .encodedJson()
            .execute();
    }

    /**
     * Cari riwayat lab pasien berdasarkan NIK
     */
    public Bundle searchLabResults(String nik) {
        return fhirClient.search()
            .forResource(Observation.class)
            .where(Observation.PATIENT.hasChainedProperty(
                Patient.IDENTIFIER.exactly().systemAndCode(
                    "https://fhir.kemkes.go.id/id/nik", nik
                )
            ))
            .and(Observation.CATEGORY.exactly().code("laboratory"))
            .sort().descending(Observation.DATE)
            .count(50)
            .returnBundle(Bundle.class)
            .execute();
    }
}

Pola Hybrid: HL7 v2 → Konversi → FHIR

Inilah pola yang paling sering digunakan dalam proyek nyata. Sistem lama mengirim HL7 v2, kita konversi ke FHIR untuk dilaporkan ke SATUSEHAT atau dikonsumsi sistem modern.

Arsitektur Hybrid HL7 v2 → FHIR

[ SIMRS Lama ] ──MLLP:2575──→ [ Camel MLLP Listener ]
                                              │
                                              ▼
[ Kafka: hl7-raw-events ] ←── parse & publish
                        │
                        ▼
[ HL7→FHIR Transformer Service ]
                        │
┌──────────────────────┤
▼                       ▼
[ HAPI FHIR JPA Server ]  [ SATUSEHAT API ]
(internal / on-premise)   (laporan Kemenkes)
// HL7 v2 ADT → FHIR Patient (contoh konversi)
@Component
public class HL7v2ToFhirConverter {

    public Patient convertPidToFhirPatient(PID pid) throws HL7Exception {
        Patient patient = new Patient();

        // MRN dari PID-3
        String mrn = pid.getPatientIdentifierList(0).getIDNumber().getValue();
        patient.addIdentifier()
            .setSystem("http://rs.example.id/mrn")
            .setValue(mrn);

        // Nama dari PID-5: family^given
        XPN name = pid.getPatientName(0);
        patient.addName()
            .setFamily(name.getFamilyName().getSurname().getValue())
            .addGiven(name.getGivenName().getValue());

        // Gender dari PID-8
        String sex = pid.getAdministrativeSex().getValue();
        patient.setGender("M".equals(sex)
            ? Enumerations.AdministrativeGender.MALE
            : Enumerations.AdministrativeGender.FEMALE);

        // Tanggal lahir dari PID-7
        patient.setBirthDateElement(new DateType(
            pid.getDateTimeOfBirth().getTime().getValueAsDate()
        ));

        return patient;
    }
}

Analogi Banking → HealthTech (Untuk Mempercepat Pemahaman)

Jika Anda datang dari dunia banking seperti saya, tabel mental ini akan sangat membantu:

HL7 v2 ≈
ISO 8583
Message-based, binary-adjacent, koneksi persisten TCP, dipakai di ATM & POS lama
FHIR R4 ≈
ISO 20022 / Open Banking API
JSON REST, OAuth 2.0, modern, dipakai untuk integrasi fintech & open API banking
MLLP ≈
MQ Series / JMS Queue
Transport layer yang menjamin delivery pesan penting, bukan HTTP
FHIR Bundle ≈
ISO 8583 Batch / Settlement File
Kumpulkan banyak transaksi/resource, kirim sekaligus
SATUSEHAT ≈
BI-FAST / SNAP API BI
Platform nasional yang mewajibkan standar tertentu untuk semua peserta
FHIR Terminology (CodeSystem) ≈
ISO 4217 (kode mata uang)
ICD-10 untuk diagnosis, LOINC untuk lab, SNOMED CT untuk klinis
📌 Perhatikan Sistem Coding Medis Salah satu hal yang paling membingungkan bagi developer baru di healthcare: setiap data klinis punya kode standarnya sendiri. Diagnosis pakai ICD-10 (Kemenkes memakai ICD-10-CM). Hasil lab pakai LOINC. Prosedur medis pakai ICD-9-CM atau SNOMED CT. Obat pakai RxNorm. FHIR menggunakan sistem ini lewat resource CodeSystem dan ValueSet — mirip enum/lookup table yang hidup dan bisa diquery via API.

SATUSEHAT — Konteks Indonesia yang Wajib Dipahami

Sejak 2022, Kementerian Kesehatan mewajibkan seluruh fasilitas kesehatan (faskes) di Indonesia — rumah sakit, puskesmas, klinik, apotek — untuk terhubung ke platform SATUSEHAT. Platform ini menggunakan FHIR R4 sebagai standar datanya.

Aspek Detail
Base URL Sandbox https://api-satusehat.kemkes.go.id/fhir-r4/v1
Autentikasi OAuth 2.0 Client Credentials (client_id + client_secret per faskes)
Dokumen Wajib Dikirim Encounter, Condition (diagnosis), Observation (vital sign + lab), MedicationRequest, Procedure
Identitas Pasien Wajib match ke NIK via https://fhir.kemkes.go.id/id/nik
Profil Custom Kemenkes mendefinisikan StructureDefinition sendiri di atas FHIR R4 standar
⚠️ Gotcha SATUSEHAT yang Sering Ditemui: NIK pasien harus valid dan terverifikasi di Dukcapil. Jika pasien tidak punya NIK valid, Anda perlu mekanisme fallback khusus. Resource dikirim dalam urutan tertentu (Patient → Encounter → Condition/Observation/Procedure), karena ada referential integrity check.

Roadmap Belajar untuk Java Developer

  • 1
    Pahami struktur HL7 v2 secara manual Baca dan parse pesan ADT^A01 dan ORU^R01 menggunakan HAPI HL7. Coba buat MLLP listener sederhana dengan Apache Camel.
  • 2
    Kuasai FHIR R4 resources utama Mulai dari Patient, Encounter, Observation, Condition. Baca spesifikasi di hl7.org/fhir/R4 — lebih readable dari yang Anda bayangkan.
  • 3
    Setup HAPI FHIR JPA Server secara lokal Jalankan via Docker: docker run -p 8080:8080 hapiproject/hapi:latest. Ini memberi Anda sandbox FHIR server lengkap untuk bereksperimen.
  • 4
    Pelajari SMART on FHIR + OAuth 2.0 Framework autentikasi standar untuk FHIR apps. Analoginya: seperti OAuth 2.0 di Open Banking, tapi dengan scope yang spesifik untuk data klinis.
  • 5
    Daftar akun SATUSEHAT developer sandbox Buka platform.satusehat.kemkes.go.id, daftar sebagai developer, dan coba kirim Patient + Encounter ke environment sandbox.
  • 6
    Bangun hybrid pipeline HL7 v2 → FHIR Ini adalah skill yang paling dicari di pasar. Buat proof-of-concept: Camel MLLP listener → Kafka → transformer service → HAPI FHIR server.

Toolkit Esensial: Perbandingan Library

🔧 HL7 v2 (HAPI HL7)

  • hapi-base — core library
  • hapi-structures-v25 — model v2.5
  • camel-hl7 — parser otomatis
  • camel-mllp — transport MLLP
  • Alternatif: Spring Integration + HL7

🚀 FHIR (HAPI FHIR)

  • hapi-fhir-base — core library
  • hapi-fhir-structures-r4 — model R4
  • hapi-fhir-client — REST client
  • hapi-fhir-jpaserver-base — full FHIR server
  • Alternatif: Microsoft FHIR SDK for Java

Kesimpulan

HL7 v2 dan FHIR bukan kompetitor — mereka adalah dua generasi standar yang melayani kebutuhan berbeda dan harus hidup berdampingan di dunia nyata. Hampir semua proyek healthcare integration yang serius saat ini membutuhkan kemampuan menangani keduanya.

Bagi Anda yang datang dari banking atau enterprise Java, fondasi yang sudah Anda miliki — pemahaman tentang messaging protocol, event-driven architecture, dan API security — adalah aset yang sangat berharga. Konteksnya berbeda (data klinis jauh lebih sensitif dan kompleks dari data finansial), namun pola teknisnya sangat paralel.

Saat ini saya sedang membangun project demo Spring Boot yang mengimplementasikan pipeline hybrid HL7 v2 → FHIR lengkap dengan unit test, integration test ke SATUSEHAT sandbox, dan deployment via Docker Compose. Jika Anda tertarik, saya akan share artikel lanjutan beserta link repository GitHub-nya.

💬 Ada Pertanyaan atau Pengalaman Serupa?

Punya pengalaman kerja dengan HL7 atau FHIR? Sedang mengerjakan proyek integrasi SATUSEHAT? Atau baru mulai belajar healthcare tech? Tinggalkan komentar di bawah — saya baca semua komentar dan senang berdiskusi.

Jika artikel ini bermanfaat, bagikan ke rekan engineer lainnya. Semakin banyak developer Indonesia yang menguasai standar ini, semakin baik infrastruktur kesehatan digital kita.

Comments

Popular posts from this blog

Numpang Kerja Remote dari Bandung Creative Hub

Membangun AI Development Assistant Lokal

Debugging PHP Web dengan XDebug di Intellij IDEA (PHP STORM)