@@ -2044,3 +2044,207 @@ shared/ (Purple Background)
20442044- [ ] Document team guidelines and examples
20452045
20462046**Result**: A visually clear, maintainable modular architecture that scales with your team and project complexity.
2047+
2048+ ## File Creation Decision Matrix for Global Features
2049+
2050+ When implementing new global features, follow this decision matrix to ensure consistent architecture:
2051+
2052+ | File Type | When to Create | Responsibilities | What Should NOT Go Inside |
2053+ | --------------------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
2054+ | **`src/entities/<Feature>.ts`** | **Always** | • Zod schema definition; • Type exports; • Field-level validation rules; • Schema-derived utilities | • RPC calls; • Data transformation logic; • Business rules; • Vue composables |
2055+ | **`src/services/<feature>.ts`** | **Always** | • RPC/API calls; • Response → domain mapping; • Validation using entity schema; • Caching (export `cached` ref); • Error handling | • Vue composables; • UI-specific logic; • Component imports |
2056+ | **`src/services/<feature>.helpers.ts`** | **When business logic is complex** | • Pure transformation functions; • Business rules (if/then logic); • Data normalization; • Cross-field validations | • Vue reactivity; • RPC calls; • Side effects |
2057+ | **`src/composables/use<Feature>.ts`** | **Always** | • Reactive wrapper around service; • `load()` method calling service; • UI-friendly computed refs; • Vue lifecycle hooks | • RPC calls; • Data transformation; • Business logic |
2058+ | **`src/stores/use<Feature>Store.ts`** | **When you need cross-component state** | • Pinia store actions; • UI state management; • Service orchestration; • Loading/error states | • RPC calls (delegate to service); • Business logic (delegate to service) |
2059+
2060+ ### Layer Import Rules
2061+
2062+ To maintain clean architecture, follow these import restrictions:
2063+
2064+ - ✅ **Services** can import: `entities`, `helpers`
2065+ - ✅ **Composables** can import: `services` (cached refs only)
2066+ - ✅ **Stores** can import: `services`
2067+ - ❌ **Services** cannot import: `composables`, `stores`
2068+ - ❌ **Entities** cannot import: anything except other entities
2069+
2070+ ### Quick Decision Flow
2071+
2072+ For any new feature, ask:
2073+
2074+ 1. **"Do I need a data contract?"** → Create entity
2075+ 2. **"Do I need to fetch/process data?"** → Create service
2076+ 3. **"Is the processing logic complex?"** → Create service helpers
2077+ 4. **"Do I need reactive UI state?"** → Create composable
2078+ 5. **"Do I need cross-component state management?"** → Create store
2079+
2080+ **Always create**: Entity + Service + Composable
2081+ **Sometimes create**: Helpers + Store
2082+
2083+ ### Example Implementation
2084+
2085+ ```typescript
2086+ // src/entities/SystemDetails.ts - Data contract only
2087+ export const SystemDetailsSchema = z.object({
2088+ 'product-name': z.string(),
2089+ 'hw-version': z.string(),
2090+ })
2091+ export type SystemDetails = z.infer<typeof SystemDetailsSchema>
2092+
2093+ // src/services/systemDetails.ts - I/O + orchestration
2094+ export const cachedDetails = ref<SystemDetails | null>(null)
2095+ export const systemDetailsService = {
2096+ async fetchDetails(th: string | number): Promise<SystemDetails> {
2097+ // RPC call, validation, caching
2098+ },
2099+ }
2100+
2101+ // src/composables/useSystemDetails.ts - UI wrapper
2102+ export const useSystemDetails = createGlobalState(() => {
2103+ const systemDetails = computed(() => cachedDetails.value)
2104+ const loadValues = async () => {
2105+ await systemDetailsService.fetchDetails(session.value.th)
2106+ }
2107+ return { systemDetails, loadValues }
2108+ })
2109+ ```
2110+
2111+ ## File Creation Decision Matrix for Module/Feature Implementation
2112+
2113+ When implementing features within specific modules (under `src/modules/<module-name>/`), follow this decision matrix:
2114+
2115+ | File Type | When to Create | Responsibilities | What Should NOT Go Inside |
2116+ | --------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
2117+ | **`types.ts`** | **Always** | • Zod schemas for module data; • TypeScript type definitions; • Module-specific validation rules; • Type exports for public API | • Business logic; • API calls; • Vue composables; • Component logic |
2118+ | **`services/<feature>.ts`** | **Always** | • Module-specific API calls; • RPC/HTTP requests; • Response transformation; • Error handling; • Request/response mapping | • Vue reactivity; • UI logic; • Component imports; • Global state management |
2119+ | **`services/helpers.ts`** | **When API logic is complex** | • Pure transformation functions; • Request/response mapping; • Data normalization; • Validation helpers | • Vue composables; • Side effects; • Global state; • UI concerns |
2120+ | **`composables/use<Feature>.ts`** | **When UI needs reactive state** | • Module-specific reactive logic; • Local state management; • UI lifecycle hooks; • Form handling | • Direct API calls (use services/ instead); • Global state; • Cross-module dependencies |
2121+ | **`components/<Component>.vue`** | **For reusable components** | • Presentation logic; • User interactions; • Local component state; • Props/events handling | • API calls; • Business logic; • Cross-module imports; • Global state mutations |
2122+ | **`views/<View>.vue`** | **For page-level views** | • Page layout and structure; • Route handling; • Composition of components; • Page-specific state | • Direct API calls; • Business logic; • Reusable component logic |
2123+ | **`index.ts`** | **Always** | • Module public API exports; • External interface definition; • Re-exports of public components/composables | • Implementation details; • Internal utilities; • Private components |
2124+
2125+ ### Module Layer Import Rules
2126+
2127+ Within modules, follow these import restrictions:
2128+
2129+ - ✅ **`services/`** can import: `types.ts`, `services/helpers.ts`, global `@/entities`
2130+ - ✅ **`composables/`** can import: `services/`, `types.ts`
2131+ - ✅ **`components/`** can import: `composables/`, `types.ts`, global `@/shared/components`
2132+ - ✅ **`views/`** can import: `components/`, `composables/`, `types.ts`
2133+ - ✅ **`index.ts`** can import: any file within the module (for re-export)
2134+ - ❌ **`services/`** cannot import: `composables/`, `components/`, `views/`
2135+ - ❌ **`types.ts`** cannot import: anything except other types and global entities
2136+ - ❌ **Modules** cannot import: internal files from other modules (only via `index.ts`)
2137+
2138+ ### Module Decision Flow
2139+
2140+ For any new module feature, ask:
2141+
2142+ 1. **"Do I need module-specific types?"** → Create/update `types.ts`
2143+ 2. **"Do I need to fetch external data?"** → Create `services/<feature>.ts`
2144+ 3. **"Is the API logic complex?"** → Create `services/helpers.ts`
2145+ 4. **"Do I need reactive UI state?"** → Create `composables/use<Feature>.ts`
2146+ 5. **"Do I need reusable components?"** → Create `components/<Component>.vue`
2147+ 6. **"Do I need page views?"** → Create `views/<View>.vue`
2148+ 7. **"Should this be available to other modules?"** → Export via `index.ts`
2149+
2150+ ### Module Structure Example
2151+
2152+ ```plaintext
2153+ src/modules/port-configuration/
2154+ ├── types.ts # Port-specific schemas and types
2155+ ├── services/
2156+ │ ├── ports.ts # Port CRUD operations
2157+ │ ├── configurations.ts # Configuration API calls
2158+ │ └── helpers.ts # API transformation utilities
2159+ ├── composables/
2160+ │ ├── usePortEditor.ts # Port editing state
2161+ │ └── usePortValidation.ts # Port validation logic
2162+ ├── components/
2163+ │ ├── PortList.vue # Port listing component
2164+ │ ├── PortEditor.vue # Port editing form
2165+ │ └── PortStatus.vue # Port status display
2166+ ├── views/
2167+ │ ├── PortConfigurationView.vue # Main port configuration page
2168+ │ └── PortDetailsView.vue # Individual port details page
2169+ └── index.ts # Public API exports
2170+ ```
2171+
2172+ ### Example Module Implementation
2173+
2174+ ```typescript
2175+ // src/modules/port-configuration/types.ts
2176+ export const PortSchema = z.object({
2177+ id: z.string(),
2178+ name: z.string(),
2179+ status: z.enum(['active', 'inactive']),
2180+ })
2181+ export type Port = z.infer<typeof PortSchema>
2182+
2183+ // src/modules/port-configuration/services/ports.ts
2184+ import { PortSchema, type Port } from '../types'
2185+
2186+ export const portsService = {
2187+ async fetchPorts(): Promise<Port[]> {
2188+ const response = await fetchJsonRpc({ method: 'get_ports' })
2189+ return response.map((port) => PortSchema.parse(port))
2190+ },
2191+
2192+ async updatePort(port: Port): Promise<void> {
2193+ await fetchJsonRpc({ method: 'update_port', params: port })
2194+ },
2195+ }
2196+
2197+ // src/modules/port-configuration/composables/usePortEditor.ts
2198+ import { ref } from 'vue'
2199+ import { portsService } from '../services/ports'
2200+ import type { Port } from '../types'
2201+
2202+ export function usePortEditor() {
2203+ const selectedPort = ref<Port | null>(null)
2204+ const loading = ref(false)
2205+
2206+ const loadPort = async (id: string) => {
2207+ loading.value = true
2208+ try {
2209+ const ports = await portsService.fetchPorts()
2210+ selectedPort.value = ports.find((p) => p.id === id) || null
2211+ } finally {
2212+ loading.value = false
2213+ }
2214+ }
2215+
2216+ return { selectedPort, loading, loadPort }
2217+ }
2218+
2219+ // src/modules/port-configuration/index.ts - Public API
2220+ export type { Port } from './types'
2221+ export { usePortEditor } from './composables/usePortEditor'
2222+ export { default as PortList } from './components/PortList.vue'
2223+ export { default as PortEditor } from './components/PortEditor.vue'
2224+ export { default as PortConfigurationView } from './views/PortConfigurationView.vue'
2225+ ```
2226+
2227+ ### Cross-Module Communication
2228+
2229+ When modules need to communicate:
2230+
2231+ ```typescript
2232+ // ✅ CORRECT - Import via public API
2233+ import { usePortEditor, type Port } from '@/modules/port-configuration'
2234+
2235+ // ❌ WRONG - Direct import of internal files
2236+ import { usePortEditor } from '@/modules/port-configuration/composables/usePortEditor'
2237+ ```
2238+
2239+ ### Module vs Global Decision
2240+
2241+ | Scope | Use Module Files | Use Global Files |
2242+ | --------------------------- | ----------------------------------------------------------- | --------------------------------- |
2243+ | **Feature-specific** | Module `services/`, `composables/`, `components/`, `views/` | Never |
2244+ | **Reusable across modules** | Export via `index.ts` then decide | Often better as global |
2245+ | **Shared types/entities** | If module-specific | Global `src/entities/` |
2246+ | **Common services** | If module-specific | Global `src/services/` |
2247+ | **UI components** | If module-specific | Global `src/shared/components/` |
2248+ | **Page views** | Always module-specific | Never (views are module-specific) |
2249+
2250+ This structure ensures modules are self-contained while maintaining clean interfaces for cross-module communication.
0 commit comments