Still struggling a bit with patterns for context boundaries and hoping for some advice.
I frequently have code like this in my Absinthe resolvers for mutations (although the pattern would be exactly the same if it were a standard REST controller):
def password_reset(_, params, _) do
with {:l1, {:ok, _recaptcha_response}} <- {:l1, Recaptcha.verify(params.recaptcha)},
{:l2, {:ok, username}} <- {:l2, Accounts.username_or_email_to_username(params.username_or_email)},
{:l3, %User{} = user} <- {:l3, Accounts.get_user(username)},
{:l4, {:ok, reset_token}} <- {:l4, Accounts.create_password_reset(username)} do
Email.send_password_reset_email(user.email, reset_token)
{:ok, %{errors: []}}
else
{:l1, _error} ->
{:error, "Recaptcha Error"}
# All other else clauses go here
end
Of course this works great but I’ve this nagging feeling that most of this logic should exist in Accounts.create_password_reset/1
and only checking the recaptcha should stay in the resolver/controller since Recaptcha is a different app/dep and a web layer concern. It seems to make sense that the resolver/controller be the glue that calls different contexts to keep the contexts from being coupled to each other but calling Accounts 3 times here is wrong.
Maybe the resolver/controller should be like this:
def password_reset(_, params, _) do
with {:l1, {:ok, _recaptcha_response}} <- {:l1, Recaptcha.verify(params.recaptcha)},
{:l2, {:ok, {email, reset_token}}} <- {:l2, Accounts.create_password_reset(params.username_or_email)} do
Email.send_password_reset_email(email, reset_token)
{:ok, %{errors: []}}
# ... else clauses go here
Since this resolver is the only consumer of this context API I can foresee does it even matter or is this all just personal preference? Or am I missing something? Thanks!