What about access control? That's often directly tied into business logic, but it's so elegant and convenient to do it in the controllers or even the router using the pipelines. In fact a ton of the examples (I think even the Chris McCord Phoenix book) does exactly this, presumably for that reason.
However I did that with an app and haven't been very happy with it because while the bulk can easily be done in the router/controllers, there are things that are totally inappropriate to do there and so it ends up in the context. Then your logic is distributed and messy, which is awful. But putting all access control in the context also bloats and complicates a ton of otherwise very clean, elegant API functions. I try to keep the Repo query building code clean and separate but that often leads to inefficiencies (such as filtering fields in the caller instead of in the generated SQL, which is vastly preferred for obvious reasons).
Personally, here is what I do: if access control is part of the business model, then I put it in the context and, almost always, I raise. Otherwise, continue doing it in plugs. That's it!
For web apps, you should almost never be triggering these invalid access control state, because it means the UI is allowing users to do something they are not really supposed to do. If the user cannot delete a resource, then the button most likely shouldn't show up (or it should be disabled with a comment) etc.
Raising actually helps us find these bad UI paths too because I get an error I can act on.
Now let's say you do want to handle some of these errors. You have three options:
1. Continue raising and rescue in the controller (meh)
2. Continue raising and implement Plug.Exception for said exception so you control how they are shown to the user (great if they are still general exceptions)
3. Return {:ok, _} | {:error, ...} and handle it in the controller (or in the FallbackController for APIs)
What about access control? That's often directly tied into business logic, but it's so elegant and convenient to do it in the controllers or even the router using the pipelines. In fact a ton of the examples (I think even the Chris McCord Phoenix book) does exactly this, presumably for that reason.
However I did that with an app and haven't been very happy with it because while the bulk can easily be done in the router/controllers, there are things that are totally inappropriate to do there and so it ends up in the context. Then your logic is distributed and messy, which is awful. But putting all access control in the context also bloats and complicates a ton of otherwise very clean, elegant API functions. I try to keep the Repo query building code clean and separate but that often leads to inefficiencies (such as filtering fields in the caller instead of in the generated SQL, which is vastly preferred for obvious reasons).
Anyway, interested in thoughts on that.