“`html
Dominando a Arquitetura de Vue.js
Eu organizo Vue para escalar com clareza: responsabilidades bem separadas, previsibilidade de estado,
padrões consistentes de componentes e uma base de projeto que facilita manutenção.
Comece pela estrutura: pastas que “explicam” o projeto
A primeira decisão arquitetural é a estrutura de pastas. Ela serve como documentação viva:
quando você abre o repositório, já precisa entender onde fica o quê.
Uma estrutura consistente reduz acoplamento e evita o “espalhamento” de lógica em arquivos aleatórios.
Organize por camadas
ui (componentes), features (casos de uso), services (integrações), store (estado) e shared (utilidades).
Nomeie por intenção
prefira “user-profile”, “orders-list” e “auth-session” a “page1”, “componentA”, “utils2”.
Evite lógica em componentes
componentes devem orquestrar interação e apresentação; regras de domínio vão para módulos.
Compartilhe com critério
utilitários genéricos ficam em shared; peças específicas de uma feature ficam naquela feature.
Sugestão prática de organização para projetos Vue (com Vite ou similar):
// src/
src/
app/
App.vue
routes.ts
ui/
components/
BaseButton.vue
BaseInput.vue
Modal.vue
layouts/
AppLayout.vue
features/
auth/
components/
LoginForm.vue
composables/
useAuth.ts
api/
authApi.ts
types.ts
dashboard/
components/
StatsPanel.vue
api/
dashboardApi.ts
composables/
useDashboard.ts
store/
index.ts
authStore.ts
shared/
composables/
useDebounce.ts
utils/
format.ts
constants/
http.ts
Fluxo de dados: do endpoint até a tela sem bagunça
Arquitetura é previsibilidade. Eu gosto de garantir um fluxo de dados “em linha”:
API → serviço/composable → estado → componente.
Na prática, eu separo:
- Camada de integração: functions que falam com HTTP (sem depender de componentes).
- Camada de orquestração: composables que combinam efeitos, validações e transformação de dados.
- Camada de estado: store (ou estado local) com regras de consistência.
- Camada de apresentação: componentes focados em UI e eventos do usuário.
Dica importante: reduza “efeitos colaterais” espalhados. Ao invés de buscar dados dentro de múltiplos componentes,
centralize a busca na composable ou no store da feature.
// features/auth/api/authApi.ts
export type LoginPayload = { email: string; password: string };
export async function loginApi(payload: LoginPayload) {
const res = await fetch("/api/login", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(payload),
});
if (!res.ok) {
const data = await res.json().catch(() => ({}));
throw new Error(data?.message || "Falha ao realizar login");
}
return res.json() as Promise<{ token: string; user: { id: string; name: string } }>;
}
Componentes com responsabilidade única (e slots quando faz sentido)
Um componente bom é previsível, reutilizável e fácil de testar. Eu divido a responsabilidade em três tipos:
Apresentação
recebe props e emite eventos; sem regra complexa de domínio.
Orquestração de feature
usa composable/store da feature e monta a UI final (quando necessário).
UI base
botões, inputs, modais e componentes genéricos reutilizáveis.
Layout
estrutura geral da aplicação (header, sidebar, container), sem lógica de negócio.
Sobre slots: quando você percebe que precisa “trocar pedaços” do layout do componente
(ex.: cabeçalho, ações, conteúdo), use slots para manter o componente flexível sem criar variações duplicadas.
<!-- ui/components/Modal.vue -->
<template>
<div v-if="open" class="overlay" @click.self="$emit('close')">
<div class="modal" role="dialog" aria-modal="true">
<header class="modal__header">
<slot name="title"> </slot>
<button class="x" type="button" @click="$emit('close')" aria-label="Fechar">✕</button>
</header>
<section class="modal__body">
<slot />
</section>
<footer class="modal__footer">
<slot name="actions"> </slot>
</footer>
</div>
</div>
</template>
Estado e reatividade: onde colocar e como evitar inconsistências
O maior risco arquitetural em Vue é tratar estado de forma “orgânica”: um pedaço aqui, outro ali,
e lógica espalhada entre eventos e lifecycle. Para evitar isso, eu sigo critérios objetivos.
Regra 1 — estado compartilhado fica na store
Se duas telas/partes dependem do mesmo dado (ex.: sessão, permissões, filtros persistentes), ele merece store.
Regra 2 — estado de UI local fica no componente
Modal aberto, formulário com valores temporários e toggles de interface podem ficar local,
desde que não virem “fonte de verdade” de domínio.
Regra 3 — derive ao invés de replicar
Sempre que possível, use computed/derivações para evitar duplicar lógica (ex.: “é válido” a partir de campos).
Exemplo completo (uma composable de feature que controla loading/erro, chama a API e expõe uma interface simples).
// features/auth/composables/useAuth.ts
import { ref, computed } from "vue";
import { loginApi, type LoginPayload } from "../api/authApi";
type AuthState = {
loading: boolean;
error: string | null;
token: string | null;
};
export function useAuth() {
const state = ref<AuthState>({
loading: false,
error: null,
token: null,
});
const isAuthenticated = computed(() => !!state.value.token);
async function login(payload: LoginPayload) {
state.value.loading = true;
state.value.error = null;
try {
const data = await loginApi(payload);
state.value.token = data.token;
} catch (err) {
state.value.error = err instanceof Error ? err.message : "Erro inesperado";
state.value.token = null;
} finally {
state.value.loading = false;
}
}
function logout() {
state.value = { loading: false, error: null, token: null };
}
return {
state,
isAuthenticated,
login,
logout,
};
}
Quer evoluir ainda mais sua arquitetura?
Continue no yurideveloper.com com posts que aprofundam Vue na prática: padrões de componentes,
organização de features, rotas e estratégias para estado.
“`
Sou Apaixonado pela programação e estou trilhando o caminho de ter cada diz mais conhecimento e trazer toda minha experiência vinda do Design para a programação resultando em layouts incríveis e idéias inovadoras! Conecte-se Comigo!