Documentation Index Fetch the complete documentation index at: https://docs-mstore.faisalaffan.com/llms.txt
Use this file to discover all available pages before exploring further.
DDD + Clean Architecture
MStore Dashboard mengimplementasikan Domain-Driven Design (DDD) dengan Clean Architecture untuk memastikan codebase yang maintainable, testable, dan scalable.
Architecture Overview
Directory Structure
mstore_dashboard/
│
├─ app/ # Presentation Layer
│ ├─ layouts/ # Layout components
│ │ ├─ default.vue # Main layout with navigation
│ │ └─ auth.vue # Auth pages layout
│ ├─ pages/ # Route pages
│ │ ├─ index.vue # Home page
│ │ ├─ login.vue # Login page
│ │ └─ inventory/ # Inventory pages
│ ├─ middleware/ # Route middleware
│ │ └─ auth.global.ts # Auth guard
│ └─ error.vue # Error page
│
├─ features/ # Feature/Domain Layer
│ ├─ 01_core/ # Core domain
│ │ ├─ api/ # API calls
│ │ ├─ components/ # Domain components
│ │ ├─ composables/ # Domain logic
│ │ ├─ store/ # Pinia stores
│ │ ├─ types/ # TypeScript types
│ │ └─ index.ts # Barrel export
│ │
│ ├─ 03_inventory/ # Inventory domain
│ │ ├─ api/
│ │ │ ├─ getList.ts
│ │ │ ├─ create.ts
│ │ │ └─ update.ts
│ │ ├─ components/
│ │ │ └─ InventoryTable.vue
│ │ ├─ composables/
│ │ │ └─ useInventoryList.ts
│ │ ├─ store/
│ │ │ └─ inventory.store.ts
│ │ ├─ types/
│ │ │ └─ inventory.types.ts
│ │ └─ index.ts
│ │
│ └─ ... (other domains)
│
├─ components/ # Global Components
│ ├─ AppSpreadsheet.vue # Shared spreadsheet
│ └─ NotificationDropdown.vue # Notifications
│
├─ composables/ # Global Composables
│ ├─ useAuth.ts # Auth utilities
│ └─ useDebounce.ts # Debounce utility
│
├─ server/ # Backend Layer (Nitro)
│ ├─ api/ # API endpoints
│ └─ middleware/ # Server middleware
│
├─ utils/ # Pure Functions
│ ├─ format.ts # Formatting utilities
│ └─ validator.ts # Validation functions
│
└─ types/ # Global Types
└─ index.ts # Shared type definitions
Architecture Layers
1. Presentation Layer
Bertanggung jawab untuk:
Rendering UI
Route handling
User interactions
Layout management
<!-- app/pages/inventory/index.vue -->
< script setup lang = "ts" >
import { useInventoryList } from '~/features/03_inventory'
const { items , loading , fetchItems } = useInventoryList ()
onMounted (() => fetchItems ())
</ script >
< template >
< div >
< h1 > Inventory </ h1 >
< InventoryTable : items = " items " : loading = " loading " />
</ div >
</ template >
2. Feature Layer (Domain)
Setiap domain adalah bounded context yang berisi:
Component Responsibility api/HTTP calls ke backend components/UI components khusus domain composables/Business logic dan page state store/Global state management types/TypeScript interfaces index.tsBarrel export
// features/03_inventory/index.ts (Barrel Export)
export * from './api'
export * from './store'
export * from './composables'
export * from './types'
export { default as InventoryTable } from './components/InventoryTable.vue'
3. Data Layer
Menangani:
API communication
Data transformation
Caching (IndexedDB)
// features/03_inventory/api/getList.ts
export const getInventoryList = async ( params : InventoryQueryParams ) => {
const query = new URLSearchParams ()
if ( params . page ) query . set ( 'page' , params . page . toString ())
if ( params . limit ) query . set ( 'limit' , params . limit . toString ())
return await $fetch < InventoryListResponse >( '/api/inventory' , { query })
}
4. Backend Layer (Nitro)
Server-side logic:
API proxying
Server middleware
SSR data fetching
Key Principles
1. Separation of Concerns
Setiap layer memiliki tanggung jawab yang jelas:
┌─────────────────────────────────────────────┐
│ UI Layer → Render & User Interaction │
├─────────────────────────────────────────────┤
│ Composable → Page Logic & Coordination │
├─────────────────────────────────────────────┤
│ Store → State Management │
├─────────────────────────────────────────────┤
│ API → HTTP Communication │
└─────────────────────────────────────────────┘
2. Single Responsibility
Setiap file memiliki satu alasan untuk berubah:
// BAD: Mixed responsibilities
const useInventory = () => {
// State management
const items = ref ([])
// API call
const fetch = async () => { /* ... */ }
// UI logic
const formatPrice = ( price ) => { /* ... */ }
// Validation
const validate = ( item ) => { /* ... */ }
}
// GOOD: Separated responsibilities
// store/inventory.store.ts - State only
// api/getList.ts - API only
// composables/useInventoryList.ts - Page logic only
// utils/format.ts - Formatting only
3. Dependency Inversion
High-level modules tidak bergantung pada low-level modules:
// Composable depends on store interface, not implementation
const useInventoryList = () => {
const store = useInventoryStore () // Injected dependency
const fetchItems = async () => {
await store . fetchItems () // Uses store abstraction
}
return { items: store . items , fetchItems }
}
4. DRY (Don’t Repeat Yourself)
Reuse components dan utilities:
// utils/format.ts - Reusable across all domains
export const formatCurrency = ( value : number , currency = 'IDR' ) => {
return new Intl . NumberFormat ( 'id-ID' , {
style: 'currency' ,
currency
}). format ( value )
}
// Used in multiple domains
import { formatCurrency } from '~/utils/format'
Feature Module Structure
Setiap feature module mengikuti struktur yang sama:
features/{domain}/
├── api/
│ ├── index.ts # Barrel export
│ ├── getList.ts # GET /api/{domain}
│ ├── getById.ts # GET /api/{domain}/:id
│ ├── create.ts # POST /api/{domain}
│ ├── update.ts # PUT /api/{domain}/:id
│ └── delete.ts # DELETE /api/{domain}/:id
│
├── components/
│ ├── index.ts # Barrel export
│ ├── {Domain}Table.vue # List table
│ ├── {Domain}Form.vue # Create/Edit form
│ └── {Domain}Card.vue # Card component
│
├── composables/
│ ├── index.ts # Barrel export
│ ├── use{Domain}List.ts # List page logic
│ ├── use{Domain}Detail.ts # Detail page logic
│ └── use{Domain}Form.ts # Form logic
│
├── store/
│ ├── index.ts # Barrel export
│ └── {domain}.store.ts # Pinia store
│
├── types/
│ ├── index.ts # Barrel export
│ └── {domain}.types.ts # TypeScript interfaces
│
└── index.ts # Main barrel export
Decision Matrix
Situation Location Reason Shared across multiple pages Pinia Store Global state persistence Used in single page only Composable Page-specific logic Used in single component Component ref Local state API calls features/*/api/Centralized, testable Business logic Store or Composable Reusable UI state (modals, etc.) Component ref Local, temporary Utility functions utils/Cross-domain reuse Type definitions features/*/types/ or types/Type safety
Cross-Domain Communication
Ketika satu domain perlu mengakses domain lain:
// features/05_sales/store/sales.store.ts
import { useInventoryStore } from '~/features/03_inventory'
export const useSalesStore = defineStore ( 'sales' , () => {
const inventoryStore = useInventoryStore ()
const createSale = async ( items : SaleItem []) => {
// Check inventory availability
const availableItems = items . filter ( item =>
inventoryStore . checkAvailability ( item . productId , item . quantity )
)
if ( availableItems . length !== items . length ) {
throw new Error ( 'Some items are not available' )
}
// Create sale
// ...
}
return { createSale }
})
Adding New Feature
Create Feature Folder
mkdir -p features/new_feature/{api,components,composables,store,types}
Define Types
// features/new_feature/types/index.ts
export interface NewFeature {
id : string
name : string
// ...
}
Create API Layer
// features/new_feature/api/getList.ts
export const getNewFeatureList = async () => {
return await $fetch < NewFeature []>( '/api/new-feature' )
}
Create Store
// features/new_feature/store/newFeature.store.ts
export const useNewFeatureStore = defineStore ( 'newFeature' , () => {
const items = ref < NewFeature []>([])
// ...
return { items }
})
Create Barrel Export
// features/new_feature/index.ts
export * from './api'
export * from './store'
export * from './composables'
export * from './types'
Best Practices
Store : use{Feature}Store (e.g., useInventoryStore)
Composable : use{Feature}{Action} (e.g., useInventoryList)
Component : {Feature}{Type} (e.g., InventoryTable)
API : {action}{Feature} (e.g., getInventoryList)
// Good - Use barrel exports
import { useInventoryStore , InventoryTable } from '~/features/03_inventory'
// Avoid - Direct imports
import { useInventoryStore } from '~/features/03_inventory/store/inventory.store'
Selalu define types di types/ folder
Hindari any type
Export types dari barrel export
Use strict TypeScript mode
Next Steps
12 Modules Structure Detail 12 domain modules
State Management Pinia state management patterns