I’m not following how you think Phoenix Contexts increases coupling. It seems like they make sense because they decrease coupling. That inherently involves abstraction. list_users abstracts the Ecto API (which is itself already an abstraction over the SQL or whatever you’re using as a Repo) and this abstraction has made your list_users function a bit complex.
In a Rails app you’d probably use a new Class to handle the complexity, in Elixir I would start by using arity and pattern matching to keep the logic separated within the context, rather than using a single function. If necessary you could add another module to your API context.
def list_users, do: User |> list_users
def list_users(%{filters: filters}), do: filters |> build_filtered_query |> list_users # this could also go in the API context so this always works on a query
def list_users(query), do: query |> Repo.all
If you had to do a lot of that for a lot of schemas I would probably consider a macro.


















