This episode covers large-scale Vue application architecture: feature-based folder structure, separating UI, logic, and service, reusable component libraries and design systems, plus patterns that keep code structured as the team grows.

Code that works today won't necessarily survive a year. As features grow and teams get bigger, architecture quality decides whether changes are cheap or expensive. Good architecture organizes complexity — it doesn't eliminate it.
Episode 18 covers how to structure a Vue application for large scale: layered architecture, feature-based folder structure, design systems and component libraries, and separating logic, UI, and service so each part can evolve on its own.
A healthy Vue application has clear layers:
view: komponen dan template
logic: composables dan state
service: komunikasi dengan API
data: model dan transformasiViews only render; logic is held by composables; services handle networking; data defines the model shape. When one layer changes, the others don't fall apart.
Instead of grouping by file type, group by feature:
src/features/
auth/
components/LoginForm.vue
composables/useLogin.js
services/authApi.js
views/LoginView.vue
produk/
components/ProductCard.vue
services/produkApi.jsEach feature is self-contained: components, logic, services, and pages live in one folder. A feature can be developed, tested, and removed without touching other features — this is the essence of domain-driven design for frontend.
A design system starts with tokens: colors, spacing, and typography in a single source of truth.
export const tokens = {
warna: {
primer: "#3b82f6",
teks: "#1f2937",
latar: "#ffffff",
},
spacing: { kecil: 4, sedang: 8, besar: 16 },
};tokens.warna.primer is used by every component, so a brand change only requires editing one file. Base components like BaseButton read these tokens for consistency.
Reuse base components with controlled props:
<script setup>
defineProps({
variant: { type: String, default: "primer" },
disabled: { type: Boolean, default: false },
});
</script>
<template>
<button class="btn" :class="`btn-${variant}`" :disabled="disabled">
<slot />
</button>
</template>BaseButton unifies styling and behavior. Variations go through the variant prop, not by copying styles each time.
All network calls are collected in the service layer:
export const produkApi = {
async daftar() {
const res = await fetch("/api/produk");
return res.json();
},
async detail(id) {
const res = await fetch(`/api/produk/${id}`);
return res.json();
},
};produkApi.daftar() wraps an endpoint. Components don't need to know the fetch details; the service can be swapped for GraphQL or a mock without changing the view.
A composable turns raw data into state ready to display:
import { ref } from "vue";
import { produkApi } from "./produkApi";
export function useProduk() {
const list = ref([]);
const loading = ref(false);
async function muat() {
loading.value = true;
list.value = await produkApi.daftar();
loading.value = false;
}
return { list, loading, muat };
}The view just calls useProduk() and renders list; logic and state can be tested without a browser.
<script setup>
import { useProduk } from "../composables/useProduk";
const { list, loading, muat } = useProduk();
muat();
</script>
<template>
<div v-if="loading">Memuat...</div>
<ProductCard v-for="item in list" :key="item.id" :produk="item" />
</template>A thin view loads a composable, calls its action, and renders. All business decisions live in the logic layer, keeping the UI easy to read and test.
Tip
Good architecture can be explained to a new team member in a single diagram. If your folder structure already needs a novel-length document, it might be time to simplify.
Episode 18 structured your application for growth: layered view-logic-service-data architecture, feature-based folder structure, a design system with tokens and base components, and clean separation that lets each layer evolve independently.
Key takeaways:
In the next episode 19, we'll cover modern tooling and build automation — Vite as a high-performance bundler, TypeScript integration, CI/CD pipelines for Vue applications, plus linting, formatting, and pre-commit hooks.