Skip to content

Instantly share code, notes, and snippets.

@yordis
Forked from slashdotdash/README.md
Created January 26, 2022 16:24
Show Gist options
  • Save yordis/768dbe85087211db5427b1c7062ef949 to your computer and use it in GitHub Desktop.
Save yordis/768dbe85087211db5427b1c7062ef949 to your computer and use it in GitHub Desktop.
Commanded middleware to enrich commands during dispatch, such as calling an external API.

Commanded middleware for command enrichment

Usage

Add the EnrichCommand middleware to your command router:

defmodule MyApp.Router do
  use Commanded.Commands.Router
  
  middleware Middleware.EnrichCommand
end

Implement the CommandEnrichment protocol for any command which needs external data, such as an external API or read model query.

defimpl CommandEnrichment, for: ExampleCommand do
  @doc """
  Enrich command during dispatch, but before aggregate handling.
  """
  def enrich(%ExampleCommand{} = command) do
    %ExampleCommand{id: id} = command

    command = %ExampleCommand{
      command
      | external_data: lookup_external_data(id)
    }

    {:ok, command}
  end
  
  defp lookup_external_data(id) do
    # .. fetch data
  end
end

The enrich/1 function should return {:ok, command} on success, or {:error, error} on failure.

defprotocol CommandEnrichment do
@doc """
Enrich a command with additional data during dispatch, before passing to aggregate.
As an example, this is an extension point where additional data could be retreived
from the database to enrich the command's fields.
"""
@fallback_to_any true
def enrich(command)
end
defimpl CommandEnrichment, for: Any do
@doc """
By default the command is not modified.
"""
def enrich(command), do: {:ok, command}
end
defmodule Middleware.EnrichCommand do
@behaviour Commanded.Middleware
alias Commanded.Middleware.Pipeline
@doc """
Enrich the command via the opt-in command enrichment protocol.
"""
def before_dispatch(%Pipeline{command: command} = pipeline) do
case CommandEnrichment.enrich(command) do
{:ok, command} ->
%Pipeline{pipeline | command: command}
{:error, _error} = reply ->
pipeline
|> Pipeline.respond(reply)
|> Pipeline.halt()
end
end
def after_dispatch(pipeline), do: pipeline
def after_failure(pipeline), do: pipeline
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment