{"id":13507861,"url":"https://github.com/otobus/event_bus","last_synced_at":"2025-12-11T23:36:11.094Z","repository":{"id":23087543,"uuid":"98097394","full_name":"otobus/event_bus","owner":"otobus","description":":surfer: Traceable, extendable and minimalist **event bus** implementation for Elixir with built-in **event store** and **event watcher** based on ETS.","archived":false,"fork":false,"pushed_at":"2023-04-10T03:53:28.000Z","size":425,"stargazers_count":702,"open_issues_count":7,"forks_count":42,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-12-05T14:25:02.523Z","etag":null,"topics":["elixir","event-sourcing","eventbus","eventstore","instrumentation","message-bus"],"latest_commit_sha":null,"homepage":"https://hexdocs.pm/event_bus","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/otobus.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null},"funding":{"patreon":"mustafaturan"}},"created_at":"2017-07-23T12:47:20.000Z","updated_at":"2025-11-28T20:08:57.000Z","dependencies_parsed_at":"2022-08-07T10:16:42.803Z","dependency_job_id":"c904af2d-f2d0-41a3-b91b-b7cac17e2c28","html_url":"https://github.com/otobus/event_bus","commit_stats":{"total_commits":212,"total_committers":13,"mean_commits":"16.307692307692307","dds":"0.34905660377358494","last_synced_commit":"af047b7549029afd8174cb3b98872ae74c440ea6"},"previous_names":["mustafaturan/event_bus"],"tags_count":31,"template":false,"template_full_name":null,"purl":"pkg:github/otobus/event_bus","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/otobus%2Fevent_bus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/otobus%2Fevent_bus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/otobus%2Fevent_bus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/otobus%2Fevent_bus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/otobus","download_url":"https://codeload.github.com/otobus/event_bus/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/otobus%2Fevent_bus/sbom","scorecard":{"id":714400,"data":{"date":"2025-08-11","repo":{"name":"github.com/otobus/event_bus","commit":"af047b7549029afd8174cb3b98872ae74c440ea6"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.4,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":5,"reason":"Found 3/6 approved changesets -- score normalized to 5","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Info: FSF or OSI recognized license: MIT License: LICENSE.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":8,"reason":"2 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-vq52-99r9-h5pw","Warn: Project is vulnerable to: GHSA-9fm9-hp7p-53mf"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 29 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-22T09:08:07.468Z","repository_id":23087543,"created_at":"2025-08-22T09:08:07.468Z","updated_at":"2025-08-22T09:08:07.468Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27672150,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-11T02:00:11.302Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elixir","event-sourcing","eventbus","eventstore","instrumentation","message-bus"],"created_at":"2024-08-01T02:00:41.373Z","updated_at":"2025-12-11T23:36:11.064Z","avatar_url":"https://github.com/otobus.png","language":"Elixir","funding_links":["https://patreon.com/mustafaturan"],"categories":["Eventhandling","Elixir","Libraries and Frameworks","Libraries"],"sub_categories":["Elixir"],"readme":"# EventBus\n\n[![Build Status](https://travis-ci.org/otobus/event_bus.svg?branch=master)](https://travis-ci.org/otobus/event_bus)\n[![Module Version](https://img.shields.io/hexpm/v/event_bus.svg)](https://hex.pm/packages/event_bus)\n[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/event_bus/)\n[![Total Download](https://img.shields.io/hexpm/dt/event_bus.svg)](https://hex.pm/packages/event_bus)\n[![License](https://img.shields.io/hexpm/l/event_bus.svg)](https://github.com/otobus/event_bus/blob/master/LICENSE)\n[![Last Updated](https://img.shields.io/github/last-commit/otobus/event_bus.svg)](https://github.com/otobus/event_bus/commits/master)\n\nTraceable, extendable and minimalist event bus implementation for Elixir with built-in event store and event watcher based on ETS.\n\n![Event Bus](https://cdn-images-1.medium.com/max/1600/1*0fcfAiHvNeHCRYhp-a32YA.png)\n\n## Table of Contents\n\n[Features](#features)\n\n[Getting Started](#getting-started)\n\n[Installation](#installation)\n\n[Usage](#usage)\n\n- [Register event topics in `config.exs`](#register-event-topics-in-configexs)\n\n- [Register/unregister event topics on demand](#registerunregister-event-topics-on-demand)\n\n- [Subscribe to the 'event bus' with a subscriber and list of given topics](#subscribe-to-the-event-bus-with-a-subscriber-and-list-of-given-topics-notification-manager-will-match-with-regex)\n\n- [Unsubscribe from the 'event bus'](#unsubscribe-from-the-event-bus)\n\n- [List subscribers](#list-subscribers)\n\n- [List subscribers of a specific event](#list-subscribers-of-a-specific-event)\n\n- [Event data structure](#event-data-structure)\n\n- [Define an event struct](#event-data-structure)\n\n- [Notify all subscribers with `EventBus.Model.Event` data](#notify-all-subscribers-with-eventbusmodelevent-data)\n\n- [Fetch an event from the store](#fetch-an-event-from-the-store)\n\n- [Mark as completed on Event Observation Manager](#mark-as-completed-on-event-observation-manager)\n\n- [Mark as skipped on Event Observation Manager](#mark-as-skipped-on-event-observation-manager)\n\n- [Check if a topic exists?](#check-if-a-topic-exists)\n\n- [Use block builder to build `EventBus.Model.Event` struct](#use-block-builder-to-build-eventbusmodelevent-struct)\n\n- [Use block notifier to notify event data to given topic](#use-block-notifier-to-notify-event-data-to-given-topic)\n\n[Sample Subscriber Implementation](#sample-subscriber-implementation)\n\n[Event Storage Details](#event-storage-details)\n\n[Traceability](#traceability)\n\n[EventBus.Metrics and UI](#eventbusmetrics-library)\n\n[Documentation](#documentation)\n\n[Addons](#addons)\n\n[Wiki](https://github.com/otobus/event_bus/wiki)\n\n[Contributing](./CONTRIBUTING.md)\n\n[License](./LICENSE.md)\n\n[Code of Conduct](./CODE_OF_CONDUCT.md)\n\n[Questions](./QUESTIONS.md)\n\n## Features\n\n- Fast data writes with enabled concurrent writes to ETS.\n\n- Fast data reads with enabled concurrent reads from ETS.\n\n- Fast by design. Almost all implementation data accesses have O(1) complexity.\n\n- Memory friendly. Instead of pushing event data, pushes event shadow(event id and topic) to only interested subscribers.\n\n- Applies [queueing theory](https://www.vividcortex.com/resources/queueing-theory) to handle inputs.\n\n- Extendable with addons.\n\n- Traceable with optional attributes. Optional attributes compatible with opentracing platform.\n\n- Minimal with required attributes(In case, you want it work minimal use 3 required attributes to deliver your events).\n\n## Getting Started\n\nStart using `event_bus` library in five basic steps:\n\n- [1: Installing Library Package](https://github.com/otobus/event_bus/wiki/Installing-Library-Package)\n- [2: Creating/Registering Topics](https://github.com/otobus/event_bus/wiki/Creating-(Registering)-Topics)\n- [3: Emitting/Dispatching an Event](https://github.com/otobus/event_bus/wiki/Emitting-(Dispatching)-an-Event)\n- [4: Creating Simple Event Consumers/Listeners/Subscribers](https://github.com/otobus/event_bus/wiki/Creating-Event-Consumers)\n- [5: Subscribing Consumer/Listener/Subscriber for a Topic](https://github.com/otobus/event_bus/wiki/Subscribing-Consumers-to-Topic(s))\n\n## Installation\n\nThe package can be installed by adding `:event_bus` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:event_bus, \"~\u003e 1.7.0\"}\n  ]\nend\n```\n\nBe sure to include `event_bus` in your `mix.exs` Mixfile:\n\n```elixir\ndef application do\n  [\n    applications: [\n      # ...\n      :event_bus\n    ]\n  ]\nend\n```\n\n## Usage\n\n##### Register event topics in `config.exs`\n\n```elixir\nconfig :event_bus, topics: [:message_received, :another_event_occurred]\n```\n\n##### Register/unregister event topics on demand\n```elixir\n# register\nEventBus.register_topic(:webhook_received)\n\u003e :ok\n\n# unregister topic\n# Warning: It also deletes the related topic tables!\nEventBus.unregister_topic(:webhook_received)\n\u003e :ok\n```\n\n##### Subscribe to the 'event bus' with a subscriber and list of given topics, `Notification Manager` will match with Regex\n\n```elixir\n# to catch every event topic\nEventBus.subscribe({MyEventSubscriber, [\".*\"]})\n\u003e :ok\n\n# to catch specific topics\nEventBus.subscribe({MyEventSubscriber, [\"purchase_\", \"booking_confirmed$\", \"flight_passed$\"]})\n\u003e :ok\n\n# if your subscriber has a config\nconfig = %{}\nsubscriber = {MyEventSubscriber, config}\nEventBus.subscribe({subscriber, [\".*\"]})\n\u003e :ok\n```\n\n##### Unsubscribe from the 'event bus'\n```elixir\nEventBus.unsubscribe(MyEventSubscriber)\n\u003e :ok\n\n# if your subscriber has a config\nconfig = %{}\nEventBus.unsubscribe({MyEventSubscriber, config})\n\u003e :ok\n```\n\n##### List subscribers\n```elixir\nEventBus.subscribers()\n\u003e [{MyEventSubscriber, [\".*\"]}, {{AnotherSubscriber, %{}}, [\".*\"]}]\n```\n\n##### List subscribers of a specific event\n```elixir\nEventBus.subscribers(:hello_received)\n\u003e [MyEventSubscriber, {{AnotherSubscriber, %{}}}]\n```\n\n##### Event data structure\n\nData structure for `EventBus.Model.Event`\n\n```elixir\n%EventBus.Model.Event{\n  id: String.t | integer(), # required\n  transaction_id: String.t | integer(), # optional\n  topic: atom(), # required\n  data: any() # required,\n  initialized_at: integer(), # optional, might be seconds, milliseconds or microseconds even nanoseconds since Elixir does not have a limit on integer size\n  occurred_at: integer(), # optional, might be seconds, milliseconds or microseconds even nanoseconds since Elixir does not have a limit on integer size\n  source: String.t, # optional, source of the event, who created it\n  ttl: integer() # optional, might be seconds, milliseconds or microseconds even nanoseconds since Elixir does not have a limit on integer size. If `ttl` field is set, it is recommended to set `occurred_at` field too.\n}\n```\n\n**`transaction_id` attribute**\n\nFirstly, `transaction_id` attribute is an optional field, if you need to store any meta identifier related to event transaction, it is the place to store. Secondly, `transaction_id` is one of the good ways to track events related to the same transaction on a chain of events. If you have time, have a look to the [story](https://hackernoon.com/trace-monitor-chain-of-microservice-logs-in-the-same-transaction-f13420f2d42c).\n\n**`initialized_at` attribute**\n\nOptional, but good to have field for all events to track when the event generator started to process for generating this event.\n\n**`occurred_at` attribute**\n\nOptional, but good to have field for all events to track when the event occurred with unix timestamp value. The library does not automatically set this value since the value depends on the timing choice.\n\n**`ttl` attribute**\n\nOptional, but might to have field for all events to invalidate an event after a certain amount of time. Currently, the `event_bus` library does not do any specific thing using this field. If you need to discard an event in a certain amount of time, that field would be very useful.\n\nNote: If you set this field, then `occurred_at` field is required.\n\n##### Define an event struct\n\n```elixir\nalias EventBus.Model.Event\nevent = %Event{id: \"123\", transaction_id: \"1\",\n  topic: :hello_received, data: %{message: \"Hello\"}}\nanother_event = %Event{id: \"124\", transaction_id: \"1\",\n  topic: :bye_received, data: [user_id: 1, goal: \"exit\"]}\n```\n**Important Note:** It is important to have unique identifier for each event struct per topic. I recommend to use a unique id generator like `{:uuid, \"~\u003e 1.1\"}`.\n\n##### Notify all subscribers with `EventBus.Model.Event` data\n```elixir\nEventBus.notify(event)\n\u003e :ok\nEventBus.notify(another_event)\n\u003e :ok\n```\n\n##### Fetch an event from the store\n```elixir\ntopic = :bye_received\nid = \"124\"\nEventBus.fetch_event({topic, id})\n\u003e %EventBus.Model.Event{data: [user_id: 1, goal: \"exit\"], id: \"124\", topic: :bye_received, transaction_id: \"1\"}\n\n# To fetch only the event data\nEventBus.fetch_event_data({topic, id})\n\u003e [user_id: 1, goal: \"exit\"]\n```\n\n##### Mark as completed on Event Observation Manager\n```elixir\nsubscriber = MyEventSubscriber\n# If your subscriber has config then pass tuple\nsubscriber = {MyEventSubscriber, config}\nEventBus.mark_as_completed({subscriber, {:bye_received, id}})\n\u003e :ok\n```\n\n##### Mark as skipped on Event Observation Manager\n```elixir\nsubscriber = MyEventSubscriber\n# If your subscriber has config then pass tuple\nsubscriber = {MyEventSubscriber, config}\nEventBus.mark_as_skipped({subscriber, {:bye_received, id}})\n\u003e :ok\n```\n\n##### Check if a topic exists?\n```elixir\nEventBus.topic_exist?(:metrics_updated)\n\u003e false\n```\n\n##### Use block builder to build `EventBus.Model.Event` struct\n\nBuilder automatically sets `initialized_at` and `occurred_at` attributes\n```elixir\nuse EventBus.EventSource\n\nid = \"some unique id\"\ntopic = :user_created\ntransaction_id = \"tx\" # optional\nttl = 600_000 # optional\nsource = \"my event creator\" # optional\n\nparams = %{id: id, topic: topic, transaction_id: transaction_id, ttl: ttl, source: source}\nEventSource.build(params) do\n  # do some calc in here\n  Process.sleep(1)\n  # as a result return only the event data\n  %{email: \"jd@example.com\", name: \"John Doe\"}\nend\n\u003e %EventBus.Model.Event{data: %{email: \"jd@example.com\", name: \"John Doe\"},\n id: \"some unique id\", initialized_at: 1515274599140491,\n occurred_at: 1515274599141211, source: \"my event creator\", topic: :user_created, transaction_id: \"tx\", ttl: 600000}\n```\n\nIt is recommended to set optional params in event_bus application config, this will allow you to auto generate majority of optional values without writing code. Here is a sample config for event_bus:\n\n```elixir\nconfig :event_bus,\n  topics: [], # list of atoms\n  ttl: 30_000_000, # integer\n  time_unit: :microsecond, # atom\n  id_generator: EventBus.Util.Base62 # module: must implement 'unique_id/0' function\n```\n\nAfter having such config like above, you can generate events without providing optional attributes like below:\n\n```elixir\n# Without optional params\nparams = %{topic: topic}\nEventSource.build(params) do\n  %{email: \"jd@example.com\", name: \"John Doe\"}\nend\n\u003e %EventBus.Model.Event{data: %{email: \"jd@example.com\", name: \"John Doe\"},\n id: \"Ewk7fL6Erv0vsW6S\", initialized_at: 1515274599140491,\n occurred_at: 1515274599141211, source: \"AutoModuleName\", topic: :user_created,\n transaction_id: nil, ttl: 30_000_000}\n\n# With optional error topic param\nparams = %{id: id, topic: topic, error_topic: :user_create_erred}\nEventSource.build(params) do\n  {:error, %{email: \"Invalid format\"}}\nend\n\u003e %EventBus.Model.Event{data: {:error, %{email: \"Invalid format\"}},\n id: \"some unique id\", initialized_at: 1515274599140491,\n occurred_at: 1515274599141211, source: nil, topic: :user_create_erred,\n transaction_id: nil, ttl: 30_000_000}\n```\n\n##### Use block notifier to notify event data to given topic\n\nBuilder automatically sets `initialized_at` and `occurred_at` attributes\n```elixir\nuse EventBus.EventSource\n\nid = \"some unique id\"\ntopic = :user_created\nerror_topic = :user_create_erred # optional (in case error tuple return in yield execution, it will use :error_topic value as :topic for event creation)\ntransaction_id = \"tx\" # optional\nttl = 600_000 # optional\nsource = \"my event creator\" # optional\nEventBus.register_topic(topic) # in case you didn't register it in `config.exs`\n\nparams = %{id: id, topic: topic, transaction_id: transaction_id, ttl: ttl, source: source, error_topic: error_topic}\nEventSource.notify(params) do\n  # do some calc in here\n  # as a result return only the event data\n  %{email: \"mrsjd@example.com\", name: \"Mrs Jane Doe\"}\nend\n\u003e # it automatically calls notify method with event data and return only event data as response\n\u003e %{email: \"mrsjd@example.com\", name: \"Mrs Jane Doe\"}\n```\n\n### Sample Subscriber Implementation\n\n```elixir\ndefmodule MyEventSubscriber do\n  ...\n\n  # if your subscriber does not have a config\n  def process({topic, id} = event_shadow) do\n    GenServer.cast(__MODULE__, event_shadow)\n    :ok\n  end\n\n  ...\n\n  # if your subscriber has a config\n  def process({config, topic, id} = event_shadow_with_conf) do\n    GenServer.cast(__MODULE__, event_shadow_with_conf)\n    :ok\n  end\n\n  ...\n\n\n  # if your subscriber does not have a config\n  def handle_cast({:bye_received, id} = event_shadow, state) do\n    event = EventBus.fetch_event(event_shadow)\n    # do sth with event\n\n    # update the watcher!\n    # version \u003e= 1.4.0\n    EventBus.mark_as_completed({__MODULE__, event_shadow})\n    # all versions\n    EventBus.mark_as_completed({__MODULE__, :bye_received, id})\n    ...\n    {:noreply, state}\n  end\n\n  def handle_cast({:hello_received, id} = event_shadow, state) do\n    event = EventBus.fetch_event({:hello_received, id})\n    # do sth with EventBus.Model.Event\n\n    # update the watcher!\n    # version \u003e= 1.4.0\n    EventBus.mark_as_completed({__MODULE__, event_shadow})\n    # all versions\n    EventBus.mark_as_completed({__MODULE__, :hello_received, id})\n    ...\n    {:noreply, state}\n  end\n\n  def handle_cast({topic, id} = event_shadow, state) do\n    # version \u003e= 1.4.0\n    EventBus.mark_as_skipped({__MODULE__, event_shadow})\n\n    # all versions\n    EventBus.mark_as_skipped({__MODULE__, topic, id})\n    {:noreply, state}\n  end\n\n  ...\n\n  # if your subscriber has a config\n  def handle_cast({config, :bye_received, id}, state) do\n    event = EventBus.fetch_event({:bye_received, id})\n    # do sth with event\n\n    # update the watcher!\n    subscriber = {__MODULE__, config}\n    EventBus.mark_as_completed({subscriber, :bye_received, id})\n    ...\n    {:noreply, state}\n  end\n\n  def handle_cast({config, :hello_received, id}, state) do\n    event = EventBus.fetch_event({:hello_received, id})\n    # do sth with EventBus.Model.Event\n\n    # update the watcher!\n    subscriber = {__MODULE__, config}\n    EventBus.mark_as_completed({subscriber, :hello_received, id})\n    ...\n    {:noreply, state}\n  end\n\n  def handle_cast({config, topic, id}, state) do\n    subscriber = {__MODULE__, config}\n    EventBus.mark_as_skipped({subscriber, topic, id})\n    {:noreply, state}\n  end\n\n  ...\nend\n```\n\n## Event Storage Details\n\nWhen an event configured in `config` file, 2 ETS tables will be created for the event on app start.\n\nAll event data is temporarily saved to the ETS tables with the name `:eb_es_\u003c\u003ctopic\u003e\u003e` until all subscribers processed the data. This table is a read heavy table. When a subscriber needs to process the event data, it queries this table to fetch event data.\n\nTo watch event status, a separate watcher table is created for each event type with the name `:eb_ew_\u003c\u003ctopic\u003e\u003e`. This table is used for keeping the status of the event. `Observation Manager` updates this table frequently with the notification of the event subscribers.\n\nWhen all subscribers process the event data, data in the event store and watcher, automatically deleted by the `Observation Manager`. If you need to see the status of unprocessed events, event watcher table is one of the good places to query.\n\nFor example; to get the list unprocessed events for `:hello_received` event:\n\n```elixir\n# The following command will return a list of tuples with the `id`, and `event_subscribers_list` where `subscribers` is the list of event subscribers, `completers` is the subscribers those processed the event and notified `Observation Manager`, and lastly `skippers` is the subscribers those skipped the event without processing.\n\n# Assume you have an event with the name ':hello_received'\n:ets.tab2list(:eb_ew_hello_received)\n\u003e [{id, {subscribers, completers, skippers}}, ...]\n```\n\nETS storage SHOULD NOT be considered as a persistent storage. If you need to store events to a persistent data store, then subscribe to all event types by a module with `[\".*\"]` event topic then save every event data.\n\nFor example;\n\n```elixir\nEventBus.subscribe({MyDataStore, [\".*\"]})\n\n# then in your data store save the event\ndefmodule MyDataStore do\n  ...\n\n  def process({topic, id} = event_shadow) do\n    GenServer.cast(__MODULE__, event_shadow)\n    :ok\n  end\n\n  ...\n\n  def handle_cast({topic, id}, state) do\n    event = EventBus.fetch_event({topic, id})\n    # write your logic to save event_data to a persistent store\n\n    EventBus.mark_as_completed({__MODULE__, {topic, id}})\n    {:noreply, state}\n  end\nend\n```\n\n## Traceability\n\nEventBus comes with a good enough data structure to track the event life cycle with its optional parameters. For a traceable system, it is highly recommend to fill optional fields on event data. It is also encouraged to use `EventSource.notify` block/yield to automatically set the `initialized_at` and `occurred_at` values.\n\n### System Events\n\nThis feature removed with the version 1.3 to keep the core library simple. If you need to trace system events please check the sample wrapper implementation from the [wiki page](https://github.com/otobus/event_bus/wiki/Tracing-System-Events).\n\n### EventBus.Metrics Library\n\nEventBus has some addons to extend its optional functionalities. One of them is `event_bus_metrics` library which comes with a UI, RESTful endpoints and SSE streams to provide instant metrics for event_bus topics.\n\n[EventBus.Metrics Instructions](https://github.com/otobus/event_bus/wiki/EventBus-Metrics-and-UI)\n\n## Documentation\n\n- [Wiki](https://github.com/otobus/event_bus/wiki)\n\n- [Module docs](https://hexdocs.pm/event_bus)\n\n- [The story](https://medium.com/@mustafaturan/event-bus-implementation-s-d2854a9fafd5)\n\n## Addons\n\nA few sample addons listed below. Please do not hesitate to add your own addon to the list.\n\n| Addon Name           | Description   | Link          | Docs          |\n| -------------------- | ------------- | ------------- | ------------- |\n| `event_bus_postgres` | Fast event consumer to persist `event_bus` events to Postgres using GenStage          | [Github](https://github.com/otobus/event_bus_postgres) | [HexDocs](https://hexdocs.pm/event_bus_postgres) |\n| `event_bus_logger`   | Deadly simple log subscriber implementation                                             | [Github](https://github.com/otobus/event_bus_logger)   | [HexDocs](https://hexdocs.pm/event_bus_logger)   |\n| `event_bus_metrics`  | Metrics UI and metrics API endpoints for EventBus events for debugging and monitoring | [Hex](https://hex.pm/packages/event_bus_metrics)       | [HexDocs](https://hexdocs.pm/event_bus_metrics)  |\n\nNote: The addons under [https://github.com/otobus](https://github.com/otobus) organization implemented as a sample, but feel free to use them in your project with respecting their licenses.\n\n## Copyright and License\n\nMIT\n\nCopyright (c) 2022 Mustafa Turan\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fotobus%2Fevent_bus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fotobus%2Fevent_bus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fotobus%2Fevent_bus/lists"}