enterprise_assest_managemen.../APP_DATA_FLOW.md

12 KiB

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

// 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

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

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

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

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

// 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

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

-- 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

{
  "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

// 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

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

<!-- 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!