Heh, I’ve thought about it, but it breaks protocols in the way I envision them being used.
Commandex currently lets you write a theoretical phoenix JSON API like this:
def register_user(conn, params) do
params
|> RegisterUser.run()
|> Respondable.to_json(conn)
end
def login_user(conn, params) do
params
|> LoginUser.run()
|> Respondable.to_json(conn)
end
Where the protocol implementation might look something like:
defimpl YourApp.Respondable, for: YourApp.RegisterUser do
import Phoenix.Controller
def to_json(%{success: true, data: %{user: user}}, conn) do
json(conn, %{token: Auth.sign_token(user), user: user})
end
def to_json(%{success: false, error: %{user: :already_exists}, conn) do
conn
|> put_status(:bad_request)
|> json(%{error: "Email is already taken."})
end
end
defimpl YourApp.Respondable, for: YourApp.LoginUser do
import Phoenix.Controller
def to_json(%{success: true, data: %{user: user}}, conn) do
json(conn, %{token: Auth.sign_token(user), user: user})
end
def to_json(%{success: false, error: %{login: :unauthorized}, conn) do
conn
|> put_status(:unauthorized)
|> json(%{error: "Invalid password given."})
end
end
The trick is having a consistent naming structure while maintaining the flexibility of different kinds of business logic in protocols. Both of those protocol implementations could call out to a shared Response.successful_auth_response(conn, user) function. But they certainly don’t have to, and more importantly, you aren’t sticking all of this logic in the controller.


















