Definition and Overview

An event is a record of something that happened at a specific point in time within a software system. Events are immutable facts -- once an event occurs, it cannot be changed or undone, only compensated for by subsequent events. In event-driven architectures, events serve as the primary mechanism for communication between loosely coupled components, replacing direct method calls with asynchronous message publication that decouples producers from consumers.

Events differ from commands and queries in their temporal semantics and coupling characteristics. A command is an instruction to do something (imperative, future-tense), a query is a request for information (interrogative, present-tense), and an event is a notification that something happened (declarative, past-tense). This distinction is fundamental to CQRS (Command Query Responsibility Segregation) and event sourcing patterns, where the complete system state can be reconstructed by replaying the ordered sequence of events from the beginning of time.

In the BEAM ecosystem, events are naturally represented as messages between processes. The actor model treats message passing as the fundamental communication primitive, making event-driven architecture the default rather than an imposed pattern. Phoenix PubSub, Telemetry, and GenServer callback mechanisms all operate on event semantics, providing multiple abstraction levels for different use cases.

Technical Deep Dive

Event Classification

CategoryDescriptionExamplesLifetime
Domain EventsBusiness-meaningful state changesOrderPlaced, UserRegistered, ScanCompletedPermanent (event store)
Integration EventsCross-service communicationEntityUpdated, ReportGeneratedTransient to persistent
System EventsInfrastructure-level signalsProcessStarted, NodeConnected, MemoryThresholdTransient (telemetry)
UI EventsUser interaction signalsButtonClicked, FormSubmitted, PageViewedTransient (LiveView)

Event Schema Properties

Every well-formed event contains a standard set of metadata fields alongside domain-specific payload:

FieldTypeRequiredDescription
event_idUUIDYesUnique identifier for deduplication
event_typestringYesFully qualified event name
occurred_atDateTimeYesWhen the event happened (source clock)
recorded_atDateTimeYesWhen the event was persisted (system clock)
sourcestringYesProducing component identifier
correlation_idUUIDNoLinks related events across components
causation_idUUIDNoLinks to the event/command that caused this event
payloadmapYesDomain-specific event data
metadatamapNoAdditional context (user, session, trace)

Event Delivery Guarantees

GuaranteeDescriptionUse CaseImplementation
At-most-onceEvent may be lost, never duplicatedTelemetry, metricsPhoenix PubSub default
At-least-onceEvent delivered 1+ times, may duplicateCritical business eventsOutbox pattern + retry
Exactly-onceEvent delivered precisely onceFinancial transactionsIdempotent consumers + dedup

Architecture and Implementation

Event-driven architecture in the Prismatic Platform operates across three layers. The local layer uses BEAM process messages and GenServer callbacks for intra-node communication. The cluster layer uses Phoenix PubSub (backed by Phoenix.PubSub.PG2) for inter-node event distribution. The persistent layer uses PostgreSQL-backed event logs for durable event storage and replay.

The publish-subscribe pattern decouples event producers from consumers. A producer publishes events to a named topic without knowledge of which (or how many) consumers are subscribed. Consumers subscribe to topics of interest and receive events asynchronously. This pattern enables adding new event consumers without modifying producers, supporting extensibility and separation of concerns.

Event correlation is achieved through correlation and causation identifiers. The correlation ID groups all events related to a single user action or business transaction. The causation ID creates a causal chain showing which event triggered which subsequent event. Together, these fields enable distributed tracing and debugging across the entire event flow.

Usage in Prismatic Platform

The Prismatic Platform uses events pervasively for inter-component communication, real-time UI updates, telemetry collection, and audit logging.

defmodule Prismatic.Event do
  @moduledoc """
  Base event structure used across the platform.
  Provides consistent schema, serialization, and
  metadata for all domain and integration events.
  """

  @type t :: %__MODULE__{
    event_id: String.t(),
    event_type: String.t(),
    occurred_at: DateTime.t(),
    source: String.t(),
    correlation_id: String.t() | nil,
    causation_id: String.t() | nil,
    payload: map(),
    metadata: map()
  }

  defstruct [
    :event_id,
    :event_type,
    :occurred_at,
    :source,
    :correlation_id,
    :causation_id,
    payload: %{},
    metadata: %{}
  ]

  @spec new(String.t(), String.t(), map(), keyword()) :: t()
  def new(event_type, source, payload, opts \\ []) do
    %__MODULE__{
      event_id: Ecto.UUID.generate(),
      event_type: event_type,
      occurred_at: DateTime.utc_now(),
      source: source,
      correlation_id: Keyword.get(opts, :correlation_id),
      causation_id: Keyword.get(opts, :causation_id),
      payload: payload,
      metadata: Keyword.get(opts, :metadata, %{})
    }
  end
end

defmodule Prismatic.EventBus do
  @moduledoc """
  Central event publication and subscription hub.
  Wraps Phoenix.PubSub with structured event semantics
  and telemetry integration.
  """

  @pubsub Prismatic.PubSub

  @spec publish(String.t(), Prismatic.Event.t()) :: :ok
  def publish(topic, %Prismatic.Event{} = event) do
    :telemetry.execute(
      [:prismatic, :event, :published],
      %{count: 1},
      %{topic: topic, event_type: event.event_type, source: event.source}
    )

    Phoenix.PubSub.broadcast(@pubsub, topic, {:event, event})
  end

  @spec subscribe(String.t()) :: :ok | {:error, term()}
  def subscribe(topic) do
    Phoenix.PubSub.subscribe(@pubsub, topic)
  end

  @spec unsubscribe(String.t()) :: :ok
  def unsubscribe(topic) do
    Phoenix.PubSub.unsubscribe(@pubsub, topic)
  end
end

The DD pipeline publishes events on the "dd:pipeline" topic for real-time LiveView dashboard updates. The OSINT toolbox publishes scan progress and completion events. The agent system emits lifecycle events for monitoring and orchestration. All events flow through the Telemetry pipeline for metrics collection and the event log for audit compliance.

Cross-References

  • Event Log -- Immutable event persistence
  • Message Passing -- BEAM communication primitive
  • PubSub -- Publish-subscribe event distribution
  • Telemetry -- Metric collection from events
  • Immutable Log -- Append-only event storage
  • Livebooks: api_integration/ notebooks demonstrate event-driven patterns
  • Academy: Topics on distributed systems cover event architecture

See Also


Connect & Contribute

Created by Tomas Korcak (korczis) | Open Source under GHL