{"id":20725723,"url":"https://github.com/exponentially/extreme_system","last_synced_at":"2025-09-13T13:29:54.382Z","repository":{"id":62429520,"uuid":"85072654","full_name":"exponentially/extreme_system","owner":"exponentially","description":"Building blocks for distributed elixir based systems","archived":false,"fork":false,"pushed_at":"2021-03-21T01:02:55.000Z","size":109,"stargazers_count":12,"open_issues_count":0,"forks_count":4,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-09-07T03:42:55.024Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/exponentially.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-15T12:59:47.000Z","updated_at":"2023-10-22T05:04:01.000Z","dependencies_parsed_at":"2022-11-01T20:08:37.875Z","dependency_job_id":null,"html_url":"https://github.com/exponentially/extreme_system","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/exponentially/extreme_system","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exponentially%2Fextreme_system","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exponentially%2Fextreme_system/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exponentially%2Fextreme_system/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exponentially%2Fextreme_system/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/exponentially","download_url":"https://codeload.github.com/exponentially/extreme_system/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exponentially%2Fextreme_system/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274969011,"owners_count":25383116,"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-09-13T02:00:10.085Z","response_time":70,"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":[],"created_at":"2024-11-17T04:20:18.782Z","updated_at":"2025-09-13T13:29:54.317Z","avatar_url":"https://github.com/exponentially.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Extreme.System\n\n## Aggregates\n\nAggregates are loaded into memory on demand, meaning once the command is dispatched to aggregate.\n\n1. First, aggregates are created by using `Extreme.System.GenAggregate`. Here is an example of an aggregate:\n\n```elixir\ndefmodule ExtremeSystem.Example.Users.Aggregates.User do\n  use     Extreme.System.GenAggregate\n  alias   ExtremeSystem.Example.Events, as: Event\n  import  Ecto.Changeset\n\n  defmodule State,\n    do: defstruct GenAggregate.state_params ++\n        [:id, :name]\n\n\n  ## Client API\n\n  def start_link(ttl \\\\ 2_000, opts \\\\ []),\n    do: GenAggregate.start_link(__MODULE__, ttl, opts)\n\n  def new(server, id, cmd),\n    do: exec(server, {:new, id, cmd})\n\n  def update_profile(server, cmd),\n    do: exec(server, {:update_profile, cmd})\n\n\n  ## Server Callbacks\n\n  def init(ttl),\n    do: {:ok, struct(State, initial_state(ttl))}\n\n  ### Command handling\n\n  def handle_exec({:new, id, cmd}, from, state) do\n    events = [\n      %Event.User.Created{id: id, email: cmd[\"email\"]},\n      %Event.User.ProfileSet{id: id, name: cmd[\"name\"]}\n    ]\n    {:block, from, {:events, events}, state}\n  end\n\n  def handle_exec({_cmd, %{\"id\" =\u003e id, \"version\" =\u003e expected_version}}, from, %{id: id, version: current_version}=state) when expected_version != current_version,\n    do: {:noblock, from, {:error, :wrong_expected_version, current_version, expected_version}, state}\n\n  def handle_exec({:update_profile, %{\"id\" =\u003e id, \"name\" =\u003e name}}, from, %{id: id, name: name}=state),\n    do: {:noblock, from, {:ok, state.version}, state}\n  def handle_exec({:update_profile, %{\"id\" =\u003e id}=cmd}, from, %{id: id}=state) do\n    case _update_profile(cmd, state) do\n      {:ok, events} -\u003e {:block,   from, {:events, events}, state}\n      other         -\u003e {:noblock, from, other,             state}\n    end\n  end\n\n  def handle_exec({cmd, payload}, from, state),\n    do: {:noblock, from, {:error, :conflict, \"Can't execute #{inspect cmd} with: #{inspect payload}\"}, state}\n\n  defp _update_profile(cmd, state) do\n    case _validate_profile_update(cmd) do\n      %{valid?: false}=changeset -\u003e {:error, changeset}\n      %{changes: data} -\u003e\n        events = [\n          %Event.User.ProfileSet{id: state.id, name: data.name}\n        ]\n        {:ok, events}\n    end\n  end\n\n  defp _validate_profile_update(cmd) do\n    blank = %{}\n    types = %{name: :string}\n    {blank, types}\n      |\u003e cast(cmd, Map.keys(types))\n      |\u003e validate_required([:name])\n      |\u003e validate_length(:name, min: 6)\n  end\n\n  ### Apply events\n\n  defp apply_event(%Event.User.Created{}=event, state) do\n    %State{state|\n      id: event.id\n    }\n  end\n  defp apply_event(%Event.User.ProfileSet{}=event, state) do\n    %State{state|\n      name: event.name\n    }\n  end\n  defp apply_event(_, state), do: state\nend\n```\n\n2. Next, we need to register aggregates with a scheduler, ie. to associate an aggregate with an event stream name. We make use of `Extreme.System.CommandConfiguration`:\n\n```elixir\ndefmodule ExtremeSystem.Example.Users.CommandConfiguration do\n  alias   ExtremeSystem.Example.Users.Aggregates\n  use     Extreme.System.CommandConfiguration, aggregates: [{Aggregates.User,    \"ex_users\"},\n                                                            {Aggregates.Company, \"ex_company\"}]\nend\n```\n\n3. Next we need to create a message handler, that will dispatch commands to appropriate aggregates. For this we use `Extreme.System.MessageHandler`. To send a message to an existing aggregate we use `with_aggregate`, while to send a message to a new aggregate (e.g. creation) we use `with_new_aggregate`. The former assumes there is at least one event persisted in EventStore that is related to your aggregate otherwise you will receive an error:\n\n```elixir\ndefmodule ExtremeSystem.Example.MessageHandlers.Users do\n  alias   ExtremeSystem.Example.Users,   as: App\n  use     Extreme.System.MessageHandler, prefix:        App,\n                                         aggregate_mod: App.Aggregates.User\n  import  ExtremeSystem.Example.MessageHandlers.ResponseHelper, only: [respond_on: 1]\n\n  def new(cmd) do\n    with_new_aggregate(\"Registering new user\", cmd, fn {:ok, pid, id} -\u003e \n      aggregate_mod().new(pid, id, cmd)\n    end) |\u003e respond_on\n  end\n\n  def update_profile(%{\"id\" =\u003e id}=cmd) do\n    with_aggregate(\"Updating profile for user #{inspect id}\", id, fn {:ok, pid} -\u003e\n      aggregate_mod().update_profile(pid, cmd)\n    end) |\u003e respond_on\n  end\nend\n```\n\n4. To route a message to appropriate message handler, we need to use `Extreme.System.Facade`:\n\n```elixir\ndefmodule ExtremeSystem.Example.Users.Facade do\n  use     Extreme.System.Facade, default_cache: 1_000\n\n  alias   ExtremeSystem.Example.MessageHandlers, as: MsgHandler\n\n  route   :new,            MsgHandler.Users\n  route   :update_profile, MsgHandler.Users\nend\n```\n\n5. We can then register this facade globally:\n\n```elixir\ndefmodule ExtremeSystem.Example.Users do\n  alias   ExtremeSystem.Example.Users, as: App\n  alias   Extreme.System,              as: ExSys\n  use     ExSys.Application\n\n  def _start do\n    import Supervisor.Spec, warn: false\n\n    extreme_settings = Application.get_env(:users, :extreme)\n\n    children = [\n      supervisor( ExSys.CommandSup, [App.CommandConfiguration, App, extreme_settings]),\n      supervisor( ExSys.FacadeSup,  [App.Facade, {:global, Example.UsersFacade}]),\n    ]\n\n    {:ok, children: children, name: __MODULE__}\n  end\nend\n```\n\n6. And then call when we need it:\n\n```elixir\nres = :global.whereis_name(facade)\nGenServer.call(res, command, timeout: 2_000)\n```\n\n\n## Installation\n\nIf [available in Hex](https://hex.pm/docs/publish), the package can be installed as:\n\n  1. Add `extreme_system` to your list of dependencies in `mix.exs`:\n\n    ```elixir\n    def deps do\n      [{:extreme_system, \"~\u003e 0.2.14\"}]\n    end\n    ```\n\n## Example\n\nAn example system that uses `Extreme.System` can be found here: https://github.com/exponentially/extreme_system_example\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexponentially%2Fextreme_system","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fexponentially%2Fextreme_system","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexponentially%2Fextreme_system/lists"}