Derived Modular Arch
Guides

Vue + Vite

DMA on Vue 3: SFC, composables, provide/inject.

Runnable Vue 3 + Vite example: composition root src/app/, SFC modules, colocated composables, provide/inject for cross-cutting context.

Sources: examples/vue-vite.

Composition root

src/app/
├── App.vue            # nav, badges, mount */public/*
├── AppProviders.vue   # provide(shopThemeKey, …)
└── app.css            # global reset + CSS variables

App.vue wires catalog and notifications via emit → public API — without feature → feature:

<CatalogPage @added-to-cart="onAddedToCart" />
import { addNotification } from "@/features/notifications/public/notifications-api";

Composables rule

Composables stay in the feature folder until a second module imports the same logic:

ComposableWhereWhy
use-catalog-search.tsfeatures/catalog/Catalog only
use-checkout.tsfeatures/checkout/Checkout only
use-notifications.tsfeatures/notifications/Notifications only

Promotion: product state → services/, pure helpers → shared/lib.

provide / inject

Cross-cutting theme — in app, not in a feature:

<!-- AppProviders.vue -->
<script setup>
import { provide } from "vue";
import { shopThemeKey } from "@/shared/domain/shop-theme";

provide(shopThemeKey, { accent: "teal" });
</script>
<!-- features/profile.vue -->
<script setup>
import { inject } from "vue";
import { shopThemeKey } from "@/shared/domain/shop-theme";

const theme = inject(shopThemeKey);
</script>

Injection keys in shared/domain/ — portable, no product logic.

SFC boundaries

FileLayer
App.vueapp/ — mount */public/* only
AppProviders.vueapp/ — provide context
catalog-page.vuefeature public/ — relative → composables
profile.vuestage-0 — inject
cart.tsservice — framework-agnostic

public/ imports internals via relative paths (../use-catalog.ts). Cross-module — @/ aliases to */public/*.

Where to put code

TaskFolder
Shell, layoutsrc/app/
Product screenfeatures/<name>/public/<page>.vue
Feature composablecolocated in feature
2+ consumersservices/<name>/public/
UI primitiveshared/ui/
Injection keysshared/domain/
Typesshared/model/

Styles

KindWhere
Global baselineapp/app.css
Shell layoutApp.vue <style scoped>
Feature pagepublic/*.vue scoped or CSS Modules
SCSS (optional)profile.vue <style lang="scss" scoped>
Shared UIshared/ui/*.vue scoped

CSS Modules: catalog-page.module.css + <style module src="…">.

Tests

Colocated *.test.ts (Vitest, environment: "node"):

  • use-catalog-search.test.ts
  • checkout.store.test.ts
  • services/cart/public/cart.test.ts

Component tests — optional with @vue/test-utils.

Commands

bun run dev
bun run build
bun run dma-check   # dma check .
bun run lint        # biome + @derived-modular/biome-plugin
bun run test

What's next

On this page