enterprise_assest_managemen.../APP_DATA_FLOW.md

357 lines
12 KiB
Markdown

# App Data Flow Documentation
This document explains the complete data flow architecture of the Enterprise Asset Management application, using the Assets view as an example.
## 📊 Data Flow Architecture Overview
```
Assets View → Assets Store → Asset Repository → Base Repository → Directus API → Database
↓ ↓ ↓ ↓ ↓ ↓
User UI State Mgmt Business Logic HTTP Client REST API PostgreSQL
```
## 🔄 Step-by-Step Data Journey
### **Step 1: User Navigates to Assets View**
**File:** `/frontend/src/views/Assets.vue`
```javascript
// When component mounts
onMounted(async () => {
uiStore.setPageTitle('Assets');
await loadData(); // This starts the data flow
});
```
### **Step 2: View Calls the Assets Store**
**File:** `/frontend/src/views/Assets.vue` → Lines 427-429
```javascript
const loadData = async () => {
// Authentication happens first
await authService.ensureAuthenticated();
// Then data loading begins
await assetsStore.fetchAssets(); // 🎯 This starts the chain
await assetsStore.fetchCategories();
await assetsStore.fetchLocations();
};
```
### **Step 3: Assets Store Coordinates the Request**
**File:** `/frontend/src/stores/assets.js` → Lines 85-105
```javascript
async fetchAssets(params = {}) {
this.isLoading = true; // 📊 Update UI state
this.error = null;
try {
const queryParams = {
page: this.pagination.page,
limit: this.pagination.limit,
...params,
};
// 🔗 Delegate to repository layer
const response = await assetRepository.getAll(queryParams);
// 📦 Store data in state
this.assets = response.data || [];
this.pagination.total = response.meta?.total_count || 0;
} catch (error) {
this.error = error.message;
throw error;
} finally {
this.isLoading = false; // 📊 Update UI state
}
}
```
**🎯 Store Responsibilities:**
- **State Management** - Holds assets data in reactive state
- **Loading States** - Manages `isLoading`, `error` flags
- **Pagination** - Tracks page, limit, total count
- **Cache Invalidation** - Calls cache service when data changes
- **Error Handling** - Catches and stores error messages
### **Step 4: Asset Repository Adds Business Logic**
**File:** `/frontend/src/repositories/AssetRepository.js` → Lines 9-30
```javascript
async getAll(params = {}) {
try {
const searchParams = {
...params,
// 🔗 Define what related data to fetch
fields: [
'*', // All asset fields
'category_id.id', // Category relationship
'category_id.name',
'category_id.color',
'location_id.id', // Location relationship
'location_id.name',
'location_id.building',
'location_id.floor',
'vendor_id.id', // Vendor relationship
'vendor_id.name',
],
};
// 🔗 Delegate to base repository
return await super.getAll(searchParams);
} catch (error) {
throw this.handleError(error);
}
}
```
**🎯 Repository Responsibilities:**
- **Business Logic** - Asset-specific query parameters
- **Relationship Mapping** - Define what related data to fetch
- **Error Transformation** - Convert API errors to user-friendly messages
- **QR Code Generation** - Asset-specific features
- **Data Validation** - Business rule enforcement
### **Step 5: Base Repository Handles HTTP Communication**
**File:** `/frontend/src/repositories/BaseRepository.js` → Lines 10-18
```javascript
async getAll(params = {}) {
try {
// 🌐 Make HTTP request to Directus API
const response = await this.api.get(`/items/${this.collection}`, {
params, // Query parameters (fields, filters, pagination)
});
return response.data; // Return Directus response
} catch (error) {
throw this.handleError(error);
}
}
```
**🎯 Base Repository Responsibilities:**
- **HTTP Communication** - Axios-based API calls
- **URL Construction** - Build REST endpoints (`/items/assets`)
- **Parameter Serialization** - Convert objects to query strings
- **Response Handling** - Extract data from HTTP responses
- **Generic CRUD** - Reusable create, read, update, delete operations
### **Step 6: Directus Service Manages Authentication**
**File:** `/frontend/src/services/directus.js` → Lines 15-27
```javascript
// Request interceptor adds auth token
directusApi.interceptors.request.use(
(config) => {
const token = localStorage.getItem('directus_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`; // 🔐 Add auth
}
return config;
}
);
```
**🎯 Directus Service Responsibilities:**
- **Authentication** - Add Bearer tokens to requests
- **Token Refresh** - Automatic token renewal
- **Base URL Configuration** - API endpoint setup
- **Request/Response Interceptors** - Global middleware
- **Development Auto-auth** - Seamless development experience
### **Step 7: HTTP Request to Directus API**
```http
GET http://localhost:8055/items/assets?fields=*,category_id.id,category_id.name,category_id.color,location_id.id,location_id.name,location_id.building,location_id.floor,vendor_id.id,vendor_id.name
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
### **Step 8: Directus Queries PostgreSQL Database**
**Database Schema:** From `/schema/init.sql`
```sql
-- Directus executes this SQL query
SELECT
a.*,
c.id as "category_id.id",
c.name as "category_id.name",
c.color as "category_id.color",
l.id as "location_id.id",
l.name as "location_id.name",
l.building as "location_id.building",
l.floor as "location_id.floor",
v.id as "vendor_id.id",
v.name as "vendor_id.name"
FROM assets a
LEFT JOIN asset_categories c ON a.category_id = c.id
LEFT JOIN locations l ON a.location_id = l.id
LEFT JOIN vendors v ON a.vendor_id = v.id
WHERE a.organization_id = '70562576-ac3e-4d84-b3a6-6d0d52b8cc10';
```
### **Step 9: Database Returns Raw Data**
```json
{
"data": [
{
"id": "cd89112d-510d-4f02-acb2-bdcd6242cd2d",
"name": "Frontend Test Laptop",
"asset_identifier": "TEST-FRONTEND-1752412612",
"status": "active",
"acquisition_cost": "1200.00",
"category_id": {
"id": "4a607979-0b7c-4f04-b42a-f78616922312",
"name": "Office Furniture",
"color": "#4CAF50"
},
"location_id": {
"id": "0ead8a26-b691-45a5-be55-2ad5143f078e",
"name": "Conference Room",
"building": "Building A"
}
}
]
}
```
### **Step 10: Data Flows Back Through the Chain**
```javascript
// BaseRepository receives HTTP response
response.data HTTP Response
// AssetRepository receives structured data
return response.data BaseRepository
// Store receives and processes data
this.assets = response.data || [] AssetRepository
// Vue reactive system triggers re-render
assets.value Store (Pinia reactive state)
```
### **Step 11: Assets View Processes Data for Display**
**File:** `/frontend/src/views/Assets.vue` → Lines 333-362
```javascript
const displayedAssets = computed(() => {
// 🔄 Transform raw data for UI display
let filtered = assets.value.map(asset => ({
...asset,
// Extract nested relationship data for display
category: asset.category_id?.name || 'Unknown',
category_color: asset.category_id?.color || '#9E9E9E',
location: asset.location_id ?
`${asset.location_id.name}${asset.location_id.building ? ` - ${asset.location_id.building}` : ''}` :
'Unknown'
}));
// Apply filters (search, category, status)
if (search.value) {
const searchTerm = search.value.toLowerCase();
filtered = filtered.filter(asset =>
asset.name.toLowerCase().includes(searchTerm) ||
asset.asset_identifier.toLowerCase().includes(searchTerm)
);
}
return filtered;
});
```
### **Step 12: Vue Renders the Data**
**File:** `/frontend/src/views/Assets.vue` → Lines 104-113
```vue
<!-- Grid View -->
<v-row v-if="viewMode === 'grid'">
<v-col
v-for="asset in displayedAssets" <!-- 🎨 Reactive data renders -->
:key="asset.id"
cols="12" sm="6" md="4" lg="3"
>
<AssetCard :asset="asset" @click="viewAsset(asset.id)" />
</v-col>
</v-row>
```
## 🎯 Role Summary of Each Layer
### **1. 📄 Views/Components (Presentation Layer)**
- **User Interface** - Display data and handle user interactions
- **Event Handling** - Respond to clicks, form submissions
- **Data Transformation** - Format data for display (dates, currency, etc.)
- **Loading States** - Show spinners, empty states, errors
### **2. 🗂️ Stores (State Management Layer)**
- **Global State** - Hold application data accessible across components
- **Reactivity** - Trigger UI updates when data changes
- **Caching** - Avoid redundant API calls
- **State Coordination** - Manage loading, error, and success states
### **3. 🏢 Repositories (Business Logic Layer)**
- **Domain Logic** - Asset-specific business rules and operations
- **Data Mapping** - Transform between API and application models
- **Relationship Handling** - Define what related data to fetch
- **Validation** - Ensure data integrity and business rules
### **4. ⚡ Base Repository (Infrastructure Layer)**
- **HTTP Communication** - Generic REST API operations
- **Error Handling** - Convert HTTP errors to application errors
- **Request Configuration** - Headers, timeouts, interceptors
- **Response Processing** - Extract data from HTTP responses
### **5. 🔐 Services (Cross-Cutting Concerns)**
- **Authentication** - Token management and security
- **Caching** - Performance optimization
- **Permissions** - Access control
- **Configuration** - Environment-specific settings
## 📁 File Structure Reference
```
enterprise-asset-management/
├── frontend/
│ ├── src/
│ │ ├── views/
│ │ │ ├── Assets.vue # 📄 Presentation Layer
│ │ │ ├── AddAsset.vue
│ │ │ └── EditAsset.vue
│ │ ├── stores/
│ │ │ ├── assets.js # 🗂️ State Management
│ │ │ ├── ui.js
│ │ │ └── auth.js
│ │ ├── repositories/
│ │ │ ├── BaseRepository.js # ⚡ Infrastructure Layer
│ │ │ ├── AssetRepository.js # 🏢 Business Logic
│ │ │ └── AuthRepository.js
│ │ ├── services/
│ │ │ ├── directus.js # 🔐 API Service
│ │ │ ├── auth.js # 🔐 Auth Service
│ │ │ ├── permissions.js # 🔐 Permission Service
│ │ │ └── cache.js # 🔐 Cache Service
│ │ └── components/
│ │ └── assets/
│ │ ├── AssetForm.vue # 📄 Reusable Component
│ │ └── AssetCard.vue
├── schema/
│ └── init.sql # 🗄️ Database Schema
└── scripts/
├── setup-permissions-enhanced.sh # 🔧 Permission Setup
└── verify-permissions.sh # 🔧 Permission Verification
```
## 🔄 Data Flow Patterns
### **Create Asset Flow**
```
AssetForm → emit('submit') → AddAsset.vue → assetsStore.createAsset() →
AssetRepository.create() → BaseRepository.create() → POST /items/assets →
Database INSERT → Success Response → Cache Invalidation → UI Update
```
### **Update Asset Flow**
```
AssetForm → emit('submit') → EditAsset.vue → assetsStore.updateAsset() →
AssetRepository.update() → BaseRepository.update() → PATCH /items/assets/{id} →
Database UPDATE → Success Response → Cache Invalidation → UI Update
```
### **Authentication Flow**
```
View → authService.ensureAuthenticated() → Check localStorage token →
If expired: authService.refreshToken() → directusApi interceptor →
Add Bearer token to request headers → Continue with API call
```
This architecture provides **separation of concerns**, **testability**, **maintainability**, and **scalability** for the asset management application!