【问题标题】:How to structure a Phoenix umbrella framework for many applications如何为许多应用构建一个凤凰伞框架
【发布时间】:2017-09-15 10:24:48
【问题描述】:

我正在使用 phx 1.3 和伞形应用程序为一套新产品构建架构。

我有一个现有的基于 Phoenix 的企业级 WebRTC 软电话(许多按键、一个显示器、多个输入和输出音频设备选择等)。 我用 Phoenix 开发了一个 Slack 克隆消息应用程序原型。 这两个应用程序都相当大 我需要将电话与聊天应用程序集成到一个前端,该前端可能只是电话,只是聊天客户端,或者两者兼而有之。 我需要为聊天客户端添加许多新功能 我还希望架构支持使用相同的客户端在呼叫服务器(基于用户)上提供额外的设置,并可能提供大量管理员级别的设置。 将来我可能还会添加其他应用程序,例如操作员面板、日志查看器,并且列表还在继续…… 客户端JS非常简单,没有前端框架。我在服务器端渲染模板并将 html 推送到频道。

我想构建这个可插拔的。相同的端点和数据库。一种常见的用户体验。

我认为将有两个通用应用程序,一个用于 Phoenix 端点和几个控制器,另一个用于主 Repo 和几个模式。我试图弄清楚为每个应用程序使用两个或更多附加应用程序会有多困难。一个用于上下文和模式,另一个用于控制器、视图、模板和早午餐资源。可能是另一个用于 3rd 方 API。

为了完成这项工作,我需要为每个应用程序中的路由器动态调度。处理每个应用程序中包含的迁移的方法,可能还有更多我还没有想到的方法。

有人试过吗?有没有类似结构的开源项目?

【问题讨论】:

    标签: elixir phoenix-framework


    【解决方案1】:

    我日常使用的 elixir 应用程序是一个包含 13 个应用程序的保护伞。

    根是 Phoenix 端点和顶级路由器,它将请求转发到其他应用程序中定义的路由器。

    这意味着应用不会被拆分为层(网络/业务/数据),而是拆分为垂直域切片。

    随着应用程序在过去 12 个月中显着增长,这已经很好地扩展了。

    我遇到的最大问题是 Phoenix 路由器在转发到其他路由器时会从请求中剥离前导路径,因此我们创建了一个 mount 宏与 Plug 路由器一起使用,它保持请求路径不变:

    defmodule MyApp.Router do
      @moduledoc """
      Top level routing to each of the sub-systems
      """
    
      use Plug.Router
    
      plug :match
      plug :dispatch
    
      mount "/events/*_", Events.Router
      mount "/report/*_", Report.Router
      mount "/stats/*_",  Stats.Router
      mount "/auth/*_",   Auth.Router
    end
    

    并安装:

    defmacro mount(path, router, opts \\ []) do
      quote do
        @opts unquote(router).init(unquote(opts))
        match unquote(path), do: unquote(router).call(var!(conn), @opts)
      end
    end
    

    为了简单起见,我们在单个应用中管理整个数据库的迁移,但 Ecto Schema 是在每个应用中单独声明的。

    Here 是一个展示一些概念的项目。

    【讨论】:

    • 谢谢!我喜欢你的mount 方法。我过去有这个问题。没有想到你的做法。但是,我尝试使用此 `Application.get_env(:app, :routers) |> Enum.map(&(forward "/", &1))` 快速飙升。我把它放在我的主路由器的末尾,它似乎工作。你能想到这种方法有什么问题吗?
    • vertical domain slices 听起来不错。我已经看到将持久性/数据库提取到它自己的应用程序apps/db/ 中的引用。您对此有何看法?
    • 我看到你提到了We manage the migrations of the entire database in a single app just for simplicity。这种架构有什么问题吗?
    • 到目前为止,db 应用程序运行良好。它反映了多个伞式应用程序共享单个数据库的架构,并使用多个 postgresql 模式进行逻辑分离。共享应用只需要声明Repo 模块和迁移脚本。其他每个伞形应用程序都可以免费管理自己的 Ecto.Schema 模块和查询构建器。
    • 嗨@MikeBuhot 我喜欢你的解决方案。但我想知道有没有办法在我具有以下结构的应用程序之间共享通道:apps --proxy_app ----endpoint.ex ----router_plug.ex(Web.Plugs.Router) ----user_socket。 ex ----channels ------room_channel.ex --web_app_1 ----router.ex --web_app_2 ----router.ex -- proxy_app 包括 web_app_1 和 web_app_2。有时我可能需要从 web_app_1 和 web_app_2 广播事件。有没有办法我可以做到这一点?提前致谢
    【解决方案2】:

    我还在开发一个相当大的伞形应用程序。而不是维护一个配置了每个应用程序入口点的文件,我想看看我是否可以让它更加动态。于是我写了一个这样的插件:

    def call(%{path_info: [path|_]} = conn, opts) do
      path = path |> String.downcase() |> Macro.camelize()
      module =
        try do
          Module.safe_concat [LocationRouting, path, Location]
        rescue
          _ -> nil
        end
      if module do
        module.call(conn, opts)
      else
        conn
      end
    end
    

    我将该插件添加到端点。然后取决于被调用的模块,该模块再次能够使用Plug.Builder添加路由器或类似于端点的东西

    您可以在此处查看完整示例:https://github.com/tverlaan/location_routing

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-01
      • 2019-10-17
      • 2016-05-08
      • 1970-01-01
      • 2022-01-24
      • 1970-01-01
      • 2017-06-05
      • 1970-01-01
      相关资源
      最近更新 更多