Skip to content

Event-Driven Architecture

Overview

VitalBridge uses an event-driven architecture to coordinate business operations across independently deployed microservices.

Instead of directly modifying data owned by other services, services communicate through domain events published to Apache Kafka.

This architecture provides:

  • Loose coupling
  • Independent deployments
  • Fault isolation
  • Horizontal scalability
  • Eventual consistency
  • Reliable event delivery

Why Event-Driven Architecture?

Traditional distributed systems often rely on synchronous service-to-service communication.

flowchart LR

    A["Service A"]

    B["Service B"]

    C["Service C"]

    A --> B

    B --> C
Hold "Alt" / "Option" to enable pan & zoom

This approach creates:

  • Tight coupling
  • Cascading failures
  • Deployment dependencies
  • Increased latency

VitalBridge instead uses asynchronous events.

flowchart LR

    A["Service A"]

    K["Apache Kafka"]

    B["Service B"]

    C["Service C"]

    A -->|"Publish Event"| K

    K -->|"Consume Event"| B

    K -->|"Consume Event"| C
Hold "Alt" / "Option" to enable pan & zoom

Services remain independent while still reacting to business events.


Core Principle

A service owns its own data.

flowchart LR

    DOC["Doctor Service"]

    DOC_DB[("Doctor DB")]

    APPT["Appointment Service"]

    APPT_DB[("Appointment DB")]

    DOC --> DOC_DB

    APPT --> APPT_DB

    DOC_DB -.-x APPT_DB
Hold "Alt" / "Option" to enable pan & zoom

No service may:

  • Write to another service's database
  • Share tables
  • Share repositories
  • Share schemas

Cross-service communication must occur through events.


Event Flow

A typical event flow looks like this:

flowchart LR

    CMD["Business Command"]

    SERVICE["Owning Service"]

    OUTBOX["Outbox Event"]

    KAFKA["Apache Kafka"]

    CONSUMER["Consumer Service"]

    CMD --> SERVICE

    SERVICE --> OUTBOX

    OUTBOX --> KAFKA

    KAFKA --> CONSUMER
Hold "Alt" / "Option" to enable pan & zoom

The producer service publishes an event.

One or more consumer services react independently.


Example: Tenant Creation

When a Super Administrator creates a tenant:

flowchart LR

    SA["Super Administrator"]

    TR["Tenant Registry Service"]

    K["Apache Kafka"]

    ADM["Admin Service"]

    ID["Identity Service"]

    SA -->|"Create Tenant"| TR

    TR -->|"Tenant Created Event"| K

    K --> ADM

    K --> ID
Hold "Alt" / "Option" to enable pan & zoom

The Tenant Registry Service remains unaware of how downstream services process the event.


Example: Appointment Booking

flowchart LR

    PAT["Patient"]

    APPT["Appointment Service"]

    K["Apache Kafka"]

    SCH["Doctor Schedule Service"]

    COMM["Communication Engine"]

    AUDIT["Audit Log Service"]

    PAT --> APPT

    APPT -->|"Appointment Confirmed"| K

    K --> SCH

    K --> COMM

    K --> AUDIT
Hold "Alt" / "Option" to enable pan & zoom

A single business event may trigger multiple workflows.


Transactional Outbox Pattern

VitalBridge uses the Transactional Outbox pattern for reliable event delivery.

The Problem

Publishing directly to Kafka inside a database transaction can lead to inconsistencies.

flowchart LR

    DB["Database Commit"]

    K["Kafka Publish"]

    DB --> K
Hold "Alt" / "Option" to enable pan & zoom

If Kafka fails after the database commit succeeds:

  • Data is stored
  • Event is lost

This creates inconsistent state.


The Solution

VitalBridge writes business data and event records atomically.

flowchart LR

    CMD["Command"]

    TX["Database Transaction"]

    DATA["Business Data"]

    OUTBOX["Outbox Event"]

    CMD --> TX

    TX --> DATA

    TX --> OUTBOX
Hold "Alt" / "Option" to enable pan & zoom

Both records are committed together.


Event Publication

A background publisher processes outbox records.

flowchart LR

    OUTBOX["Outbox Table"]

    PUBLISHER["Outbox Publisher"]

    KAFKA["Apache Kafka"]

    OUTBOX --> PUBLISHER

    PUBLISHER --> KAFKA
Hold "Alt" / "Option" to enable pan & zoom

This guarantees:

  • No lost events
  • Reliable delivery
  • Retry safety

CQRS Communication Model

VitalBridge distinguishes between commands and queries.

Commands

Commands change state.

flowchart LR

    CLIENT["Client"]

    SERVICE["Service"]

    K["Kafka Event"]

    CLIENT -->|"Command"| SERVICE

    SERVICE -->|"Domain Event"| K
Hold "Alt" / "Option" to enable pan & zoom

Examples:

  • Create Tenant
  • Add Provider
  • Add Patient
  • Create Appointment
  • Cancel Appointment

Queries

Queries read data.

flowchart LR

    CLIENT["Client"]

    SERVICE["Query Service"]

    CLIENT -->|"Read Request"| SERVICE
Hold "Alt" / "Option" to enable pan & zoom

Queries never modify state.

Examples:

  • List Providers
  • List Patients
  • Get Availability
  • Get Appointments

Event Consumers

Consumers must be idempotent.

flowchart LR

    K["Kafka Event"]

    CONSUMER["Consumer"]

    DUP["Duplicate Event"]

    K --> CONSUMER

    DUP --> CONSUMER
Hold "Alt" / "Option" to enable pan & zoom

Consumers must safely process duplicate deliveries.

This protects the platform from:

  • Retries
  • Rebalances
  • Network failures

Eventual Consistency

Event-driven systems do not provide immediate consistency.

flowchart LR

    SERVICE_A["Service A"]

    K["Kafka"]

    SERVICE_B["Service B"]

    SERVICE_A --> K

    K --> SERVICE_B
Hold "Alt" / "Option" to enable pan & zoom

There is a small delay between:

  1. Event publication
  2. Event consumption

This behavior is expected and intentional.


Kafka as the Event Backbone

Apache Kafka acts as the central event backbone.

flowchart TB

    TR["Tenant Registry"]

    DOC["Doctor"]

    PAT["Patient"]

    SCH["Schedule"]

    APPT["Appointment"]

    VIDEO["Video"]

    K["Apache Kafka"]

    TR --> K
    DOC --> K
    PAT --> K
    SCH --> K
    APPT --> K
    VIDEO --> K
Hold "Alt" / "Option" to enable pan & zoom

Kafka enables:

  • Service independence
  • Reliable delivery
  • Replay capabilities
  • Horizontal scalability

Architectural Rules

VitalBridge enforces the following event-driven principles:

Allowed

  • Services publish domain events
  • Services consume domain events
  • Queries use read-only communication
  • Outbox pattern for event publication

Forbidden

  • Shared databases
  • Cross-service writes
  • Distributed transactions
  • Direct database access between services
  • State-changing Kafka RPC

These rules ensure the platform remains scalable, resilient, and maintainable as additional services are introduced.