Packages

DDD semantics for blunt

Current section

Files

Jump to
blunt_ddd lib blunt bounded_context proxy.ex
Raw

lib/blunt/bounded_context/proxy.ex

defmodule Blunt.Context.Proxy do
@moduledoc false
alias Blunt.Message.{Input, Metadata}
alias Blunt.Context.{Error, Proxy}
def validate!({:command, command_module, _function_name}, context_module) do
error = "#{inspect(command_module)} in #{inspect(context_module)} is not a valid #{inspect(Blunt.Command)}."
do_validate!(command_module, :command, error)
end
def validate!({:query, query_module, _function_name}, context_module) do
error = "#{inspect(query_module)} in #{inspect(context_module)} is not a valid #{inspect(Blunt.Query)}."
do_validate!(query_module, :query, error)
end
defp do_validate!(message_module, type, error) do
case Code.ensure_compiled(message_module) do
{:module, module} ->
unless Metadata.is_message_type?(module, type) do
raise Error, message: error
end
_ ->
raise Error, message: error
end
end
def generate({:command, command_module, proxy_opts}) do
moduledoc = docs(command_module)
{function_name, proxy_opts} = function_name(command_module, proxy_opts)
quote do
@doc unquote(moduledoc)
def unquote(function_name)(values, opts \\ []) do
Proxy.dispatch(unquote(command_module), values, unquote(proxy_opts), opts)
end
end
end
def generate({:query, query_module, proxy_opts}) do
moduledoc = docs(query_module)
{function_name, proxy_opts} = function_name(query_module, proxy_opts)
query_function_name = String.to_atom("#{function_name}_query")
quote do
@doc unquote(moduledoc)
def unquote(function_name)(values \\ [], opts \\ []) do
Proxy.dispatch(unquote(query_module), values, unquote(proxy_opts), opts)
end
@doc "Same as `#{unquote(function_name)}` but returns the query without executing it"
def unquote(query_function_name)(values, opts \\ []) do
Proxy.dispatch(unquote(query_module), values, unquote(proxy_opts), opts, return: :query)
end
end
end
def docs(module) do
if function_exported?(module, :__doc__, 1) do
shortdoc = module.__doc__(:short)
fielddoc = module.__doc__(:field)
optiondoc = module.__doc__(:option)
shortdoc <> fielddoc <> optiondoc
end
end
def function_name(message_module, opts) do
{as, opts} = Keyword.pop(opts, :as)
name =
case as do
nil ->
[name | _] =
message_module
|> Module.split()
|> Enum.reverse()
name
|> to_string
|> Macro.underscore()
|> String.to_atom()
name ->
name
end
{name, opts}
end
def dispatch(message_module, values, proxy_opts, dispatch_opts, internal_opts \\ []) do
{field_values, opts} =
dispatch_opts
|> Keyword.merge(proxy_opts)
|> Keyword.merge(internal_opts)
|> Keyword.put(:dispatched_from, :bounded_context)
|> Keyword.put(:user_supplied_fields, user_supplied_fields(values))
|> Keyword.pop(:field_values, [])
field_values = Enum.into(field_values, %{})
values = Input.normalize(values, message_module)
values
|> Map.merge(field_values)
|> message_module.new()
|> message_module.dispatch(opts)
end
defp user_supplied_fields(list) when is_list(list),
do: Keyword.keys(list)
defp user_supplied_fields(struct) when is_struct(struct),
do: user_supplied_fields(Map.from_struct(struct))
defp user_supplied_fields(map) when is_map(map),
do: Map.keys(map)
end