Files
docs/EVENT_DRIVEN_MIGRATION_GUIDE.md
2026-02-09 21:51:46 -08:00

4.7 KiB

Event-Driven Architecture Migration Guide

Date: 2025-01-27 Purpose: Guide for migrating projects to event-driven architecture Status: Complete


Overview

This guide provides instructions for migrating projects to use the shared event bus (NATS) for event-driven communication.


Prerequisites

  • NATS event bus deployed
  • Access to event bus
  • Understanding of event-driven patterns

Migration Steps

Step 1: Install NATS Client

pnpm add nats

Step 2: Create Event Publisher

import { connect, NatsConnection } from 'nats';

class EventPublisher {
  private nc: NatsConnection | null = null;

  async connect() {
    this.nc = await connect({
      servers: process.env.NATS_URL || 'nats://nats:4222',
    });
  }

  async publish(subject: string, data: unknown) {
    if (!this.nc) {
      await this.connect();
    }
    await this.nc!.publish(subject, JSON.stringify(data));
  }

  async close() {
    await this.nc?.close();
  }
}

Step 3: Create Event Subscriber

import { connect, NatsConnection } from 'nats';

class EventSubscriber {
  private nc: NatsConnection | null = null;

  async connect() {
    this.nc = await connect({
      servers: process.env.NATS_URL || 'nats://nats:4222',
    });
  }

  async subscribe(subject: string, handler: (data: unknown) => void) {
    if (!this.nc) {
      await this.connect();
    }
    const sub = this.nc!.subscribe(subject);
    for await (const msg of sub) {
      const data = JSON.parse(msg.data.toString());
      handler(data);
    }
  }

  async close() {
    await this.nc?.close();
  }
}

Step 4: Define Event Schemas

// events/user-events.ts
export interface UserCreatedEvent {
  type: 'user.created';
  userId: string;
  email: string;
  timestamp: Date;
}

export interface UserUpdatedEvent {
  type: 'user.updated';
  userId: string;
  changes: Record<string, unknown>;
  timestamp: Date;
}

Step 5: Publish Events

import { EventPublisher } from './event-publisher';
import { UserCreatedEvent } from './events/user-events';

const publisher = new EventPublisher();

async function createUser(userData: UserData) {
  // Create user logic
  const user = await createUserInDB(userData);

  // Publish event
  const event: UserCreatedEvent = {
    type: 'user.created',
    userId: user.id,
    email: user.email,
    timestamp: new Date(),
  };
  await publisher.publish('events.user.created', event);
}

Step 6: Subscribe to Events

import { EventSubscriber } from './event-subscriber';
import { UserCreatedEvent } from './events/user-events';

const subscriber = new EventSubscriber();

async function setupEventHandlers() {
  await subscriber.subscribe('events.user.created', async (data: UserCreatedEvent) => {
    // Handle user created event
    await sendWelcomeEmail(data.email);
    await createUserProfile(data.userId);
  });
}

Best Practices

Event Naming

  • Use consistent naming: events.{domain}.{action}
  • Examples: events.user.created, events.order.placed

Event Schema

  • Define schemas using TypeScript interfaces
  • Include type, timestamp, and relevant data
  • Version events for compatibility

Error Handling

  • Implement retry logic
  • Use dead letter queues
  • Log all events

Monitoring

  • Track event rates
  • Monitor latency
  • Set up alerts

Testing

Unit Tests

import { describe, it, expect } from 'vitest';
import { EventPublisher } from './event-publisher';

describe('EventPublisher', () => {
  it('should publish events', async () => {
    const publisher = new EventPublisher();
    await publisher.publish('test.event', { data: 'test' });
    // Verify event was published
  });
});

Integration Tests

import { describe, it, expect } from 'vitest';
import { EventPublisher, EventSubscriber } from './events';

describe('Event Integration', () => {
  it('should publish and receive events', async () => {
    const publisher = new EventPublisher();
    const subscriber = new EventSubscriber();

    const received: unknown[] = [];
    await subscriber.subscribe('test.event', (data) => {
      received.push(data);
    });

    await publisher.publish('test.event', { data: 'test' });
    await new Promise(resolve => setTimeout(resolve, 100));

    expect(received).toHaveLength(1);
  });
});

Migration Checklist

  • Install NATS client
  • Create event publisher
  • Create event subscriber
  • Define event schemas
  • Update code to publish events
  • Update code to subscribe to events
  • Test event publishing
  • Test event subscription
  • Set up monitoring
  • Update documentation

Last Updated: 2025-01-27