Back to blog
Integration Engineeringadvanced

FHIR & HL7 Integration: Healthcare APIs in .NET

Build healthcare integrations with FHIR R4 and HL7 v2 in .NET. Covers FHIR resource model, SMART on FHIR authentication, Firely SDK, HL7 parsing, patient data pipelines, and real-world integration patterns.

LearnixoApril 13, 202611 min read
View Source
FHIRHL7Healthcare.NETIntegrationC#Azure
Share:𝕏

Healthcare Integration Landscape

Healthcare data exchange is uniquely complex. Patient data must be:

  • Interoperable: systems from different vendors must share data
  • Secure: HIPAA/GDPR compliance, patient consent
  • Accurate: clinical decisions depend on correct data
  • Auditable: full history of who accessed what and when

Two dominant standards define how healthcare systems communicate:

| Standard | Type | Used For | |----------|------|---------| | HL7 v2 | Pipe-delimited messages | Legacy hospital systems (ADT, lab results, orders) | | FHIR R4 | REST + JSON/XML | Modern APIs, mobile apps, cloud integrations |

Most healthcare organisations run both β€” FHIR for new integrations, HL7 v2 for legacy systems (Epic, Cerner, Meditech).


FHIR Fundamentals

FHIR (Fast Healthcare Interoperability Resources) is a modern REST API standard for healthcare data.

Core concepts

  • Resource: a unit of healthcare data β€” Patient, Observation, Medication, Encounter, etc.
  • Server: exposes a FHIR-compliant REST API
  • Bundle: a container for multiple resources
  • Profile: a constraint on a base resource (e.g., UK Core Patient profile)
  • Extension: adding custom fields to a resource

FHIR REST API

GET    /Patient/123              β†’ read a patient
POST   /Patient                  β†’ create a patient (server assigns ID)
PUT    /Patient/123              β†’ update/replace a patient
PATCH  /Patient/123              β†’ partial update
DELETE /Patient/123              β†’ delete (creates a tombstone)
GET    /Patient?family=Smith     β†’ search
GET    /Patient/123/_history     β†’ version history
POST   /                         β†’ batch/transaction bundle

FHIR Patient resource (JSON)

JSON
{
  "resourceType": "Patient",
  "id": "example-patient-1",
  "meta": {
    "versionId": "1",
    "lastUpdated": "2025-04-13T09:30:00Z",
    "profile": ["https://fhir.hl7.org.uk/StructureDefinition/UKCore-Patient"]
  },
  "identifier": [
    {
      "system": "https://fhir.nhs.uk/Id/nhs-number",
      "value": "9000000009"
    }
  ],
  "name": [
    {
      "use": "official",
      "family": "Smith",
      "given": ["John", "Paul"]
    }
  ],
  "gender": "male",
  "birthDate": "1985-03-15",
  "address": [
    {
      "use": "home",
      "line": ["42 High Street"],
      "city": "Manchester",
      "postalCode": "M1 1AA",
      "country": "GB"
    }
  ],
  "telecom": [
    { "system": "phone", "value": "+44 7700 900000", "use": "mobile" },
    { "system": "email", "value": "john.smith@example.com" }
  ],
  "generalPractitioner": [
    { "reference": "Organization/gp-practice-001" }
  ]
}

Setup β€” Firely SDK (.NET)

The official .NET FHIR SDK is Firely (formerly Hl7.Fhir.Net).

Bash
dotnet add package Hl7.Fhir.R4
dotnet add package Hl7.Fhir.Validation.R4
C#
using Hl7.Fhir.Model;
using Hl7.Fhir.Rest;
using Hl7.Fhir.Serialization;

// Create a Patient resource
var patient = new Patient
{
    Id = "patient-001",
    Identifier = new List<Identifier>
    {
        new Identifier("https://fhir.nhs.uk/Id/nhs-number", "9000000009")
    },
    Name = new List<HumanName>
    {
        new HumanName().WithGiven("John").AndFamily("Smith")
    },
    Gender = AdministrativeGender.Male,
    BirthDate = "1985-03-15",
    Active = true,
};

