goncalotomas
I’m working on a Phoenix LiveView app that uses boundary, and we’ve bumped into warnings writing out a way to do notifications. There are a couple of tricks that would remove these warnings, but I care about the right way to model data and to organize the code. ![]()
Here’s a rough example of the code I’m working with:
defmodule MyApp.Notifications do
@moduledoc """
The Notifications module is responsible for managing user notifications - both in-app and email.
"""
use Boundary, top_level?: true
# importing verified routes
use MyAppWeb, :verified_routes
def send_invitation(invitation) do
to =
if invitation.user_id do
url(~p"/app/users/invitations")
else
url(~p"/auth/register"))
end
MyApp.Mailer.deliver_invitation_email(invitation, to)
send_invitation_notification(invitation)
# ...
:ok
end
defp send_notification(invitation) do
# ...
end
end
The real code is a bit more complicated, but hopefully this illustrates the issue.
We’ve configured Boundary to reject any calls to from the MyApp context to the MyAppWeb context, which makes this module not pass the boundary checks:
warning: forbidden reference to MyAppWeb
(references from MyApp.Notifications to MyAppWeb are not allowed)
lib/my_app/notifications.ex:17
It makes sense to prevent access to the Web context from the non-Web, but in this case we’re, were just trying to use verified routes so that the links for the invitations are verified to exist. Previously we could use Phoenix.Router.Helpers, but from the docs, it looks like the routes module helper is deprecated, so no help there.
Moving the logic that switches between the possible URLs inside the Mailer doesn’t seem helpful, since it looks like we’re leaking the logic around Invitations when Mailer should just care about sending email.
We’ve also toyed with a few ideas that basically revolve around replacing the Route Helpers module, but quickly ran into the thought of “there’s got to be a better way to do this”.
Can someone help us organize our code a bit better?
Thanks! ![]()
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #blog-post
- #elixirconf-us
- #elixir-ls
- #ai
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 6- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
LostKobrakai
You can move what
use MyAppWeb, :verified_routesdoes to a separate module, which you can independently scope with boundary.You could also reverse the dependency by injecting the module for url generation e.g. using config. Works around Boundary a bit, but imo is the architectually cleaner approach.
goncalotomas
I tried doing that, but it doesn’t change the output. Here’s how I added it:
The issue is that if I put this module under
MyAppWeb, Boundary knows you’re still referencing the Web layer:And I get this compile warning:
If I try to put
VerifiedRoutesout of the Web boundary, i.e.MyApp.VerifiedRoutes, I get these compile errors instead:I don’t understand exactly why it fails to import the same code that works for
MyAppWeb, so I couldn’t do it that way either.The dependency inversion technique is something that I had heard of but never actually used. For now I’m seeing it as the only viable approach.
Thank you!
LostKobrakai
You could exclude the module just like it was previously done for the router helper module.
Not sure what this issue about the
urlfunction is on a quick glance.al2o3cr
I’ve used the “module in config” approach to solve this exact issue in an umbrella app.
It has a couple components:
MyAppWebthat exposes a URL-generation APIMyAppthat looks up a module and then calls functions on itMyAppto the module fromMyAppWebOne downside of this simple approach is that the dynamic
applyis very hard for most tools to “see through”, so you probably won’t get compile-time warnings if something’s incorrect (callingfor_invitationwith the wrong arity, etc)You can mitigate most of that with some additional boilerplate-y code:
Application.fetch_env!part and the call into a separate module inMyApp@behaviourthat it implementsMyAppWeb.NotificationUrlsgtcode
Saša’s book helped to get me started long ago. So, this is clearly a solid tool built by an expert.
I’m in the middle of restructuring a large codebase, and
boundarycame up. My layout is monorepo (imaginelib→ {foo,bar,baz}), currently via manually ensuring a directed dependency graph. The original inclination for such manual enforcement was simply to use grep to look for any circular refs, but I like the idea ofboundaryenforcing this programmatically.The lib looks well-vetted. Has anyone had luck using this for larger codebases? Any feedback, tips?
goncalotomas
There was a new talk from Saša recently published that talks about the process of both incorporating
boundaryinto a large new codebase and also working towards removing projects from umbrella format:I found it a great talk, and there’s no better source than the author about this library