
Arquitetura Modular com Nuxt Layers em Projetos Vue
O Desafio Invisível que Todo Dev Frontend Enfrenta
Você conhece essa história: começou um projeto Vue pequeno, organizou tudo direitinho - components
aqui, pages
ali, stores
no seu lugar. Três meses depois, você tem 147 componentes, e encontrar aquele ProductCardVariantB.vue
virou uma expedição arqueológica.
Pior ainda: você precisa mexer no carrinho de compras, mas os arquivos estão espalhados em 7 pastas diferentes. Um componente aqui, uma store ali, as páginas em outro lugar… e aquela sensação de que a organização que fazia sentido no início agora está atrapalhando seu trabalho.
Este é o problema silencioso da escalabilidade em frontend. E a solução vem de um lugar inesperado: conceitos de arquitetura backend aplicados ao mundo do navegador.
A Inspiração: Monólitos Modulares do Backend
No mundo backend, existe um debate eterno: monólito ou microserviços? Mas arquitetos como Sam Newman e Simon Brown propuseram uma terceira via: o monólito modular.
Como Martin Fowler explica, um monólito modular é “uma aplicação única que mantém uma estrutura interna altamente modularizada”. Você tem os benefícios de modularização (separação de responsabilidades, desenvolvimento independente, fronteiras claras) sem a complexidade operacional de microserviços (múltiplos deploys, comunicação via rede, orquestração).
O Princípio Fundamental
A sacada genial é organizar o código por domínio de negócio em vez de responsabilidade técnica. No backend, isso significa que tudo relacionado a “Pagamentos” fica junto - controllers, services, repositories, models. Tudo sobre “Usuários” em outro módulo. E assim por diante.
Mas… e no frontend?
O Problema da Organização Tradicional em Vue/Nuxt
Vamos ser honestos sobre como 99% dos projetos Vue são organizados hoje:
meu-ecommerce/
├── components/
│ ├── cart/
│ │ ├── CartItem.vue
│ │ ├── CartSummary.vue
│ │ └── CartDrawer.vue
│ ├── product/
│ │ ├── ProductCard.vue
│ │ ├── ProductGallery.vue
│ │ └── ProductReviews.vue
│ └── shared/
│ ├── Button.vue
│ └── Modal.vue
├── pages/
│ ├── products/
│ │ └── [id].vue
│ ├── cart.vue
│ └── index.vue
├── stores/
│ ├── cart.js
│ ├── product.js
│ └── user.js
└── composables/
├── useCart.js
└── useProduct.js
Parece organizado, certo? Mas vamos pensar em um cenário real:
“Preciso implementar uma nova feature no carrinho”
Você vai precisar:
- Navegar até
components/cart/
para os componentes - Pular para
pages/cart.vue
para a página - Ir em
stores/cart.js
para a lógica de estado - Verificar
composables/useCart.js
para funções auxiliares - Talvez até
api/cart.js
se tiver separado
São 5 diretórios diferentes para trabalhar em uma única feature. Multiplique isso por cada desenvolvedor do time, cada feature, cada dia… e você entende por que projetos grandes se tornam difíceis de manter.
Nuxt Layers: A Revolução Silenciosa
Com o Nuxt 3, veio uma feature que passou meio despercebida mas que é revolucionária: Nuxt Layers. Como Dave Stewart explica em seu artigo sobre arquitetura modular, Layers permitem reorganizar completamente a estrutura do projeto.
Em vez de organizar por tipo de arquivo, organizamos por domínio:
meu-ecommerce/
├── layers/
│ ├── cart/ # Tudo sobre carrinho
│ │ ├── components/
│ │ │ ├── CartItem.vue
│ │ │ ├── CartSummary.vue
│ │ │ └── CartDrawer.vue
│ │ ├── pages/
│ │ │ └── cart.vue
│ │ ├── stores/
│ │ │ └── cart.js
│ │ ├── composables/
│ │ │ └── useCart.js
│ │ └── nuxt.config.ts # Config específica do cart
│ │
│ ├── product/ # Tudo sobre produtos
│ │ ├── components/
│ │ ├── pages/
│ │ ├── stores/
│ │ └── nuxt.config.ts
│ │
│ └── catalog/ # Tudo sobre catálogo
│ ├── components/
│ ├── pages/
│ ├── stores/
│ └── nuxt.config.ts
└── nuxt.config.ts # Config principal
Como Funciona na Prática
Cada layer é como uma “mini-aplicação” Vue. Tem sua própria estrutura, suas próprias configurações, seus próprios módulos. O Nuxt então “costura” tudo junto em tempo de build.
// nuxt.config.ts principal
export default defineNuxtConfig({
extends: [
'./layers/cart',
'./layers/product',
'./layers/catalog'
]
})
E cada layer pode ter sua própria configuração:
// layers/cart/nuxt.config.ts
export default defineNuxtConfig({
// Módulos específicos do carrinho
modules: ['@vueuse/nuxt'],
// Components do carrinho
components: {
dirs: [{
path: './components',
prefix: 'Cart' // CartItem, CartSummary, etc.
}]
}
})
Os Benefícios Transformadores
1. Coesão Natural de Domínio
Quando você precisa trabalhar no carrinho, tudo está em layers/cart
. Não precisa ficar pulando entre pastas. É como ter um mini-projeto dedicado só para aquela feature.
# Trabalhando no carrinho? É só isso:
cd layers/cart
# Todos os arquivos relacionados estão aqui
2. Desenvolvimento Verdadeiramente Paralelo
Com domínios isolados, times diferentes podem trabalhar sem pisar no pé um do outro:
- Time A trabalhando em
layers/checkout
- Time B refatorando
layers/product
- Time C criando nova feature em
layers/recommendations
Menos conflitos no Git, menos “quem mexeu no meu componente?”, menos stress.
3. Onboarding Simplificado
Novo dev no time? Em vez de explicar a arquitetura inteira:
“Você vai cuidar do catálogo. Está tudo em layers/catalog
. É como se fosse uma aplicação Vue separada, mas integrada com o resto.”
Pronto. Em 5 minutos a pessoa já está produtiva.
4. Boundaries Naturais e Encapsulamento
Cada layer expõe apenas o que precisa ser público. O resto fica encapsulado:
// layers/cart/index.ts - Interface pública
export { useCart } from './composables/useCart'
export { CartButton } from './components/CartButton'
// CartDrawer, CartLogic, etc. ficam privados
5. Preparação para o Futuro
Hoje você tem um monólito modular. Amanhã, se precisar, pode:
- Extrair uma layer como pacote NPM
- Transformar em micro-frontend
- Mover para um repo separado
- Criar uma aplicação independente
A arquitetura já está pronta para evolução.
Cenários Onde Brilha (e Onde Não)
Cenários Perfeitos ✅
1. E-commerce Médio/Grande
- Domínios claros: Catálogo, Produto, Carrinho, Checkout, User
- Features complexas mas independentes
- Múltiplos times ou desenvolvedores
2. Aplicações SaaS B2B
- Dashboard, Reports, Settings, Integrations
- Cada módulo com suas próprias regras e complexidades
- Necessidade de evolução independente
3. Portais e Marketplaces
- Área do vendedor, área do comprador, admin
- Diferentes experiências e necessidades
- Times especializados por área
4. Aplicações Multi-tenant
- Core compartilhado
- Customizações por cliente em layers separadas
- Facilita white-label
Onde Talvez Não Faça Sentido ❌
1. Landing Pages e Sites Institucionais
- Poucos componentes
- Sem domínios claros de negócio
- Complexidade desnecessária
2. MVPs e Protótipos
- Velocidade > Estrutura
- Mudanças constantes de escopo
- Time muito pequeno (1-2 devs)
3. Aplicações Hipersimples
- CRUD básico
- Menos de 20 componentes
- Sem perspectiva de crescimento
Comparação com Outras Abordagens
Monorepo com Workspaces
monorepo/
├── packages/
│ ├── cart/
│ ├── product/
│ └── shared/
└── apps/
└── main-app/
Prós do Monorepo:
- Separação física total
- Versionamento independente
- Pode publicar no NPM
Prós do Nuxt Layers:
- Menos complexidade de configuração
- Build único e otimizado
- Melhor DX (developer experience)
- HMR funcionando perfeitamente
Micro-frontends
Micro-frontends são o extremo da modularização - aplicações completamente independentes que se juntam no browser.
Quando Micro-frontends fazem sentido:
- Times completamente independentes
- Tecnologias diferentes (Vue + React + Angular)
- Deploy independente é crítico
- Escala massiva (100+ devs)
Quando Nuxt Layers é melhor:
- Time único ou poucos times
- Stack Vue/Nuxt padronizada
- Quer evitar complexidade de orquestração
- Performance é prioridade
Implementando na Prática: Exemplo Simples
Vamos ver um exemplo minimalista de como estruturar um blog + loja:
// nuxt.config.ts
export default defineNuxtConfig({
extends: [
'./layers/base', // Compartilhados
'./layers/blog', // Blog com Nuxt Content
'./layers/shop' // Loja simples
]
})
// layers/blog/nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/content'],
content: {
sources: {
blog: {
prefix: '/blog',
base: './layers/blog/content'
}
}
}
})
<!-- layers/shop/pages/shop.vue -->
<template>
<div>
<h1>Nossa Loja</h1>
<ProductGrid :products="products" />
<!-- ProductGrid está em layers/shop/components/ -->
</div>
</template>
<script setup>
// Tudo local ao domínio shop
import { useShopStore } from '../stores/shop'
const shop = useShopStore()
const products = await shop.loadProducts()
</script>
Migração Gradual: O Segredo do Sucesso
A beleza desta arquitetura é que você não precisa reescrever tudo. Pode migrar gradualmente:
Fase 1: Identificar Domínios
Liste os domínios óbvios da sua aplicação. Geralmente são as “grandes áreas” que os usuários veem.
Fase 2: Criar Layer Base
Mova componentes compartilhados, utils, composables genéricos.
Fase 3: Migrar um Domínio Piloto
Escolha o domínio mais isolado (geralmente algo como “About” ou “Blog”) e migre como teste.
Fase 4: Expandir Gradualmente
Um domínio por vez, conforme o time tem tempo. Não é um big bang, é evolução.
O Futuro é Modular
A arquitetura modular não é apenas uma moda ou uma forma diferente de organizar pastas. É uma mudança fundamental em como pensamos sobre a estrutura de aplicações frontend.
Como Eric Evans explica em Domain-Driven Design, software deve refletir o domínio do negócio. Com Nuxt Layers, finalmente temos uma forma elegante de fazer isso no frontend.
Conclusão: Pronto para o Próximo Passo?
Se você chegou até aqui, provavelmente está pensando: “Ok, a teoria é linda, mas como eu implemento isso de verdade?”
Ótima pergunta! No próximo artigo, vamos sair da teoria e construir um e-commerce completo do zero usando Nuxt Layers. Vamos implementar:
- Sistema de catálogo com filtros e busca
- Páginas de produto com galeria e reviews
- Carrinho de compras com persistência
- Integração real entre as layers
- Testes, deploy e otimizações
Com código real, funcionando, que você pode copiar e adaptar.
Recursos para se Aprofundar
- Documentação Oficial Nuxt Layers - A fonte definitiva
- Nuxt Layers Unwrapped - Artigo detalhado de Dave Stewart
- Domain-Driven Design - Eric Evans sobre modelagem por domínio
- Building Evolutionary Architectures - Sobre arquiteturas que evoluem