// Serialize to JSON
var serializer = new FhirJsonSerializer(new SerializerSettings { Pretty = true });
string json = serializer.SerializeToString(patient);

// Deserialize from JSON
var parser = new FhirJsonParser();
var parsedPatient = parser.Parse<Patient>(json);

// Access structured data
string nhsNumber = parsedPatient.Identifier
    .FirstOrDefault(i => i.System == "https://fhir.nhs.uk/Id/nhs-number")?.Value ?? "";

FHIR Client

C#
var client = new FhirClient("https://r4.smarthealthit.org");

// Read a patient
Patient patient = await client.ReadAsync<Patient>("Patient/87a339d0-8cae-418e-89c7-8651e6aab3c6");

// Search patients by NHS number
Bundle results = await client.SearchAsync<Patient>(new[]
{
    ("identifier", "https://fhir.nhs.uk/Id/nhs-number|9000000009")
});

foreach (var entry in results.Entry)
{
    var p = (Patient)entry.Resource;
    Console.WriteLine($"{p.Name[0].FamilyElement} - {p.BirthDate}");
}

// Create a new patient
var created = await client.CreateAsync(patient);
Console.WriteLine($"Created: {created.Id}");

// Update patient
patient.Active = false;
var updated = await client.UpdateAsync(patient);

// Get patient with observations
var observation = new Observation
{
    Status = ObservationStatus.Final,
    Code = new CodeableConcept("http://loinc.org", "8867-4", "Heart rate"),
    Subject = new ResourceReference("Patient/patient-001"),
    Effective = new FhirDateTime(DateTimeOffset.UtcNow),
    Value = new Quantity(72, "beats/min", "http://unitsofmeasure.org")
};
await client.CreateAsync(observation);

// Bundle transaction (atomic: all succeed or all fail)
var bundle = new Bundle { Type = Bundle.BundleType.Transaction };
bundle.Entry.Add(new Bundle.EntryComponent
{
    FullUrl = "urn:uuid:patient-temp-id",
    Resource = patient,
    Request = new Bundle.RequestComponent
    {
        Method = Bundle.HTTPVerb.POST,
        Url = "Patient"
    }
});
bundle.Entry.Add(new Bundle.EntryComponent
{
    Resource = observation,
    Request = new Bundle.RequestComponent
    {
        Method = Bundle.HTTPVerb.POST,
        Url = "Observation"
    }
});

var transactionResult = await client.TransactionAsync(bundle);

SMART on FHIR Authentication

SMART on FHIR is the standard OAuth2 profile for FHIR APIs.

C#
// backend-to-backend: client credentials flow
public class FhirAuthService
{
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly SmartSettings _settings;

    public async Task<string> GetAccessTokenAsync()
    {
        var client = _httpClientFactory.CreateClient();

        // Discover FHIR server capabilities
        var capabilityStatement = await client.GetFromJsonAsync<JsonDocument>(
            $"{_settings.FhirBaseUrl}/metadata");

        var tokenUrl = ExtractTokenUrl(capabilityStatement);

        // Request token
        var response = await client.PostAsync(tokenUrl, new FormUrlEncodedContent(new[]
        {
            ("grant_type",    "client_credentials"),
            ("client_id",     _settings.ClientId),
            ("client_secret", _settings.ClientSecret),
            ("scope",         "system/Patient.read system/Observation.read"),
        }));

        var tokenResponse = await response.Content.ReadFromJsonAsync<TokenResponse>();
        return tokenResponse!.AccessToken;
    }
}

// Register authenticated FHIR client
builder.Services.AddHttpClient<IFhirService, FhirService>(client =>
{
    client.BaseAddress = new Uri(settings.FhirBaseUrl);
}).AddHttpMessageHandler<FhirAuthHandler>();

public class FhirAuthHandler : DelegatingHandler
{
    private readonly FhirAuthService _auth;

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken ct)
    {
        var token = await _auth.GetAccessTokenAsync();
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
        return await base.SendAsync(request, ct);
    }
}

Building a FHIR Service

