All Articles
HealthcareHealthcare EngineeringHIPAA

Engineering a Care Coordination Platform: Architecture, EMR Integration, and Real-Time Alerting

Care coordination software sits at the intersection of HL7 messaging, HIPAA compliance, real-time clinical alerting, and complex state machines for patient workflows. This is what the architecture looks like when you build it for production.

Gaurang Ghinaiya
Gaurang Ghinaiya

Founder & CEO

April 30, 2026
8 min read
Engineering a Care Coordination Platform: Architecture, EMR Integration, and Real-Time Alerting

Care coordination platforms do one deceptively simple thing: make sure the right clinician knows about the right patient at the right time. In practice, this means processing real-time events from multiple EMR systems, maintaining patient state across care episodes, routing alerts to the appropriate care team members, and doing all of this in a way that is HIPAA-compliant and reliable enough that clinicians trust it with actual patient care.

This post walks through the architecture of a care coordination platform — the kind of system we built for CareCoordinations to manage post-acute care workflows, which involved integrating with HCHB (home health EMR), managing patient state across care episodes, and delivering real-time alerts to care coordinators and field clinicians.

The domain model

Care coordination has a specific domain vocabulary that needs to be reflected in the data model:

  • Patient: The person receiving care. Has demographics, insurance, diagnoses, and care history.
  • Care episode: A bounded period of care with a specific goal (post-surgical recovery, chronic disease management, hospice). A patient may have multiple sequential or overlapping episodes.
  • Care team: The set of clinicians assigned to a patient during an episode. Includes a care coordinator (typically a nurse or social worker), a primary physician, and various specialists and field clinicians.
  • Care plan: The structured set of interventions, visit schedules, and goals for a care episode.
  • Clinical event: Something that happens to or about a patient — a visit, a lab result, a vital sign, a medication change, a hospitalization, a discharge.
  • Alert: A notification generated by a clinical event that requires action from a care team member.
  • Task: A specific action item assigned to a care team member, with a due date and completion tracking.

EMR integration architecture

Care coordination platforms must integrate with the EMR systems that record clinical activity. For home health care coordination, the primary integration is with a home health EMR — HCHB in the system we built. This integration is HL7 v2, not FHIR, because HCHB's real-time event notification capability is built on HL7 v2 ADT messaging.

The MLLP listener

We run a dedicated MLLP listener service that receives HL7 v2 messages from HCHB on a persistent TCP connection. The listener handles the low-level MLLP framing and acknowledgment protocol, then publishes normalized events to an internal queue.

// MLLP server — handles the raw TCP framing
const server = net.createServer((socket) => {
  let buffer = Buffer.alloc(0)

  socket.on('data', (data) => {
    buffer = Buffer.concat([buffer, data])

    while (true) {
      const startIdx = buffer.indexOf(0x0B)  // MLLP start block
      const endIdx = buffer.indexOf(Buffer.from([0x1C, 0x0D]))  // MLLP end block

      if (startIdx === -1 || endIdx === -1) break

      const messageBytes = buffer.slice(startIdx + 1, endIdx)
      const messageText = messageBytes.toString('utf-8')

      // Send ACK
      socket.write(Buffer.from([0x0B]))
      socket.write(buildAck(messageText, 'AA'))
      socket.write(Buffer.from([0x1C, 0x0D]))

      // Publish to queue for processing
      queue.add('process-hl7-message', { raw: messageText })

      buffer = buffer.slice(endIdx + 2)
    }
  })
})

server.listen(HL7_PORT, () => {
  logger.info({ port: HL7_PORT }, 'MLLP listener started')
})

HL7 v2 message processing

The queue worker parses each HL7 v2 message and routes it based on message type and trigger event:

queue.process('process-hl7-message', async (job) => {
  const segments = parseHL7v2(job.data.raw)
  const msh = extractMSH(segments)
  const messageType = \`\${msh.messageType}^{msh.triggerEvent}\`

  // Log raw message for audit trail
  await db.hl7MessageLog.create({
    data: {
      messageId: msh.messageControlId,
      messageType,
      sendingFacility: msh.sendingFacility,
      receivedAt: new Date(),
      rawMessage: job.data.raw,
    }
  })

  switch (messageType) {
    case 'ADT^A01':  // Admit
      return handleAdmit(segments)
    case 'ADT^A03':  // Discharge
      return handleDischarge(segments)
    case 'ADT^A08':  // Update patient info
      return handlePatientUpdate(segments)
    case 'ADT^A11':  // Cancel admit
      return handleCancelAdmit(segments)
    case 'ADT^A13':  // Cancel discharge
      return handleCancelDischarge(segments)
    case 'ORU^R01':  // Observation result (lab, vitals)
      return handleObservationResult(segments)
    default:
      logger.warn({ messageType }, 'Unhandled HL7 message type')
  }
})

Patient matching

The most technically challenging part of the EMR integration: mapping between the EMR's patient identifiers and the care coordination platform's patient records. EMRs use Medical Record Numbers (MRN) that are internal to the EMR system. The care coordination platform has its own patient IDs. The mapping must handle:

  • New patients — first time the EMR sends a message for this patient
  • Returning patients — patient was previously seen, needs to match to existing record
  • Merged patients — EMR has merged two patient records, one of which may exist in our system
  • Demographic updates — patient's name, DOB, or address has changed in the EMR
async function matchOrCreatePatient(pid: HL7PID): Promise {
  // 1. Try exact MRN match from this EMR facility
  const byMrn = await db.patientIdentifiers.findFirst({
    where: { identifierType: 'MRN', identifierValue: pid.patientId, facility: pid.facilityId },
  })
  if (byMrn) return byMrn.patientId

  // 2. Try demographic fuzzy match (name + DOB + last 4 SSN if present)
  const candidates = await findByDemographics(pid)
  if (candidates.length === 1) {
    // Confident single match — link the MRN
    await db.patientIdentifiers.create({
      data: { patientId: candidates[0].id, identifierType: 'MRN', identifierValue: pid.patientId, facility: pid.facilityId }
    })
    return candidates[0].id
  }

  if (candidates.length > 1) {
    // Multiple matches — flag for manual review
    await flagForPatientMatchReview(pid, candidates)
    return createStagingPatient(pid)  // create a staging record pending review
  }

  // 3. No match — create new patient
  const patient = await db.patients.create({ data: mapPIDtoPatient(pid) })
  await db.patientIdentifiers.create({
    data: { patientId: patient.id, identifierType: 'MRN', identifierValue: pid.patientId, facility: pid.facilityId }
  })
  return patient.id
}

 

The alert engine

Clinical alerting is the core value proposition of a care coordination platform. The alert engine evaluates clinical events against a set of alert rules and determines who needs to know, how urgently, and through what channel.

Alert rules

interface AlertRule {
  id: string
  name: string
  trigger: (event: ClinicalEvent, patient: Patient) => boolean
  severity: 'critical' | 'high' | 'medium' | 'low'
  recipients: (patient: Patient, careTeam: CareTeamMember[]) => CareTeamMember[]
  channels: AlertChannel[]
  escalationDelayMinutes: number  // if not acknowledged, escalate after this many minutes
  message: (event: ClinicalEvent, patient: Patient) => string
}

const ALERT_RULES: AlertRule[] = [
  {
    id: 'readmission-risk',
    name: 'Readmission within 30 days of discharge',
    trigger: (event, patient) =>
      event.type === 'ADT^A01' &&
      patient.lastDischargeDate &&
      daysDiff(patient.lastDischargeDate, event.occurredAt) <= 30,
    severity: 'critical',
    recipients: (patient, careTeam) =>
      careTeam.filter(m => m.role === 'care-coordinator' || m.role === 'primary-physician'),
    channels: ['push', 'sms', 'in-app'],
    escalationDelayMinutes: 15,
    message: (event, patient) =>
      \`READMISSION: \${patient.firstName} \${patient.lastName} admitted to \${event.facility} — \${daysDiff(patient.lastDischargeDate!, event.occurredAt)} days post-discharge\`,
  },
  // ... more rules
]

Alert delivery and escalation

async function processAlert(rule: AlertRule, event: ClinicalEvent, patient: Patient): Promise {
  const careTeam = await getCareTeam(patient.id)
  const recipients = rule.recipients(patient, careTeam)
  const message = rule.message(event, patient)

  const alert = await db.alerts.create({
    data: {
      patientId: patient.id,
      ruleId: rule.id,
      severity: rule.severity,
      message,
      status: 'pending',
      generatedAt: new Date(),
    }
  })

  // Deliver to each recipient
  for (const recipient of recipients) {
    await deliverAlert(alert, recipient, rule.channels)
  }

  // Schedule escalation if not acknowledged
  await queue.add(
    'check-alert-acknowledgment',
    { alertId: alert.id },
    { delay: rule.escalationDelayMinutes * 60 * 1000 }
  )
}

queue.process('check-alert-acknowledgment', async (job) => {
  const alert = await db.alerts.findUnique({ where: { id: job.data.alertId } })
  if (!alert || alert.status === 'acknowledged') return

  // Escalate — route to next level
  const escalationRule = ESCALATION_RULES[alert.ruleId]
  if (escalationRule) {
    await escalateAlert(alert, escalationRule)
  }
})

Real-time updates with WebSockets

Care coordinators monitor patient dashboards that need to update in real time as clinical events arrive. A coordinator watching 40 patients should see a new alert appear on the dashboard within seconds of the HL7 message arriving — not on the next page refresh.

We use Server-Sent Events (SSE) for the care coordinator dashboard real-time feed. SSE is simpler than WebSockets for one-directional server-to-client push, and the care coordinator interface is read-mostly with writes happening through explicit user actions.

// SSE endpoint — one connection per care coordinator
export async function GET(req: Request) {
  const coordinatorId = await getAuthenticatedCoordinator(req)
  if (!coordinatorId) return new Response('Unauthorized', { status: 401 })

  const stream = new ReadableStream({
    start(controller) {
      const subscription = eventBus.subscribe(coordinatorId, (event) => {
        controller.enqueue(\`data: \${JSON.stringify(event)}\n\n\`)
      })

      req.signal.addEventListener('abort', () => {
        subscription.unsubscribe()
        controller.close()
      })
    }
  })

  return new Response(stream, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive',
    }
  })
}

HIPAA-specific implementation details

Every piece of the care coordination platform operates under HIPAA's technical safeguards requirements. In practice for this specific system:

  • All HL7 v2 messages are logged with the raw content for audit purposes, retained for 6 years
  • Every alert delivery is logged with recipient, channel, timestamp, and whether it was acknowledged
  • Patient records are only visible to care team members assigned to that patient — the system enforces this at the API layer, not just the UI layer
  • All ePHI in transit is encrypted (TLS 1.3), all ePHI at rest is encrypted (AES-256 via RDS)
  • Session tokens expire after 15 minutes of inactivity
  • The HL7 message log is append-only and stored in a separate S3 bucket from operational data

Care coordination software at the level of reliability that clinical workflows require is genuinely complex to build. If you are planning a care coordination platform or a home health management system and want to understand the architecture before you start building, we have done this work and can help you get the foundation right.

Related service

Healthcare Software Development

HIPAA-compliant platforms, EMR integration, and care coordination tools for US home health agencies.

Learn more

Written by

Gaurang Ghinaiya
Gaurang Ghinaiya

Founder & CEO

Gaurang Ghinaiya is the Founder & CEO of Nexios Technologies. He is passionate about building innovative software solutions that drive business growth. With years of experience in technology leadership, he guides teams toward excellence.

Let's talk

Have a project in mind?

Tell us about your project below, or pick another way to reach us. Average response time: under 4 business hours.