DEV Community

Cover image for Arquitetura Modular com Nuxt Layers em Projetos Vue
Gabriel Caiana
Gabriel Caiana

Posted on

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
Enter fullscreen mode Exit fullscreen mode

Parece organizado, certo? Mas vamos pensar em um cenário real:

"Preciso implementar uma nova feature no carrinho"

Você vai precisar:

  1. Navegar até components/cart/ para os componentes
  2. Pular para pages/cart.vue para a página
  3. Ir em stores/cart.js para a lógica de estado
  4. Verificar composables/useCart.js para funções auxiliares
  5. 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
Enter fullscreen mode Exit fullscreen mode

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'
  ]
})
Enter fullscreen mode Exit fullscreen mode

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.
    }]
  }
})
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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/
Enter fullscreen mode Exit fullscreen mode

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
  ]
})
Enter fullscreen mode Exit fullscreen mode
// layers/blog/nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxt/content'],

  content: {
    sources: {
      blog: {
        prefix: '/blog',
        base: './layers/blog/content'
      }
    }
  }
})
Enter fullscreen mode Exit fullscreen mode
<!-- 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>
Enter fullscreen mode Exit fullscreen mode

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

  1. Documentação Oficial Nuxt Layers - A fonte definitiva
  2. Nuxt Layers Unwrapped - Artigo detalhado de Dave Stewart
  3. Domain-Driven Design - Eric Evans sobre modelagem por domínio
  4. Building Evolutionary Architectures - Sobre arquiteturas que evoluem

Top comments (1)

Collapse
 
hashbyt profile image
Hashbyt

Modular frontend architecture like Nuxt Layers is transformative for SaaS projects, enabling natural domain cohesion and parallel development that scales teams without sacrificing UX consistency.