C#
public interface IFhirPatientService
{
    Task<PatientDto?> GetByNhsNumberAsync(string nhsNumber, CancellationToken ct = default);
    Task<IReadOnlyList<ObservationDto>> GetObservationsAsync(string patientId, CancellationToken ct = default);
    Task<string> CreateOrUpdatePatientAsync(PatientDto dto, CancellationToken ct = default);
}

public class FhirPatientService : IFhirPatientService
{
    private readonly FhirClient _client;
    private readonly IMapper _mapper;
    private readonly ILogger<FhirPatientService> _logger;

    public async Task<PatientDto?> GetByNhsNumberAsync(string nhsNumber, CancellationToken ct = default)
    {
        try
        {
            var bundle = await _client.SearchAsync<Patient>(new[]
            {
                ("identifier", $"https://fhir.nhs.uk/Id/nhs-number|{nhsNumber}")
            });

            var patient = bundle.Entry
                .Select(e => e.Resource as Patient)
                .FirstOrDefault(p => p is not null);

            return patient is null ? null : _mapper.Map<PatientDto>(patient);
        }
        catch (FhirOperationException ex) when (ex.Status == System.Net.HttpStatusCode.NotFound)
        {
            return null;
        }
    }

    public async Task<IReadOnlyList<ObservationDto>> GetObservationsAsync(
        string patientId, CancellationToken ct = default)
    {
        var bundle = await _client.SearchAsync<Observation>(new[]
        {
            ("patient", patientId),
            ("_sort", "-date"),
            ("_count", "50"),
        });

        return bundle.Entry
            .Select(e => e.Resource as Observation)
            .Where(o => o is not null)
            .Select(o => _mapper.Map<ObservationDto>(o!))
            .ToList();
    }

    public async Task<string> CreateOrUpdatePatientAsync(PatientDto dto, CancellationToken ct = default)
    {
        // Search for existing patient by NHS number
        var existing = await GetByNhsNumberAsync(dto.NhsNumber, ct);

        var fhirPatient = _mapper.Map<Patient>(dto);

        if (existing is null)
        {
            var created = await _client.CreateAsync(fhirPatient);
            _logger.LogInformation("Created FHIR Patient {Id} for NHS {NhsNumber}", created.Id, dto.NhsNumber);
            return created.Id!;
        }
        else
        {
            fhirPatient.Id = existing.FhirId;
            await _client.UpdateAsync(fhirPatient);
            _logger.LogInformation("Updated FHIR Patient {Id}", existing.FhirId);
            return existing.FhirId;
        }
    }
}

HL7 v2 Message Parsing

HL7 v2 is the dominant standard in hospital systems. Messages look like:

MSH|^~\&|SENDING_APP|SENDING_FAC|RECV_APP|RECV_FAC|20250413093000||ADT^A01|MSG001|P|2.5
EVN|A01|20250413093000
PID|1||9000000009^^^NHS^NH||Smith^John^Paul||19850315|M|||42 High Street^^Manchester^^M1 1AA^GBR|||||||||||||||
PV1|1|I|Ward7^Bed12^Room2||||12345^Jones^Sarah^Dr^MD|||MED||||ADM|||||||||||||||||||||||||20250413|||

Parsing with NHapiTools

Bash
dotnet add package NHapi.Model.V25
dotnet add package NHapi.Base
C#
using NHapi.Base.Parser;
using NHapi.Model.V25.Message;
using NHapi.Model.V25.Segment;

public class Hl7MessageParser
{
    private readonly PipeParser _parser = new();

    public AdtAdmissionDto? ParseAdt(string rawMessage)
    {
        try
        {
            var message = _parser.Parse(rawMessage);
            if (message is not ADT_A01 adt) return null;

            var pid = adt.PID;
            var pv1 = adt.PV1;

            return new AdtAdmissionDto
            {
                // PID-3: patient identifier list
                NhsNumber = pid.GetPatientIdentifierList(0).IDNumber.Value,

                // PID-5: patient name
                FamilyName = pid.GetPatientName(0).FamilyName.Surname.Value,
                GivenName  = pid.GetPatientName(0).GivenName.Value,

                // PID-7: date of birth
                DateOfBirth = DateTime.ParseExact(
                    pid.DateTimeOfBirth.Time.Value, "yyyyMMdd", null),

                // PID-8: sex
                Gender = pid.AdministrativeSex.Value,

                // PV1: visit information
                WardCode     = pv1.AssignedPatientLocation.PointOfCare.Value,
                AdmissionDate = DateTime.ParseExact(
                    pv1.AdmitDateTime.Time.Value, "yyyyMMddHHmmss", null),
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to parse HL7 message");
            return null;
        }
    }

    // Parse lab result (ORU^R01)
    public LabResultDto? ParseLabResult(string rawMessage)
    {
        var message = _parser.Parse(rawMessage);
        if (message is not ORU_R01 oru) return null;

        var pid = oru.GetPATIENT_RESULT(0).PATIENT.PID;
        var obr = oru.GetPATIENT_RESULT(0).GetORDER_OBSERVATION(0).OBR;
        var obx = oru.GetPATIENT_RESULT(0).GetORDER_OBSERVATION(0).GetOBSERVATION(0).OBX;

        return new LabResultDto
        {
            NhsNumber   = pid.GetPatientIdentifierList(0).IDNumber.Value,
            TestCode    = obr.UniversalServiceIdentifier.Identifier.Value,
            TestName    = obr.UniversalServiceIdentifier.Text.Value,
            Result      = obx.GetObservationValue(0).Data?.ToString() ?? "",
            Units       = obx.Units.Identifier.Value,
            ReferenceRange = obx.ReferencesRange.Value,
            AbnormalFlag = obx.GetAbnormalFlags(0).Value,
            ResultDate  = DateTime.ParseExact(obx.DateTimeOfTheObservation.Time.Value, "yyyyMMddHHmmss", null),
        };
    }

    // Build HL7 ACK (acknowledgement)
    public string BuildAck(string messageControlId, string ackCode = "AA")
    {
        var ack = new ACK();
        ack.MSH.SendingApplication.NamespaceID.Value = "INTEGRATION_ENGINE";
        ack.MSH.MessageType.MessageCode.Value = "ACK";
        ack.MSH.MessageControlID.Value = Guid.NewGuid().ToString("N")[..20];
        ack.MSA.AcknowledgmentCode.Value = ackCode;
        ack.MSA.MessageControlID.Value = messageControlId;
        return _parser.Encode(ack);
    }
}

HL7 MLLP Listener (TCP)

HL7 v2 messages are sent over MLLP (Minimal Lower Layer Protocol) β€” a simple TCP framing protocol.

C#
// MLLP framing: VT + message + FS + CR
// VT = 0x0B (vertical tab)
// FS = 0x1C (file separator)
// CR = 0x0D (carriage return)

public class MllpListener : BackgroundService
{
    private const byte VT = 0x0B;
    private const byte FS = 0x1C;
    private const byte CR = 0x0D;

    private readonly ILogger<MllpListener> _logger;
    private readonly Hl7MessageParser _parser;
    private readonly IServiceScopeFactory _scopeFactory;
    private TcpListener? _tcpListener;

    protected override async Task ExecuteAsync(CancellationToken ct)
    {
        _tcpListener = new TcpListener(IPAddress.Any, 2575);
        _tcpListener.Start();
        _logger.LogInformation("MLLP listener started on port 2575");

        while (!ct.IsCancellationRequested)
        {
            var client = await _tcpListener.AcceptTcpClientAsync(ct);
            _ = HandleClientAsync(client, ct);  // handle each connection concurrently
        }
    }

    private async Task HandleClientAsync(TcpClient client, CancellationToken ct)
    {
        using var stream = client.GetStream();
        var buffer = new byte[65536];
        var messageBuffer = new List<byte>();

        while (!ct.IsCancellationRequested)
        {
            int bytesRead = await stream.ReadAsync(buffer, ct);
            if (bytesRead == 0) break;

            for (int i = 0; i < bytesRead; i++)
            {
                if (buffer[i] == VT)
                {
                    messageBuffer.Clear();
                }
                else if (buffer[i] == FS)
                {
                    var rawMessage = Encoding.UTF8.GetString(messageBuffer.ToArray());
                    await ProcessMessageAsync(rawMessage, stream, ct);
                    messageBuffer.Clear();
                }
                else
                {
                    messageBuffer.Add(buffer[i]);
                }
            }
        }
    }

