06-event-driven

command module
v0.0.0-...-a21a33c Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 14, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

README

Example 06: Event-Driven Architecture

Demonstrates event-driven patterns with NCore's event bus, pub/sub system, and async event handling from the extension system.

Features

  • Event Bus: Central event distribution
  • Pub/Sub Pattern: Publish events, subscribe to topics
  • Async Processing: Non-blocking event handling
  • Event Types: System events, domain events, integration events
  • Event Persistence: Optional event sourcing
  • Event Replay: Replay events for debugging

Architecture

┌─────────────┐                    ┌──────────────┐
│  Publisher  │───── Event ───────►│  Event Bus   │
└─────────────┘                    └──────┬───────┘
                                          │
                            ┌─────────────┼─────────────┐
                            │             │             │
                            ▼             ▼             ▼
                      ┌──────────┐  ┌──────────┐  ┌──────────┐
                      │ Handler  │  │ Handler  │  │ Handler  │
                      │    A     │  │    B     │  │    C     │
                      └──────────┘  └──────────┘  └──────────┘

Event Types

1. Domain Events
// User registered event
type UserRegisteredEvent struct {
    UserID    string
    Email     string
    Timestamp time.Time
}
2. System Events
// Extension loaded event
type ExtensionLoadedEvent struct {
    Name      string
    Version   string
    Timestamp time.Time
}
3. Integration Events
// External service notification
type PaymentReceivedEvent struct {
    OrderID   string
    Amount    float64
    Currency  string
    Timestamp time.Time
}

Project Structure

06-event-driven/
├── data/
│   ├── data.go          # SQLite connection
│   └── repository/
│       └── user.go      # User repository
├── event/
│   ├── bus.go           # Event bus implementation
│   └── store_sqlite.go  # SQLite event store
├── service/
│   ├── handlers.go      # Notification/analytics/audit services
│   └── user.go          # Publishes events
└── handler/
│   └── handler.go       # HTTP handlers
├── main.go
├── config.yaml
└── README.md

Publishing Events

// In user service
func (s *UserService) CreateUser(ctx context.Context, req *CreateUserReq) (*User, error) {
    user, err := s.repo.Create(ctx, req)
    if err != nil {
        return nil, err
    }

    // Publish event
    s.eventBus.Publish("user.registered", &UserRegisteredEvent{
        UserID:    user.ID,
        Email:     user.Email,
        Timestamp: time.Now(),
    })

    return user, nil
}

Subscribing to Events

// In notification service
func (s *NotificationService) Init() {
    s.eventBus.Subscribe("user.registered", s.handleUserRegistered)
    s.eventBus.Subscribe("order.placed", s.handleOrderPlaced)
}

func (s *NotificationService) handleUserRegistered(event *UserRegisteredEvent) error {
    return s.sendWelcomeEmail(event.Email)
}

Event Bus Implementation

type EventBus struct {
    handlers map[string][]EventHandler
    mu       sync.RWMutex
}

func (b *EventBus) Publish(topic string, event interface{}) {
    b.mu.RLock()
    handlers := b.handlers[topic]
    b.mu.RUnlock()

    for _, handler := range handlers {
        go handler(event) // Async execution
    }
}

func (b *EventBus) Subscribe(topic string, handler EventHandler) {
    b.mu.Lock()
    defer b.mu.Unlock()

    b.handlers[topic] = append(b.handlers[topic], handler)
}

Event Persistence

type EventStore interface {
    Save(event *Event) error
    Load(eventID string) (*Event, error)
    LoadByAggregate(aggregateID string) ([]*Event, error)
    Replay(from time.Time) error
}

// Example: Save to database
func (s *PostgresEventStore) Save(event *Event) error {
    _, err := s.db.Exec(`
        INSERT INTO events (id, type, aggregate_id, payload, timestamp)
        VALUES ($1, $2, $3, $4, $5)
    `, event.ID, event.Type, event.AggregateID, event.Payload, event.Timestamp)
    return err
}

Event Patterns

1. Fan-Out Pattern
User Created Event
    ├── Send welcome email
    ├── Create user profile
    ├── Log analytics
    └── Update cache
2. Saga Pattern
Order Placed
    ├── Reserve inventory → Success/Failure
    ├── Process payment → Success/Failure
    └── Send confirmation → Success/Failure
3. CQRS Pattern
Command: CreateUser
    └── Event: UserCreated
        ├── Write Model (Update database)
        └── Read Model (Update search index)

API Examples

Publish Event
curl -X POST http://localhost:8080/events \
  -d '{
    "type": "user.registered",
    "data": {
      "user_id": "123",
      "email": "user@example.com"
    }
  }'
Query Events
# Get all events for aggregate
curl http://localhost:8080/events?aggregate_id=user-123

# Get events by type
curl http://localhost:8080/events?type=user.registered

# Get events since timestamp
curl http://localhost:8080/events?since=2024-01-01T00:00:00Z

Event Handlers

Email Handler
func (h *EmailHandler) Handle(event Event) error {
    switch e := event.(type) {
    case *UserRegisteredEvent:
        return h.sendWelcomeEmail(e.Email)
    case *PasswordResetEvent:
        return h.sendPasswordResetEmail(e.Email, e.Token)
    }
    return nil
}
Analytics Handler
func (h *AnalyticsHandler) Handle(event Event) error {
    return h.tracker.Track(event.Type, map[string]interface{}{
        "timestamp": event.Timestamp,
        "data":      event.Data,
    })
}
event:
  bus:
    buffer_size: 1000
    workers: 10
  store:
    enabled: true
    driver: postgres
    retention_days: 90
  handlers:
    async: true
    timeout: 30s
    retry_count: 3

Monitoring

# Event bus stats
curl http://localhost:8080/events/stats

# Response:
{
  "published": 15234,
  "processed": 15180,
  "failed": 54,
  "subscribers": {
    "user.registered": 3,
    "order.placed": 5
  }
}

Testing

func TestUserService_CreateUser(t *testing.T) {
    bus := event.NewMockBus()
    svc := NewUserService(repo, bus)

    user, err := svc.CreateUser(ctx, req)
    assert.NoError(t, err)

    // Verify event published
    assert.Equal(t, 1, bus.PublishedCount("user.registered"))
}

Use Cases

  • User registration workflows
  • Order processing pipelines
  • Notification systems
  • Audit logging
  • Analytics tracking
  • Cache invalidation
  • Microservice communication

Next Steps

License

This example is part of the NCore project.

Documentation

Overview

Package main boots the event-driven example.

Directories

Path Synopsis
Package data manages SQLite persistence for event-driven services.
Package data manages SQLite persistence for event-driven services.
repository
Package repository stores users for the event-driven example.
Package repository stores users for the event-driven example.
Package event implements the in-process event bus.
Package event implements the in-process event bus.
Package handler exposes HTTP endpoints for event-driven workflows.
Package handler exposes HTTP endpoints for event-driven workflows.
Package service contains application services for event-driven flows.
Package service contains application services for event-driven flows.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL