How do you architect a large-scale Vue.js app for reuse?
answer
A scalable Vue.js architecture organizes code by business domains and shared platforms. Use a monorepo with workspace packages for UI components, composables, utilities, and feature modules. Adopt the Composition API for logic reuse, Pinia for typed state, and Vue Router with route-level code splitting. Standardize design tokens, component APIs, and event contracts. Enforce linting, tests, and story-driven development. Encapsulate side effects in services, keep views thin, and publish shared packages for cross-app reuse.
Long Answer
Large Vue.js applications succeed when structure expresses the business. Instead of scattering files by technical type, group code by domain and extract cross-cutting pieces into platform packages. The result is modularity, predictable reuse, and a codebase that scales without friction.
1) Repository and package layout
Use a monorepo (for example, PNPM workspaces or Nx) with packages such as @app/web, @app/mobile-web, @shared/ui, @shared/composables, @shared/utils, and feature modules like @feature/catalog, @feature/checkout. Each package has its own build, tests, and version. This unlocks code sharing with explicit boundaries and enables independent publishing if needed.
2) Composition API and composables
Prefer the Composition API to encapsulate domain logic as reusable composables. Name them by capability (useCart, useFeatureFlags, useFetchCached). Keep them pure where possible and inject side effects through adapters. Return state, actions, and lifecycle hooks with strict typing and explicit error models. Co-locate tests and simple usage examples to drive adoption.
3) State management with Pinia
Adopt Pinia for store modules that mirror domains. Keep stores slim: normalized entities, derived getters, and action methods that call services. Avoid over-centralization; prefer local component state for transient UI. Persist only what is required and invalidate with clear cache strategies. Use pinia-plugin-persistedstate cautiously and encrypt sensitive values.
4) Routing and module federation
Define routes per domain, using lazy loading and route-level code splitting. Provide route guards that rely on capability checks rather than roles hard-coded in components. For very large organizations, expose feature shells through Module Federation or build-time composition to enable independent deployments without duplicating shared libraries.
5) UI system and design tokens
Create a documented component library in Storybook under @shared/ui. Standardize inputs, forms, tables, and navigation, with accessibility baked in. Expose design tokens (color, spacing, typography, motion) via CSS variables and consume them in Vue Single File Components. Stabilize component props and events through RFCs and codemods to evolve without breaking dependents.
6) Data access and side effects
Abstract network and storage behind services: @shared/services/http, @shared/services/cache. Centralize fetch, retry with backoff, cancellation, and error translation. Provide a useQuery and useMutation pattern to handle request state, caching keys, and optimistic updates. Keep components declarative; let composables or services orchestrate data.
7) Performance and code quality
Budget for bundle size and interaction latency. Enable tree shaking, vendor chunk splitting, and prefetch hints. Use defineAsyncComponent for heavy widgets. Track Web Vitals, Pinia mutation timings, and slow renders. Enforce ESLint, TypeScript strictness, Vue Test Utils, and component snapshot testing. Run visual regression tests for shared UI.
8) Build, CI/CD, and governance
Cache builds across packages, run affected-only tests, and gate merges on coverage and type checks. Publish shared packages from the monorepo with semantic versions and changelogs. Establish architectural decisions as ADRs and require design reviews for new composables, tokens, or global stores.
9) Observability and error handling
Instrument pages and actions with analytics, expose a global error boundary, and route recoverable failures to toast or inline patterns. Log with correlation identifiers so backend traces can be joined. Maintain a support mode that surfaces environment, feature flags, and store snapshots for triage.
This structure turns Vue.js into a platform: domains stay independent, shared packages evolve safely, and teams reuse logic through composables and a governed UI library, delivering maintainable velocity at scale.
Table
Common Mistakes
- Organizing by file type instead of domains, which scatters logic and hurts reuse.
- Overusing a global store for every bit of state, creating tight coupling and spaghetti updates.
- Mixing side effects inside components rather than services or composables.
- Skipping route-level code splitting, causing slow first loads.
- Inconsistent component props and events across teams, breaking reuse.
- No design tokens, producing visual drift and costly rebrands.
- Ad hoc fetch logic without retries, cancellation, or cache keys.
- Monolithic CI that rebuilds everything, slowing delivery.
- Weak typing and no tests for composables, leading to brittle integrations.
- Ignoring accessibility and Storybook, so UI defects reach production.
Sample Answers
Junior:
I would use the Composition API to move logic into use* composables, keep components focused on templates, and manage shared state with Pinia. I would add lazy routes for big views and start a small Storybook library for common buttons and forms.
Mid:
I group code by domain in a monorepo, create @shared/ui and @shared/composables, and standardize tokens and component contracts. Pinia stores mirror domains; services wrap network calls with retries and caching. Vue Router loads routes lazily, and CI runs affected builds with type checks and tests.
Senior:
I set governance: ADRs, RFCs for component APIs, semantic versioning for shared packages, and performance budgets. I introduce useQuery/useMutation, error boundaries, and observability. Module Federation or build-time composition lets teams deploy features independently while consuming the same tokens and libraries.
Evaluation Criteria
Look for domain-oriented structure, clear boundaries, and governed reuse. Strong answers use the Composition API for logic, Pinia for scoped state, and a service layer for effects. They mention lazy routes, code splitting, and Storybook with design tokens. Expect typed contracts, tests for composables and components, and CI that builds only affected packages. Senior candidates describe semantic versioning, ADRs, and performance budgets, plus observability and error boundaries. Red flags include global state everywhere, technical folder structure, inconsistent component APIs, and no plan for caching, retries, or cancellation. Bonus points for module federation strategy, accessibility baked into the UI library, and codemods for safe migrations.
Preparation Tips
- Create a small monorepo with @shared/ui, @shared/composables, and one feature.
- Build tokens in CSS variables and expose a Button, Input, and Modal in Storybook.
- Write a useFetchCached composable with keys, retries, and cancellation.
- Add a Pinia store per domain with unit tests and typed selectors.
- Configure route-level code splitting and a guarded admin layout.
- Set up ESLint, TypeScript strict mode, Vue Test Utils, and Playwright.
- Add affected-only CI, coverage gates, and semantic-release for shared packages.
- Measure bundle size, first load, and interaction latency; set budgets.
- Practice an ADR for a breaking component API and a codemod to migrate usages.
- Instrument an error boundary and a simple analytics hook for actions.
Real-world Context
A marketplace migrated to a domain-based monorepo and extracted @shared/ui. New brand tokens rolled out in one day across four apps. A fintech replaced ad hoc fetch logic with useQuery and a service layer; error rates dropped and retries reduced support tickets. A media platform introduced route-level splitting and async components; first load improved while feature velocity increased. A SaaS vendor added ADRs and semantic releases for @shared/composables; teams upgraded safely with codemods. Pinia stores aligned to domains eliminated global coupling, and Storybook previews caught accessibility issues before launch. With affected-only CI, pipeline time fell significantly, enabling more frequent, safer releases without regressions.
Key Takeaways
- Organize by domain, extract shared UI, composables, and services.
- Use the Composition API, Pinia, and route-level code splitting.
- Standardize tokens and component contracts; document in Storybook.
- Centralize side effects with retries, caching, and cancellation.
- Enforce typing, tests, ADRs, and semantic versioning for safe evolution.
Practice Exercise
Scenario:
You must design the foundation for a multi-team Vue.js platform that powers a catalog, checkout, and analytics dashboard. Teams need rapid delivery, shared look and feel, and the ability to reuse logic across apps without copy-paste.
Tasks:
- Create a monorepo plan with packages: @shared/ui, @shared/composables, @shared/services, and features @feature/catalog, @feature/checkout. Describe ownership and publishing rules.
- Define design tokens (color, spacing, typography, motion) and a Storybook checklist for accessibility, props, events, and visual tests.
- Propose a routing map with lazy routes, nested layouts, and capability-based guards.
- Specify Pinia store boundaries and a data flow that uses useQuery/useMutation, optimistic updates, cache keys, and invalidation.
- Outline a service layer for network calls with retries, backoff, cancellation, and error translation.
- Set CI gates: affected-only builds, type checks, unit and e2e tests, and semantic releases for shared packages.
- Add an observability plan: error boundary, action analytics, and store snapshot tooling for triage.
- Write one ADR that records the decision to standardize component events and a codemod plan to migrate breaking changes.
Deliverable:
A short architecture document with diagrams, a token table, a route map, store boundaries, CI gates, and the ADR, plus a checklist to validate performance budgets and accessibility before the first release.