    private async Task ProcessMessageAsync(string rawMessage, NetworkStream stream, CancellationToken ct)
    {
        string messageControlId = "UNKNOWN";
        try
        {
            messageControlId = ExtractMessageControlId(rawMessage);
            _logger.LogInformation("Received HL7 message: {MessageControlId}", messageControlId);

            // Route by message type
            var msgType = ExtractMessageType(rawMessage);
            using var scope = _scopeFactory.CreateScope();

            switch (msgType)
            {
                case "ADT^A01":  // admission
                case "ADT^A08":  // update patient info
                    var adtDto = _parser.ParseAdt(rawMessage);
                    if (adtDto is not null)
                    {
                        var service = scope.ServiceProvider.GetRequiredService<IPatientSyncService>();
                        await service.SyncAdmissionAsync(adtDto, ct);
                    }
                    break;

                case "ORU^R01":  // lab result
                    var labDto = _parser.ParseLabResult(rawMessage);
                    if (labDto is not null)
                    {
                        var service = scope.ServiceProvider.GetRequiredService<ILabResultService>();
                        await service.ProcessLabResultAsync(labDto, ct);
                    }
                    break;
            }

            // Send ACK
            var ack = _parser.BuildAck(messageControlId, "AA");
            var ackBytes = Encoding.UTF8.GetBytes($"{(char)VT}{ack}{(char)FS}{(char)CR}");
            await stream.WriteAsync(ackBytes, ct);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error processing HL7 message {MessageControlId}", messageControlId);
            var nak = _parser.BuildAck(messageControlId, "AE");  // AE = Application Error
            var nakBytes = Encoding.UTF8.GetBytes($"{(char)VT}{nak}{(char)FS}{(char)CR}");
            await stream.WriteAsync(nakBytes, ct);
        }
    }
}

HL7 to FHIR Mapping Pipeline

A common requirement: receive HL7 v2 from a hospital, transform to FHIR, write to a FHIR server.

C#
public class Hl7ToFhirPipeline
{
    private readonly Hl7MessageParser _hl7Parser;
    private readonly IFhirPatientService _fhirService;
    private readonly IMapper _mapper;

    public async Task ProcessAdmissionAsync(string hl7Message, CancellationToken ct)
    {
        // 1. Parse HL7
        var admission = _hl7Parser.ParseAdt(hl7Message);
        if (admission is null)
            throw new InvalidOperationException("Failed to parse ADT message");

        // 2. Map to FHIR Patient
        var patientDto = new PatientDto
        {
            NhsNumber   = admission.NhsNumber,
            FamilyName  = admission.FamilyName,
            GivenNames  = new[] { admission.GivenName },
            DateOfBirth = admission.DateOfBirth,
            Gender      = MapGender(admission.Gender),
        };

        // 3. Upsert to FHIR server
        string fhirId = await _fhirService.CreateOrUpdatePatientAsync(patientDto, ct);

        // 4. Create FHIR Encounter for the admission
        var encounter = new Encounter
        {
            Status = Encounter.EncounterStatus.InProgress,
            Class = new Coding("http://terminology.hl7.org/CodeSystem/v3-ActCode", "IMP", "inpatient encounter"),
            Subject = new ResourceReference($"Patient/{fhirId}"),
            Period = new Period { Start = admission.AdmissionDate.ToString("yyyy-MM-ddTHH:mm:ssZ") },
            Location = new List<Encounter.LocationComponent>
            {
                new Encounter.LocationComponent
                {
                    Location = new ResourceReference { Display = $"{admission.WardCode}" }
                }
            }
        };

        await _fhirService.CreateEncounterAsync(encounter, ct);
    }
}

Azure FHIR Service

Azure Health Data Services includes a managed FHIR server.

C#
// appsettings.json
{
  "FhirServer": {
    "BaseUrl": "https://your-workspace.fhir.azurehealthcareapis.com",
    "TenantId": "your-tenant-id",
    "ClientId": "your-client-id",
    "ClientSecret": "your-client-secret"
  }
}

// Program.cs β€” register with Azure credential
builder.Services.AddSingleton<FhirClient>(sp =>
{
    var settings = sp.GetRequiredService<IOptions<FhirSettings>>().Value;
    var credential = new ClientSecretCredential(
        settings.TenantId, settings.ClientId, settings.ClientSecret);

    var httpClient = new HttpClient(new BearerTokenHandler(credential,
        "https://healthcareapis.azure.com/.default"));

    return new FhirClient(settings.BaseUrl, httpClient);
});

FHIR Subscription (Webhooks)

FHIR Subscriptions notify your system when data changes on the FHIR server.

JSON
{
  "resourceType": "Subscription",
  "status": "active",
  "reason": "Notify on new observations",
  "criteria": "Observation?patient=Patient/123",
  "channel": {
    "type": "rest-hook",
    "endpoint": "https://your-api.com/fhir/webhooks",
    "payload": "application/fhir+json",
    "header": ["Authorization: Bearer {token}"]
  }
}
C#
// Webhook handler
[HttpPost("/fhir/webhooks")]
public async Task<IActionResult> HandleFhirNotification(
    [FromBody] Bundle bundle,
    CancellationToken ct)
{
    foreach (var entry in bundle.Entry)
    {
        if (entry.Resource is Observation obs)
        {
            await _observationProcessor.ProcessAsync(obs, ct);
        }
    }
    return Ok();
}

Audit and Compliance

FHIR mandates an audit trail. Every access to patient data must be logged (HIPAA, GDPR).

C#
public class FhirAuditService
{
    private readonly FhirClient _client;

    public async Task LogAccessAsync(
        string userId, string patientId, string action, string resourceType)
    {
        var auditEvent = new AuditEvent
        {
            Type = new Coding("http://terminology.hl7.org/CodeSystem/audit-event-type", "rest"),
            Recorded = DateTimeOffset.UtcNow,
            Outcome = AuditEvent.AuditEventOutcome.N0,  // success
            Agent = new List<AuditEvent.AgentComponent>
            {
                new AuditEvent.AgentComponent
                {
                    Who = new ResourceReference($"Practitioner/{userId}"),
                    Requestor = true,
                    Network = new AuditEvent.NetworkComponent
                    {
                        Address = "192.168.1.1",
                        Type = AuditEvent.AuditEventAgentNetworkType.N2,
                    }
                }
            },
            Entity = new List<AuditEvent.EntityComponent>
            {
                new AuditEvent.EntityComponent
                {
                    What = new ResourceReference($"Patient/{patientId}"),
                    Role = new Coding("http://terminology.hl7.org/CodeSystem/object-role", "1"),
                }
            },
        };

        await _client.CreateAsync(auditEvent);
    }
}

Production Architecture

Hospital Systems (Epic/Cerner)
        β”‚ HL7 v2 over MLLP (TCP 2575)
        β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Integration Engine (.NET)                   β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ MLLP Listener│──►│ HL7 Parser   │──►│ FHIR Mapper β”‚  β”‚
β”‚  β”‚ (Port 2575)  β”‚   β”‚              β”‚   β”‚             β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                               β”‚          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚              Service Bus / Queue                   β”‚   β”‚
β”‚  β”‚  (decouple ingestion from processing)              β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                               β”‚          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚         FHIR Transform & Publish Worker           β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                β”‚
                                                β–Ό
                               Azure Health Data Services
                                    (FHIR R4 Server)
                                                β”‚
                              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                              β–Ό                 β–Ό                    β–Ό
                        Analytics          Clinical Apps         Patient Portal
                     (Power BI, Synapse)  (Web/Mobile)          (SMART on FHIR)

What to Learn Next

  • Observability & Reliability: add structured logging and monitoring to your integration
  • Azure Data Factory: build ETL pipelines for healthcare analytics
  • Security & Compliance: GDPR, data encryption, role-based access for clinical data

Enjoyed this article?

Explore the Integration Engineering learning path for more.

Found this helpful?

Share:𝕏

Leave a comment

Have a question, correction, or just found this helpful? Leave a note below.