Simple Phoenix umbrella app - how to create URLs from domain side?

I’m considering breaking up a (growing) web app into an umbrella app, and following the instructions in the Programming Phoenix 1.4 book. But I’m having an issue trying to handle a need for URL generation from the “domain” app, unrelated to the web app.

I have events (or even simple timers) that trigger messages (e-mails or Channel broadcasts), and I would like those messages to include URLs to specific resources. Receiving and triggering events based on business events seems like it falls on the domain app side of things… however, the domain app has no access the Phoenix Router.Helpers.

Have other folks found a clean way to work around this? Would I have to create a separate “message generation” application just to handle this case, and is that even possible without introducing a circular dependency?

2 Likes

@jamesvl Did you ever figure this out? I’m struggling with a similar issue. I have multiple seperate ddd domains in an umbrella app, one in particular sends emails out of band of user interaction from the interface layer(phoenix). Question is how do I create URLs when emails are sent without explicit interaction with/from the web layer?

An approach I’ve used with good results is to put the URL-generation parts into a module in the web side that implements a behaviour from the domain side. For instance:

defmodule DomainSide.UrlGenerator do
  @callback signup_url(some_params)
end

defmodule DomainSide.Urls do
  @behaviour DomainSide.UrlGenerator

  @impl true
  def signup_url(some_params) do
    generator().signup_url(some_params)
  end

  defp generator do
    Application.get_env(:domain_side, :url_generator)
  end
end

defmodule WebSide.UrlGenerator do
  @behaviour DomainSide.UrlGenerator

  @impl true
  def signup_url(some_params) do
    # actually call the router here
  end
end

# in umbrella config
config :domain_side, :url_generator, WebSide.UrlGenerator

This breaks what would otherwise be a compile-time cyclic dependency by moving part of it to umbrella config.

1 Like

@al2o3cr This solution worked quite well. Thanks!