大模型应用
需要基于 Elixir/BEAM 构建实时、容错、少客户端…
适用于用 Phoenix + LiveView on BEAM 开发实时、可监督重启、低客户端 JS 的 Web 应用,并覆盖 Ecto/Contexts、PubSub/Presence、测试与发布。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
需要基于 Elixir/BEAM 构建实特定
解决思路
- 围绕 OTP supervision 让 web、data、background 进程隔离且可重启;用 Contexts 编码领域边界;让 LiveView 在服务端渲染 HTML 并通过 WebSockets 同步 UI;用 PubSub + Presence 做扇出、跟踪与协作。
- 用 asdf 安装 Erlang 27.0 和 Elixir 1.17.3,并执行 `asdf global erlang 27.0 elixir 1.17.3`。
- 执行 `mix archive.install hex phx_new` 安装 Phoenix 生成器。
- 执行 `mix phx.new my_app --live` 创建带 LiveView、Ecto、esbuild 的项目;随后执行 `cd my_app`、`mix deps.get`、`mix ecto.create`、`mix phx.server`,访问 http://localhost:4000。
- 按关键布局组织项目:`lib/my_app/application.ex` 放 OTP supervision tree(Repo、Endpoint、Telemetry、PubSub、Oban 等);`lib/my_app_web/endpoint.ex` 放 Endpoint、plugs、sockets、LiveView 配置;`lib/my_app_web/router.ex` 放 Pipelines、scopes、routes、LiveSessions;`lib/my_app/` 放 Contexts 和 Ecto schemas;`test/support/{conn_case,data_case}.ex` 放 Ecto + Phoenix 测试助手。
- 在 `application.ex` 中保持 supervision tree 简短、子进程隔离;children 可包含 `MyApp.Repo`、`{Phoenix.PubSub, name: MyApp.PubSub}`、`MyAppWeb.Endpoint`、`{Oban, Application.fetch_env!(:my_app, Oban)}`、`MyApp.Metrics`;用 `Supervisor.start_link(children, strategy: :one_for_one, name: MyApp.Supervisor)`。
- 用 GenServer 模式包装有状态服务:实现 `start_link/2`、`GenServer.call` 和 `handle_call`,在 `handle_call(:inc, _from, state)` 中计算新状态并返回 `{:reply, new_state, new_state}`。
- 遵循 BEAM 原则:优先大量小进程;用明确重启策略监督一切;用消息传递(`GenServer.cast`/`send`)避免共享状态;用 ETS/Cachex 做内存缓存并保持被监督。
- 在 `router.ex` 中分离 browser/api 关注点:`pipeline :browser` 依次 plug `:accepts` html、`:fetch_session`、`:fetch_live_flash`、`:protect_from_forgery`、`:put_secure_browser_headers`、`:fetch_current_user`;`pipeline :api` plug `:accepts` json;在 scope 中用 `pipe_through`、`live`、`resources` 路由。
- 把 Plugs 当作可组合请求中间件:保持 plug 纯粹、短小;跨切面逻辑优先用 pipeline plugs 而不是 controller plugs。
- 定义 Ecto schema 和 changeset:在 `schema "users"` 中放 `email`、`hashed_password`、`confirmed_at`、`timestamps()`;`registration_changeset/2` 依次 `cast`、`validate_required`、`validate_format(:email, ~r/@/)`、`validate_length(:password, min: 12)`、`unique_constraint(:email)`、`put_password_hash`;用 `Argon2.hash_pwd_salt` 生成 `hashed_password`。
- 在 Context 模块中封装 Ecto 查询:`import Ecto.Query, warn: false`,`alias MyApp.{Repo, Accounts.User}`;实现 `list_users` 用 `Repo.all(User)`、`get_user!` 用 `Repo.get!(User, id)`、`register_user` 用 `%User{} |> User.registration_changeset(attrs) |> Repo.insert()`。
- 多步写入用 `Ecto.Multi`:`Multi.new()` 后 `Multi.insert(:user, ...)`,再 `Multi.run(:welcome_email, fn _repo, %{user: user} -> ... end)`,最后 `Repo.transaction()`。
- 用 LiveView 模块把 UI 状态放服务端:`mount/3` 中 `assign(socket, count: 0)`;`handle_event("inc", _params, socket)` 中 `update(socket, :count, &(&1 + 1))`;`render/1` 中用 `~H` 模板和 `phx-click`。
- 遵循 HEEx 技巧:优先 `assign_new/3` 懒计算每连接只算一次的昂贵数据;大列表用 `stream/3` 减少 diff payload;在 `handle_params/3` 处理 URL 驱动状态;不要把 socket 状态存到 params。
- 用 Live Components 拆分 UI:`use MyAppWeb, :live_component`,在 `render/1` 中用 `~H` 遍历 `@items` 并用 `.link navigate={item.href}` 渲染导航。
- PubSub-driven LiveView:在 `mount/3` 中先 `if connected?(socket), do: Phoenix.PubSub.subscribe(MyApp.PubSub, "orders")`;在 `handle_info({:order_created, order}, socket)` 中把 order 加入 `:orders`。
- 从 Context 广播变更:`create_order/1` 在 `Repo.insert()` 成功后执行 `Phoenix.PubSub.broadcast(MyApp.PubSub, "orders", {:order_created, order})`,再返回 `{:ok, order}`。
- 用 Presence 实现在线/输入指示:在 Channel `join("room:" <> room_id, _payload, socket)` 中 `send(self(), :after_join)`;在 `handle_info(:after_join, socket)` 中 `Presence.track(socket, socket.assigns.user_id, %{online_at: System.system_time(:second)})`,`push(socket, "presence_state", Presence.list(socket))`。
- 加固 Channel 安全:在 `join/3` 中授权 topics,在 params/session 中验证用户 token,限制 payload 大小。
- 用 `mix test` 和生成助手测试:在 ConnCase 中 `use MyAppWeb.ConnCase, async: true`,用 `get(conn, "/")` 和 `assert html_response(conn, 200) =~ "Welcome"` 测试页面渲染。
- 用 LiveView 测试:`import Phoenix.LiveViewTest`,用 `live(conn, "/counter")` 获取 view,执行 `view |> element("button", "Increment") |> render_click()`,断言 `render(view) =~ "Count: 1"`。
- 用 DataCase 提供 sandboxed DB connections;把测试包在事务中隔离数据。
- 用 Fixtures 构建测试数据:用 `ExMachina` 或放在 `test/support/fixtures` 下的简单 helper modules。
- 用 Telemetry 可观测:Phoenix 暴露 `[:phoenix, :endpoint, ...]` 事件;通过 `:telemetry_poller`、`OpentelemetryPhoenix`、`OpentelemetryEcto` 导出。
- 部署资产:执行 `mix assets.deploy`,它会运行 npm install、esbuild、tailwind(若配置)并做 digests。
- 制作 release:执行 `MIX_ENV=prod mix release`;在 `config/runtime.exs` 配置 runtime env;用 `PHX_SERVER=true _build/prod/rel/my_app/bin/my_app start` 启动。
- 集群扩展:加入 `libcluster`,用 DNS/epmd 策略水平扩展;使用 distributed PubSub/Presence。
- 缓存:热路径用 ETS/Cachex;优先短 TTL,并在写入时失效。
- 后台任务:用 Oban 处理重试/退避;在 application tree 中监督它。
- 热路径检查:启用 `:telemetry` metrics,检查 LiveView diff 大小,避免大 assigns;优先 streams。
- 避免漏 PubSub 事件:LiveView 必须做 `connected?/1` 检查后再订阅,否则初次渲染会漏事件。
- 避免在 LiveView render 中做重活:把逻辑移到 contexts 并预计算 assigns。
- 多步写入必须用 `Ecto.Multi`,否则失败会留下部分状态。
- 避免用长 NIF 或重 CPU 工作阻塞 BEAM schedulers;卸载到 ports/Oban jobs。
- 避免过度使用无监督或无限制的全局 ETS;会泄漏内存。
- 参考命令:`mix phx.routes` 列出路由和 LiveView 路径。
- 参考命令:`mix phx.gen.live Accounts User users email:string confirmed_at:naive_datetime` 生成 LiveView CRUD,之后审查 context 边界。
- 参考命令:`mix format && mix credo --strict` 做格式化和 lint。
- 参考命令:`mix test --seed 0 --max-failures 1` 让失败确定;可配合 `mix test.watch`。
- 核心组织原则:让 domain logic 留在 contexts,LiveViews 保持薄,BEAM 监督每个组件以增强韧性。
适用边界 · 注意事项
- 团队或项目不采用 Elixir/Erlang/BEAM,或无法引入 Phoenix 工具链时,不适用。
- 需要大量复杂客户端交互、离线优先 PWA、或重 SPA 且不适合服务端 UI 状态的场景,LiveView 不是首选。
- 部署环境无法维持 WebSocket/长连接或不允许有状态节点(如部分受限 serverless)时,不适合直接用 LiveView/Presence/PubSub 方案。