Acoplar a robustez da máquina virtual do Erlang (BEAM) rodando Elixir (Phoenix) no backend com uma interface moderna em Next.js/React gerenciada pelo PNPM no frontend sob uma estrutura de monorepo híbrido é uma das decisões de arquitetura mais resilientes para sistemas de alta concorrência.
O desafio reside em coordenar os lifecycles de compilação, o gerenciamento de assets, o ambiente de desenvolvimento local e a infraestrutura de deploy de forma coesa. Abaixo, detalho como estruturar essa integração para produção.
1. A estrutura do monorepo#
Para evitar misturar dependências do Node com arquivos do Elixir na raiz do projeto, estruturo os ecossistemas de forma isolada dentro de pastas do diretório apps:
/meu-projeto-monorepo
├── apps/
│ ├── backend/ # Elixir Phoenix (BEAM)
│ │ ├── lib/
│ │ ├── config/
│ │ ├── priv/static/ # Destino dos assets estáticos
│ │ └── mix.exs
│ └── frontend/ # Next.js / React (Node.js/V8)
│ ├── src/
│ ├── public/
│ ├── next.config.js
│ └── package.json
├── package.json # Root do monorepo Node
├── pnpm-workspace.yaml # Definição de escopo do workspace Node
├── docker-compose.yml # Orquestração local de serviços
├── Makefile # Atalhos de comandos do projeto
└── .github/workflows/ci.yml # Pipeline de integração contínua
O arquivo pnpm-workspace.yaml na raiz especifica a pasta do frontend:
# pnpm-workspace.yaml
packages:
- 'apps/frontend'
2. Estratégias de integração entre frontend e backend#
Abordagem a: static export (SSG/SPA integrado)#
Ideal para painéis administrativos onde as views são servidas de forma estática pelo endpoint HTTP do Phoenix (priv/static):
- O Next.js compila assets estáticos puros (HTML, JS, CSS).
- Os arquivos são sincronizados via script para a pasta
priv/static/do Phoenix.
Abordagem b: servidor híbrido (SSR separado)#
Recomendado se o frontend necessita de Server-Side Rendering (SSR) dinâmico do Next.js ou otimização pesada de imagens:
- Next.js roda sob Node na porta
3000e Phoenix roda na BEAM na porta4000. - Um proxy reverso (Nginx) roteia
/api/e/socket/para o Phoenix, e as rotas de página para o Next.js.
3. Configuração do next.config.js#
Para abordagem a (static export):#
// apps/frontend/next.config.js
module.exports = {
output: 'export',
distDir: 'out',
images: {
unoptimized: true,
},
};
Script de sincronização no package.json do frontend:
{
"scripts": {
"build": "next build",
"postbuild": "rsync -av --delete --exclude=\"phoenix_dashboard/\" out/ ../backend/priv/static/"
}
}
4. Phoenix watchers e hot module replacement (HMR)#
Para rodar todo o ambiente em um único comando no desenvolvimento, integre o lifecycle do Next.js aos Watchers do Phoenix no apps/backend/config/dev.exs:
config :backend, BackendWeb.Endpoint,
http: [port: 4000],
debug_errors: true,
code_reloader: true,
check_origin: false,
watchers: [
pnpm: [
"run",
"dev",
cd: Path.expand("../../frontend", __DIR__)
]
]
5. Dockerização multi-stage#
# apps/backend/Dockerfile
FROM elixir:1.16-alpine AS builder
RUN apk add --no-cache build-base git
WORKDIR /app
RUN mix local.hex --force && mix local.rebar --force
ENV MIX_ENV=prod
COPY mix.exs mix.lock ./
RUN mix deps.get --only prod && mix deps.compile
COPY config config
COPY lib lib
COPY priv priv
RUN mix compile && mix release
FROM alpine:3.19 AS runtime
RUN apk add --no-cache libstdc++ openssl ncurses-libs bash
WORKDIR /app
COPY --from=builder /app/_build/prod/rel/backend ./
ENV PORT=4000
EXPOSE 4000
CMD ["./bin/server"]
6. Alta disponibilidade na BEAM#
A máquina virtual BEAM oferece tolerância a falhas nativa através de árvores de supervisão (Supervisor Trees) e clustering de nós distribuídos sem necessidade de brokers externos como Redis:
def start(_type, _args) do
children = [
Backend.Repo,
BackendWeb.Telemetry,
{Phoenix.PubSub, name: Backend.PubSub},
BackendWeb.Endpoint
]
opts = [strategy: :one_for_one, name: Backend.Supervisor]
Supervisor.start_link(children, opts)
end
Recomendações para monorepos híbridos#
- Isole os workspaces: Mantenha dependências do Node restritas ao frontend e bibliotecas Hex no backend.
- Utilize o Makefile como orquestrador local: Um comando único (
make devoumake test) simplifica a integração de novos membros no time. - Configure healthchecks no proxy: Garanta que o Nginx teste a disponibilidade de ambas as portas antes de encaminhar tráfego de produção.
Este artigo foi útil?
Deixe uma reação rápida para apoiar o conteúdo:
Este post está licenciado sob CC BY-NC.



Comentários
Participe da discussão abaixo.
0 comentários