The Unified Event Space

In Happen, events flow seamlessly across process, network, and environment boundaries through a unified NATS backbone. This approach eliminates the artificial boundaries typically found in distributed systems, enabling you to build applications that span multiple processes and environments with the same simplicity as local ones.

Beyond Boundaries

Happen's unified event space means you write code the same way regardless of where nodes are deployed:

// The exact same code works whether nodes are:
// - In the same process
// - In different processes on the same machine
// - Distributed across different machines
// - In completely different environments (server, browser, edge)
orderNode.send(inventoryNode, {
  type: "check-inventory",
  payload: { orderId: "123", items: [...] }
});

This isn't just an API convenience—it's a fundamental design principle that creates location transparency throughout your system. Nodes can be deployed wherever makes the most sense for your architecture, and your code remains unchanged.

How the Unified Event Space Works

At the heart of Happen's unified event space is the NATS messaging system with its JetStream persistence layer. When an event crosses boundaries:

  1. The event is automatically serialized using MessagePack

  2. NATS delivers it to the appropriate destination

  3. The event is deserialized on arrival

  4. The receiving node processes it using standard event handlers

All of this happens transparently—your code simply uses send() and on() without needing to know whether communication is local or remote.

Cross-Boundary Serialization

When events cross process or network boundaries in Happen, they are efficiently handled through a streamlined serialization strategy:

JSON Interface, Binary Transport

Happen provides a natural JSON-like interface for developers while using efficient binary serialization under the hood:

  • Developer Experience: Work with standard JavaScript objects in your code

  • Transport Efficiency: MessagePack binary format used for actual transmission

  • Transparent Conversion: Serialization/deserialization happens automatically

  • No Schema Required: Maintains Happen's schema-free flexibility

This approach gives you the best of both worlds - the simplicity of working with native JavaScript objects and the efficiency of binary transport.

Preserving Causality Across Boundaries

One of the most powerful aspects of Happen's unified event space is how it maintains causality across boundaries. Every event carries its complete causal context:

// When an event crosses a boundary, its context is preserved
{
  type: "inventory-reserved",
  payload: {
    orderId: "order-123",
    items: [/* ... */]
  },
  context: {
    causal: {
      id: "evt-789", // Unique event ID
      sender: "inventory-node", // Originating node
      causationId: "evt-456", // Event that caused this one
      correlationId: "txn-123", // Transaction this belongs to
      path: [ // Complete journey
        ["user-node", "order-node"],
        ["order-node", "inventory-node"]
      ]
    }
  }
}

This causal context creates a complete web of relationships that spans your entire distributed system.

Unified Configuration

Happen provides a clean, explicit approach to configuration that separates environment concerns from application logic:

// Initialize Happen with NATS as the universal backbone
const happen = initializeHappen({
  // NATS configuration
  nats: {
    // Connection configuration
    connection: {
      // Server environment (direct NATS)
      server: {
        servers: ['nats://server:4222'],
        jetstream: true
      },
      // Browser environment (WebSocket)
      browser: {
        servers: ['wss://server:8443'],
        jetstream: true
      }
    },
    
    // Enable key features
    capabilities: {
      // Persistence through JetStream
      persistence: {
        enabled: true,
        // Use Key-Value store for state
        keyValue: {
          enabled: true,
          buckets: {
            state: "happen-state",
            temporal: "happen-temporal"
          }
        }
      },
      
      // Delivery guarantees
      delivery: {
        exactlyOnce: true,
        deduplication: true
      }
    }
  }
});

Node-Level Configuration

Individual nodes can specify their own delivery requirements:

// Node with specific reliability requirements
const paymentNode = createNode("payment-service", {
  // Delivery guarantees for this specific node
  delivery: {
    exactlyOnce: true,
    acknowledge: true,
    timeout: 5000, // ms
    retries: 3
  }
});

Large Payload Management

For handling large data across boundaries, Happen leverages NATS Key-Value store to provide a global namespace:

The Global Namespace

// Store large data
const key = `data:${generateId()}`;
node.global.set(key, largeData);

// Reference in events
node.send(targetNode, {
  type: "process-data",
  payload: { dataKey: key }
});

// Retrieve on the other side
const data = await targetNode.global.get(event.payload.dataKey);

Handling Network Realities

Real-world networks are unreliable. Happen's NATS-based transport layer addresses these realities directly:

Persistent Delivery

Events can be stored in JetStream's durable storage until successfully processed, ensuring:

  • Durability: Events survive process restarts

  • Guaranteed Delivery: Events reach their destination even after network issues

  • Natural Backpressure: JetStream provides backpressure for overloaded consumers

Network Partitions

During network partitions:

  • Nodes continue operating within their connected segments

  • Events destined for disconnected nodes are stored in JetStream

  • When connectivity resumes, stored events are delivered

  • Causality is preserved throughout this process

This happens automatically through JetStream's durable storage capabilities.

Cross-Environment Communication

The unified transport system makes cross-environment communication seamless:

// Server-side node sending to browser client
serverNode.on("update-client", (event) => {
  // Use the standard send method - routing happens automatically
  serverNode.send(browserNode, {
    type: "data-updated",
    payload: event.payload.data
  });
});

Benefits of the Unified Event Space

This approach to distributed events creates powerful capabilities:

  1. Location Transparency: Your code doesn't need to know or care where nodes are deployed

  2. Protocol Flexibility: The right protocol is selected automatically for each environment

  3. Environment Adaptability: Seamlessly bridge between server, browser, and edge environments

  4. Deployment Freedom: Components can move between environments by simply changing deployment configuration

  5. Resilient Architecture: Systems naturally handle network fluctuations through JetStream

  6. Future Extensibility: Easily adopt new NATS capabilities as they become available

By providing a truly unified event space powered by NATS, Happen enables you to focus on your application's domain rather than the complexities of distributed systems.

Last updated