Skip to content

Technology Stack & Component Architecture

Overview

This document provides a comprehensive mapping of all application components to their selected technologies, integration patterns, and implementation guidelines. It serves as the authoritative reference for technology decisions across Pomp's Dispatch Center Application.


Table of Contents


Technology Summary

Full Stack Overview

flowchart TB
    subgraph presentation["PRESENTATION LAYER"]
        web["WEB (Vue.js 3)
        • TypeScript
        • Tailwind CSS
        • Vue I18n
        • Azure Maps SDK
        • SignalR Client
        • Power BI Embedded"]

        mobile["MOBILE (React Native)
        • TypeScript
        • SQLite (Offline)
        • react-i18next
        • React Native Maps
        • Azure Notification Hubs SDK"]
    end

    subgraph gateway["API GATEWAY LAYER"]
        api_gateway["Azure API Management • OAuth 2.0 • Rate Limiting • Caching"]
    end

    subgraph application["APPLICATION LAYER"]
        dotnet[".NET 10
        ASP.NET Core Web API
        SignalR
        Entity Framework"]

        rules["Rules Engine
        Microsoft RulesEngine
        FluentValidation"]

        ai["AI Services
        Azure OpenAI
        AI Vision
        Bot Service"]
    end

    subgraph messaging["MESSAGING LAYER"]
        service_bus["Service Bus
        Events
        Commands
        Dead Letter"]

        signalr["SignalR Service
        Real-time
        WebSocket"]

        notification["Notification Hubs
        Push (iOS)
        Push (Android)"]
    end

    subgraph data["DATA LAYER"]
        sql["Azure SQL Database
        Master Data
        Transactional
        Audit Logs"]

        redis["Redis Cache
        Session
        API Cache
        Reference"]

        blob["Blob Storage
        Photos
        Signatures
        Documents"]
    end

    subgraph integration["INTEGRATION LAYER"]
        maddenco["MaddenCo Proxy API"]
        reach["REACH Proxy API"]
        dayforce["Dayforce Proxy API"]
        genesys["Genesys Proxy API"]
        geotab["GeoTab API"]
    end

    subgraph observability["OBSERVABILITY"]
        monitoring["App Insights • Log Analytics • Azure Monitor • Power BI • Serilog"]
    end

    subgraph security["SECURITY"]
        sec["Azure AD (Entra ID) • Key Vault • WAF • Network Security Groups"]
    end

    presentation --> gateway
    gateway --> application
    application --> messaging
    messaging --> data
    application --> integration

Quick Reference Table

Layer Component Technology Version
Backend Application Framework .NET 10.0
Backend Web API ASP.NET Core 10.0
Backend ORM Entity Framework Core 10.0
Backend Rules Engine Microsoft.RulesEngine 5.x
Backend Validation FluentValidation 11.x
Frontend (Web) Framework Vue.js 3.x
Frontend (Web) Language TypeScript 5.x
Frontend (Web) Styling Tailwind CSS 3.x
Frontend (Web) i18n Vue I18n 9.x
Frontend (Web) Maps Azure Maps Web SDK Latest
Frontend (Web) Charts Chart.js / ApexCharts Latest
Mobile Framework React Native 0.73+
Mobile Language TypeScript 5.x
Mobile Offline DB SQLite (react-native-sqlite-storage) Latest
Mobile i18n react-i18next Latest
Mobile Maps react-native-maps Latest
Database Primary Azure SQL Database Latest
Database Cache Azure Cache for Redis 6.x
Storage Files Azure Blob Storage Latest
Messaging Async Azure Service Bus Premium
Messaging Real-time Azure SignalR Service Latest
Messaging Push Azure Notification Hubs Latest
AI/ML LLM Azure OpenAI GPT-4o
AI/ML Vision Azure AI Vision 4.0
AI/ML Bot Azure Bot Service Latest
Analytics BI Power BI Embedded Latest
Analytics APM Azure Application Insights Latest
Security Identity Azure AD (Entra ID) Latest
Security Secrets Azure Key Vault Latest
Security Gateway Azure API Management Latest
Infrastructure IaC Terraform 1.x
Infrastructure CI/CD Atlassian Bamboo Latest
Infrastructure Source Control Atlassian Bitbucket Latest

Data Storage & Management

Primary Database: Azure SQL Database

Aspect Configuration Notes
Service Tier Business Critical Geo-redundant, zone-redundant
Max Size 4 TB Auto-grow enabled
Compute 8 vCores Auto-scale based on load
Backup Point-in-time (35 days) Geo-redundant backup storage
Encryption TDE (AES-256) Always encrypted for PII

Data Categories

Category Description Retention Encryption
Master Data Customers, Stores, Technicians, Parts Indefinite Standard TDE
Transactional Data Work Orders, Invoices, Payments 7 years Standard TDE
Audit Data User actions, system events 7 years Standard TDE
Configuration System settings, rules, templates Indefinite Standard TDE
PII Contact info, addresses, phone numbers Per policy Always Encrypted

Caching: Azure Cache for Redis

Aspect Configuration Use Case
Tier Premium P1 Production workload
Size 6 GB Session + API cache
Clustering Enabled Horizontal scaling
Geo-Replication Enabled DR support

Cache Patterns

public interface ICacheService
{
    Task<T?> GetAsync<T>(string key);
    Task SetAsync<T>(string key, T value, TimeSpan? expiration = null);
    Task RemoveAsync(string key);
    Task<T> GetOrSetAsync<T>(string key, Func<Task<T>> factory, TimeSpan? expiration = null);
}

// Cache key patterns
public static class CacheKeys
{
    public static string Customer(string customerId) => $"customer:{customerId}";
    public static string Store(string storeId) => $"store:{storeId}";
    public static string Technician(string techId) => $"tech:{techId}";
    public static string WorkOrder(string workOrderId) => $"wo:{workOrderId}";
    public static string ReferenceData(string type) => $"ref:{type}";
}

Master Data Sync & Reconciliation

Sync Type Technology Frequency Source → Target
Customer Master Azure Data Factory Every 15 min MaddenCo → Pomp's
Technician Data Azure Data Factory Every 30 min Dayforce → Pomp's
Parts/Inventory Azure Service Bus Real-time MaddenCo → Pomp's
Work Orders Azure Service Bus Real-time (events) Bidirectional
Billing Data Azure Data Factory Hourly MaddenCo → Pomp's

Reconciliation Pattern

public class ReconciliationService
{
    public async Task<ReconciliationResult> ReconcileAsync(
        string dataType,
        DateTimeOffset fromDate,
        DateTimeOffset toDate)
    {
        // 1. Fetch records from source system
        var sourceRecords = await _sourceProxy.GetRecordsAsync(dataType, fromDate, toDate);

        // 2. Fetch records from local database
        var localRecords = await _repository.GetRecordsAsync(dataType, fromDate, toDate);

        // 3. Compare and identify discrepancies
        var discrepancies = CompareRecords(sourceRecords, localRecords);

        // 4. Apply conflict resolution based on Source of Truth
        var resolutions = await ResolveConflictsAsync(discrepancies, dataType);

        // 5. Log reconciliation results
        await _auditService.LogReconciliationAsync(dataType, resolutions);

        return new ReconciliationResult(resolutions);
    }
}

Documentation Reference: INTEGRATION_ARCHITECTURE.md


Business Logic & Rules Engine

Microsoft RulesEngine

Package: Microsoft.RulesEngine (NuGet)

The Rules Engine enables externalized, configurable business rules that can be modified without code deployment.

Use Cases

Rule Category Examples Configuration
Customer SLA Response time by customer tier, escalation thresholds JSON/Database
Store Assignments Default technician assignments, service area mapping JSON/Database
Work Order Routing Priority calculation, auto-assignment rules JSON/Database
Documentation Requirements Required photos by service type, signature requirements JSON/Database
Billing Rules Pricing tiers, discount calculations, tax rules JSON/Database

Implementation Pattern

// Rule definition (stored in database/JSON)
{
    "WorkflowName": "CustomerSLAWorkflow",
    "Rules": [
        {
            "RuleName": "PlatinumCustomerSLA",
            "Expression": "customer.Tier == \"Platinum\" AND workOrder.Priority == \"Emergency\"",
            "SuccessEvent": "SLA_1_HOUR",
            "Actions": {
                "OnSuccess": {
                    "Name": "SetSLAMinutes",
                    "Context": { "Minutes": 60 }
                }
            }
        },
        {
            "RuleName": "StandardCustomerSLA",
            "Expression": "customer.Tier == \"Standard\"",
            "SuccessEvent": "SLA_4_HOURS",
            "Actions": {
                "OnSuccess": {
                    "Name": "SetSLAMinutes",
                    "Context": { "Minutes": 240 }
                }
            }
        }
    ]
}

// C# Usage
public class SLAService
{
    private readonly RulesEngine.RulesEngine _rulesEngine;

    public async Task<SLAResult> CalculateSLAAsync(Customer customer, WorkOrder workOrder)
    {
        var inputs = new[] {
            new RuleParameter("customer", customer),
            new RuleParameter("workOrder", workOrder)
        };

        var results = await _rulesEngine.ExecuteAllRulesAsync("CustomerSLAWorkflow", inputs);

        return MapToSLAResult(results);
    }
}

Rule Configuration Storage

CREATE TABLE BusinessRules (
    Id UNIQUEIDENTIFIER PRIMARY KEY,
    WorkflowName NVARCHAR(100) NOT NULL,
    RuleDefinition NVARCHAR(MAX) NOT NULL, -- JSON
    Version INT NOT NULL,
    IsActive BIT NOT NULL DEFAULT 1,
    EffectiveFrom DATETIME2 NOT NULL,
    EffectiveTo DATETIME2 NULL,
    CreatedBy NVARCHAR(100) NOT NULL,
    CreatedAt DATETIME2 NOT NULL,
    ModifiedBy NVARCHAR(100) NULL,
    ModifiedAt DATETIME2 NULL
);

FluentValidation

Package: FluentValidation (NuGet)

Used for input validation across all API endpoints.

public class WorkOrderValidator : AbstractValidator<CreateWorkOrderRequest>
{
    public WorkOrderValidator()
    {
        RuleFor(x => x.CustomerId)
            .NotEmpty().WithMessage("Customer is required");

        RuleFor(x => x.ServiceType)
            .NotEmpty().WithMessage("Service type is required")
            .Must(BeValidServiceType).WithMessage("Invalid service type");

        RuleFor(x => x.Priority)
            .IsInEnum().WithMessage("Invalid priority level");

        RuleFor(x => x.ScheduledDate)
            .GreaterThan(DateTime.UtcNow).WithMessage("Scheduled date must be in the future");

        RuleFor(x => x.ContactPhone)
            .Matches(@"^\+?[1-9]\d{1,14}$").WithMessage("Invalid phone number format");
    }
}

Store Assignments

public class StoreAssignmentService
{
    private readonly RulesEngine.RulesEngine _rulesEngine;
    private readonly IStoreRepository _storeRepository;

    public async Task<Store> DetermineStoreAsync(WorkOrder workOrder)
    {
        // 1. Get customer's primary store assignment
        var primaryStore = await _storeRepository.GetPrimaryStoreForCustomerAsync(workOrder.CustomerId);

        // 2. Check store capacity and availability
        var storeCapacity = await _storeRepository.GetCurrentCapacityAsync(primaryStore.Id);

        // 3. Execute assignment rules
        var inputs = new[] {
            new RuleParameter("workOrder", workOrder),
            new RuleParameter("primaryStore", primaryStore),
            new RuleParameter("capacity", storeCapacity)
        };

        var results = await _rulesEngine.ExecuteAllRulesAsync("StoreAssignmentWorkflow", inputs);

        return MapToAssignedStore(results);
    }
}

Tech Assignment / On-Call

Integration with Dayforce for technician scheduling and on-call rotations.

public class TechnicianAssignmentService
{
    private readonly IDayforceProxy _dayforceProxy;
    private readonly ITechnicianRepository _techRepository;
    private readonly RulesEngine.RulesEngine _rulesEngine;

    public async Task<TechnicianAssignment> AssignTechnicianAsync(WorkOrder workOrder)
    {
        // 1. Get available technicians from Dayforce schedule
        var availableTechs = await _dayforceProxy.GetAvailableTechniciansAsync(
            workOrder.StoreId,
            workOrder.ScheduledDate);

        // 2. Filter by skills/certifications
        var qualifiedTechs = availableTechs
            .Where(t => t.Certifications.Contains(workOrder.RequiredCertification))
            .ToList();

        // 3. Check on-call status if after hours
        if (IsAfterHours(workOrder.ScheduledDate))
        {
            var onCallTech = await _dayforceProxy.GetOnCallTechnicianAsync(
                workOrder.StoreId,
                workOrder.ScheduledDate);
            qualifiedTechs.Insert(0, onCallTech);
        }

        // 4. Apply assignment rules (proximity, workload, skills match)
        var inputs = new[] {
            new RuleParameter("workOrder", workOrder),
            new RuleParameter("technicians", qualifiedTechs)
        };

        var results = await _rulesEngine.ExecuteAllRulesAsync("TechAssignmentWorkflow", inputs);

        return MapToAssignment(results);
    }
}

Documentation Reference: INTEGRATION_ARCHITECTURE.md, DISPATCH_ALERTS.md


Business Process Engine & AI

Workflow Management

Azure Service Bus provides the foundation for event-driven workflow orchestration.

Intake Workflow

flowchart LR
    reach["REACH
    Portal"]
    service_bus["Service
    Bus"]
    intake["Intake
    Processor"]
    assignment["Assignment
    Engine"]

    events["Events:
    • NewOrder
    • Update
    • Cancel"]

    actions_intake["Actions:
    • Validate
    • Enrich
    • Store"]

    actions_assign["Actions:
    • SLA Calc
    • Assign
    • Notify"]

    reach --> service_bus
    service_bus --> intake
    intake --> assignment
    service_bus --> events
    intake --> actions_intake
    assignment --> actions_assign

Assignment Workflow

public class AssignmentOrchestrator
{
    private readonly IServiceBus _serviceBus;
    private readonly ITechnicianAssignmentService _assignmentService;
    private readonly INotificationService _notificationService;

    public async Task ProcessAssignmentAsync(WorkOrder workOrder)
    {
        // 1. Calculate SLA based on customer and priority
        var sla = await _slaService.CalculateSLAAsync(workOrder);

        // 2. Find best technician match
        var assignment = await _assignmentService.AssignTechnicianAsync(workOrder);

        // 3. Update work order
        workOrder.AssignedTechnicianId = assignment.TechnicianId;
        workOrder.SLADeadline = sla.Deadline;
        await _workOrderRepository.UpdateAsync(workOrder);

        // 4. Publish events
        await _serviceBus.PublishAsync(new WorkOrderAssignedEvent(workOrder, assignment));

        // 5. Send notifications
        await _notificationService.NotifyTechnicianAsync(assignment);
        await _notificationService.NotifyCustomerAsync(workOrder, assignment.ETA);
    }
}

Azure OpenAI Integration

Model: GPT-4o (via Azure OpenAI Service)

Use Case Description Input Output
Smart Assignment Suggest optimal technician based on skills, proximity, workload Work order details, tech list Ranked tech recommendations
Customer Communication Generate professional customer notifications Context, template Personalized message
Issue Triage Analyze customer description to determine service type Customer description Service type, priority suggestion
Knowledge Base Answer technician questions about procedures Question + context Procedure guidance
public class AzureOpenAIService
{
    private readonly OpenAIClient _client;

    public async Task<TechnicianRecommendation[]> GetSmartAssignmentAsync(
        WorkOrder workOrder,
        IEnumerable<Technician> availableTechnicians)
    {
        var prompt = BuildAssignmentPrompt(workOrder, availableTechnicians);

        var response = await _client.GetChatCompletionsAsync(
            deploymentName: "gpt-4o",
            new ChatCompletionsOptions
            {
                Messages = { new ChatRequestUserMessage(prompt) },
                Temperature = 0.3f,
                MaxTokens = 500
            });

        return ParseRecommendations(response);
    }
}

Azure AI Vision (Photo Analysis)

Service: Azure AI Vision 4.0

Use Case Description Trigger
Tire Condition Analysis Assess tread depth, damage, wear patterns Photo upload
Damage Assessment Identify and categorize vehicle/equipment damage Before/after photos
Parts Verification Verify correct parts installed Completion photos
Quality Control Validate work quality from photos Completion photos
public class PhotoAnalysisService
{
    private readonly ImageAnalysisClient _visionClient;
    private readonly OpenAIClient _openAIClient;

    public async Task<TireConditionReport> AnalyzeTirePhotoAsync(Stream photoStream)
    {
        // 1. Extract visual features using Azure AI Vision
        var analysisResult = await _visionClient.AnalyzeAsync(
            BinaryData.FromStream(photoStream),
            VisualFeatures.Tags | VisualFeatures.Objects | VisualFeatures.Caption);

        // 2. Use GPT-4o Vision for detailed analysis
        var visionPrompt = BuildTireAnalysisPrompt(analysisResult);

        var openAIResponse = await _openAIClient.GetChatCompletionsAsync(
            deploymentName: "gpt-4o",
            new ChatCompletionsOptions
            {
                Messages = {
                    new ChatRequestUserMessage(
                        new ChatMessageImageContentItem(photoUri),
                        new ChatMessageTextContentItem(visionPrompt))
                },
                Temperature = 0.2f
            });

        return ParseTireConditionReport(openAIResponse);
    }
}

Azure Bot Service

Purpose: Conversational interface for customers and internal users via Genesys Cloud CCaaS integration.

Bot Capability Description Channel
Status Inquiry "Where is my technician?" SMS, Voice IVR
Appointment Scheduling Schedule/reschedule service Web Chat, SMS
FAQ/Knowledge Base Answer common questions All channels
Escalation to Agent Transfer to human agent All channels
public class DispatchBot : ActivityHandler
{
    private readonly IWorkOrderService _workOrderService;
    private readonly OpenAIClient _openAIClient;

    protected override async Task OnMessageActivityAsync(
        ITurnContext<IMessageActivity> turnContext,
        CancellationToken cancellationToken)
    {
        var userMessage = turnContext.Activity.Text;

        // Determine intent using Azure OpenAI
        var intent = await DetermineIntentAsync(userMessage);

        switch (intent)
        {
            case "StatusInquiry":
                var status = await _workOrderService.GetLatestStatusForCustomerAsync(
                    turnContext.Activity.From.Id);
                await turnContext.SendActivityAsync(BuildStatusResponse(status));
                break;

            case "Schedule":
                await StartSchedulingDialogAsync(turnContext);
                break;

            case "EscalateToAgent":
                await TransferToGenesysAgentAsync(turnContext);
                break;

            default:
                await HandleGeneralInquiryAsync(turnContext, userMessage);
                break;
        }
    }
}

Documentation Reference: INTEGRATION_ARCHITECTURE.md


User Interfaces

Web Application (Vue.js 3)

View Description Key Components
Customer View Customer relationship management Profile, History, Billing, Communication
Store View Store-level operations Dashboard, Technicians, Work Orders, Alerts
Billing View Invoice and payment management Invoice List, Detail, MaddenCo Sync
Dispatch View Central dispatch command center Map, Queue, Assignments, Real-time Feed
Configuration View System administration Rules, Templates, Users, Settings

Vue.js Project Structure

src/
├── assets/                    # Static assets
├── components/                # Shared components
│   ├── common/               # Buttons, inputs, modals
│   ├── layout/               # Header, sidebar, footer
│   ├── data/                 # Tables, grids, charts
│   └── maps/                 # Azure Maps components
├── composables/               # Shared composition functions
│   ├── useAuth.ts
│   ├── useApi.ts
│   ├── useRealtime.ts
│   └── useI18n.ts
├── views/                     # Page-level components
│   ├── customer/
│   ├── store/
│   ├── billing/
│   ├── dispatch/
│   └── configuration/
├── stores/                    # Pinia stores
│   ├── auth.ts
│   ├── workOrders.ts
│   └── notifications.ts
├── services/                  # API services
├── types/                     # TypeScript types
├── i18n/                      # Internationalization
│   ├── en.json
│   └── es.json
└── router/                    # Vue Router config

Key Dependencies

{
  "dependencies": {
    "vue": "^3.4.0",
    "vue-router": "^4.2.0",
    "pinia": "^2.1.0",
    "vue-i18n": "^9.8.0",
    "azure-maps-control": "^3.0.0",
    "@microsoft/signalr": "^8.0.0",
    "powerbi-client-vue-js": "^1.0.0",
    "chart.js": "^4.4.0",
    "vue-chartjs": "^5.3.0",
    "@vueuse/core": "^10.7.0",
    "axios": "^1.6.0"
  }
}

Mobile Application (React Native)

Feature Description Offline Support
Work Order List Today's assignments ✅ SQLite cache
Work Order Detail Full service details ✅ SQLite cache
Navigation Turn-by-turn directions ✅ Cached routes
Photo Capture Before/after photos ✅ Local queue
Signature Capture Customer signatures ✅ Local storage
Time Tracking Clock in/out ✅ Local queue
Parts Scanning Barcode scanning ✅ Offline lookup

React Native Project Structure

src/
├── components/                # Shared components
│   ├── common/
│   ├── forms/
│   └── maps/
├── screens/                   # Screen components
│   ├── WorkOrderListScreen.tsx
│   ├── WorkOrderDetailScreen.tsx
│   ├── NavigationScreen.tsx
│   ├── CameraScreen.tsx
│   ├── SignatureScreen.tsx
│   └── ProfileScreen.tsx
├── services/                  # Business logic
│   ├── api/
│   ├── sync/
│   ├── offline/
│   └── location/
├── stores/                    # State management
├── hooks/                     # Custom hooks
├── i18n/                      # Translations
│   ├── en.json
│   └── es.json
├── database/                  # SQLite schema/queries
└── types/                     # TypeScript types

Key Dependencies

{
  "dependencies": {
    "react-native": "0.73.x",
    "react-native-sqlite-storage": "^6.0.0",
    "react-native-maps": "^1.8.0",
    "react-native-camera": "^4.2.0",
    "react-native-signature-capture": "^0.4.0",
    "react-i18next": "^13.5.0",
    "i18next": "^23.7.0",
    "@react-native-async-storage/async-storage": "^1.21.0",
    "@notifee/react-native": "^7.8.0",
    "react-native-vision-camera": "^3.6.0"
  }
}

Configuration View

Administrative interface for system configuration.

Section Features Access
Business Rules View/edit rule definitions, versioning System Admin
Form Templates Create/edit dynamic forms System Admin, Store Manager
User Management User accounts, role assignments System Admin
Store Configuration Store settings, service areas System Admin, Store Manager
SLA Configuration Customer tier SLAs, escalation rules System Admin
Notification Templates SMS/Email templates System Admin
Integration Settings API configurations, sync schedules System Admin

Exception Management

Integrated into Dispatch View with dedicated exception queue.

Exception Type Handling Notification
SLA Breach Auto-escalate, require acknowledgment Alert + Email
Sync Failure Retry queue, manual intervention Dashboard alert
Assignment Failure Manual assignment required Dispatcher alert
Payment Failure Flag for review Billing alert
Integration Error Circuit breaker, fallback Ops team alert

Forms Builder / Templates

Dynamic form system for configurable work order documentation.

Feature Technology Storage
Form Designer Vue.js + FormKit Azure SQL
Field Types Text, Number, Date, Photo, Signature, Dropdown, Checkbox JSON schema
Conditional Logic Show/hide based on values Rules Engine
Validation Required, patterns, ranges FluentValidation
Offline Forms React Native + SQLite Local DB
// Form schema example
interface FormTemplate {
  id: string;
  name: string;
  serviceTypes: string[];  // Which service types use this form
  sections: FormSection[];
  validationRules: ValidationRule[];
}

interface FormSection {
  id: string;
  title: string;
  fields: FormField[];
  conditions?: ConditionalRule[];
}

interface FormField {
  id: string;
  type: 'text' | 'number' | 'date' | 'photo' | 'signature' | 'select' | 'checkbox';
  label: string;
  required: boolean;
  options?: SelectOption[];  // For dropdowns
  validation?: FieldValidation;
}

Documentation Reference: VIEWS.md


Notifications

Notification Channels

Channel Technology Use Cases Delivery
SMS Genesys Cloud (Pomp's Proxy) Customer ETA, tech alerts Real-time
Voice Call Genesys Cloud (Pomp's Proxy) Urgent escalations, SLA breaches Real-time
Email Azure Communication Services Invoices, summaries, reports Batched/Real-time
Web Push Azure SignalR Service Dispatcher alerts, status updates Real-time
Mobile Push Azure Notification Hubs Tech assignments, schedule changes Real-time
In-App SignalR + Vue/React All real-time updates Real-time

Azure Communication Services (Email)

public class EmailNotificationService
{
    private readonly EmailClient _emailClient;
    private readonly ITemplateService _templateService;

    public async Task SendInvoiceEmailAsync(Invoice invoice, Customer customer)
    {
        var template = await _templateService.GetTemplateAsync("InvoiceEmail", customer.PreferredLanguage);
        var content = await _templateService.RenderAsync(template, new { invoice, customer });

        var message = new EmailMessage(
            senderAddress: "dispatch@pomps.com",
            recipientAddress: customer.Email,
            content: new EmailContent(template.Subject) { Html = content });

        await _emailClient.SendAsync(WaitUntil.Started, message);
    }
}

Azure Notification Hubs (Mobile Push)

public class PushNotificationService
{
    private readonly NotificationHubClient _hubClient;

    public async Task SendTechnicianAssignmentAsync(Technician tech, WorkOrder workOrder)
    {
        var notification = new FcmNotification(JsonSerializer.Serialize(new
        {
            notification = new
            {
                title = "New Assignment",
                body = $"Work Order {workOrder.Number} assigned to you"
            },
            data = new
            {
                workOrderId = workOrder.Id,
                action = "VIEW_WORK_ORDER"
            }
        }));

        await _hubClient.SendNotificationAsync(notification, $"tech:{tech.Id}");
    }
}

Notification Template Engine

public interface INotificationTemplate
{
    string Id { get; }
    string Channel { get; }  // SMS, Email, Push
    string Language { get; }  // en, es
    string Subject { get; }
    string BodyTemplate { get; }
    Dictionary<string, string> Variables { get; }
}

// Example template
{
    "id": "tech_assignment_sms",
    "channel": "SMS",
    "language": "en",
    "bodyTemplate": "New assignment: {{workOrderNumber}} at {{customerName}}, {{address}}. Scheduled: {{scheduledTime}}. Reply ACCEPT or DECLINE."
}

Documentation Reference: DISPATCH_ALERTS.md, INTEGRATION_ARCHITECTURE.md


Audit & Logging

Logging Framework: Serilog

Log Type Destination Retention Format
Application Logs Azure Log Analytics 90 days JSON structured
Audit Logs Azure Log Analytics + SQL 7 years JSON structured
Security Logs Azure Security Center 7 years Native format
Performance Metrics Application Insights 90 days Native format

Audit Trail Requirements

Event Category Events Logged Data Captured
User Actions Login, logout, profile changes User, timestamp, IP, action details
Work Orders Create, update, assign, complete User, before/after values, timestamp
Billing Invoice create, payment, adjustments User, amounts, approval chain
Configuration Rule changes, template edits User, before/after, effective date
Integration API calls, sync operations Request/response, duration, status
public class AuditService
{
    private readonly ILogger<AuditService> _logger;
    private readonly IAuditRepository _repository;

    public async Task LogAsync<T>(
        AuditAction action,
        string entityType,
        string entityId,
        T? oldValue,
        T? newValue,
        string userId)
    {
        var auditEntry = new AuditEntry
        {
            Id = Guid.NewGuid(),
            Action = action,
            EntityType = entityType,
            EntityId = entityId,
            OldValue = oldValue != null ? JsonSerializer.Serialize(oldValue) : null,
            NewValue = newValue != null ? JsonSerializer.Serialize(newValue) : null,
            UserId = userId,
            Timestamp = DateTimeOffset.UtcNow,
            CorrelationId = Activity.Current?.Id
        };

        await _repository.InsertAsync(auditEntry);

        _logger.LogInformation(
            "Audit: {Action} on {EntityType}/{EntityId} by {UserId}",
            action, entityType, entityId, userId);
    }
}

Documentation Reference: LOGGING.md, MONITORING.md


Analytics & Reporting

Power BI Embedded

Report Type Description Refresh Rate
Executive Dashboard KPIs, trends, regional performance 15 minutes
Store Performance Store-level metrics, technician productivity 15 minutes
Technician Productivity Completion rates, time tracking, quality 15 minutes
SLA Compliance SLA breach analysis, trends Real-time
Financial Reports Revenue, AR aging, profitability Hourly
Customer Analytics Customer satisfaction, service patterns Daily

Dashboard Integration

// Vue.js Power BI Embedded component
<template>
  <div class="report-container">
    <PowerBIReportEmbed
      :embed-config="embedConfig"
      :css-class-name="'report-frame'"
      @report-loaded="handleReportLoaded"
      @error="handleError"
    />
  </div>
</template>

<script setup lang="ts">
import { PowerBIReportEmbed } from 'powerbi-client-vue-js';
import { ref, onMounted } from 'vue';

const embedConfig = ref({
  type: 'report',
  embedUrl: '',
  accessToken: '',
  tokenType: 1, // Embed token
  settings: {
    panes: { filters: { visible: false } },
    background: 1
  }
});

onMounted(async () => {
  const token = await reportService.getEmbedToken('store-performance');
  embedConfig.value.embedUrl = token.embedUrl;
  embedConfig.value.accessToken = token.token;
});
</script>

Canned Reports

Report Format Schedule Distribution
Daily Operations Summary PDF Daily 6 AM Email to Store Managers
Weekly Performance Report PDF/Excel Monday 7 AM Email to Regional Managers
Monthly Financial Report PDF/Excel 1st of month Email to Finance Team
SLA Breach Report Excel Real-time On-demand
Technician Timesheet PDF Weekly Email to Payroll

Custom Charts (Chart.js)

For real-time dashboards where Power BI latency is not acceptable:

// Real-time dispatch dashboard chart
<template>
  <Line :data="chartData" :options="chartOptions" />
</template>

<script setup lang="ts">
import { Line } from 'vue-chartjs';
import { useRealTimeData } from '@/composables/useRealTimeData';

const { data: workOrderData } = useRealTimeData('work-order-stats');

const chartData = computed(() => ({
  labels: workOrderData.value.timestamps,
  datasets: [
    {
      label: 'Active Work Orders',
      data: workOrderData.value.active,
      borderColor: '#3B82F6'
    },
    {
      label: 'Completed Today',
      data: workOrderData.value.completed,
      borderColor: '#10B981'
    }
  ]
}));
</script>

Documentation Reference: MONITORING.md


Devices & Internationalization

Supported Devices

Platform Minimum Version Target Version Screen Sizes
iOS 14.0 17.0 iPhone 8+
Android 10 (API 29) 14 (API 34) 5" - 7"
Web Desktop Chrome 90+, Edge 90+, Safari 15+ Latest 1280px+
Web Tablet Chrome 90+, Edge 90+, Safari 15+ Latest 768px - 1279px

Device Capabilities

Capability Technology Fallback
Camera React Native Vision Camera Device camera app
GPS Location React Native Geolocation Manual address entry
Barcode Scanning ML Kit Barcode Scanner Manual entry
Signature Capture react-native-signature-capture Photo of signature
Offline Storage SQLite Graceful degradation
Push Notifications Azure Notification Hubs In-app polling

Internationalization (i18n)

Language Code Coverage Status
English (US) en-US 100% Primary
Spanish (US) es-US 100% Required

Web (Vue I18n)

// i18n/index.ts
import { createI18n } from 'vue-i18n';
import en from './locales/en.json';
import es from './locales/es.json';

export const i18n = createI18n({
  legacy: false,
  locale: 'en',
  fallbackLocale: 'en',
  messages: { en, es }
});

// Usage in component
<template>
  <h1>{{ t('workOrder.title') }}</h1>
  <p>{{ t('workOrder.assignedTo', { name: techName }) }}</p>
</template>

<script setup lang="ts">
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
</script>

Mobile (react-i18next)

// i18n/index.ts
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import en from './locales/en.json';
import es from './locales/es.json';

i18n.use(initReactI18next).init({
  resources: { en: { translation: en }, es: { translation: es } },
  lng: 'en',
  fallbackLng: 'en',
  interpolation: { escapeValue: false }
});

// Usage in component
import { useTranslation } from 'react-i18next';

function WorkOrderCard({ workOrder }) {
  const { t } = useTranslation();

  return (
    <View>
      <Text>{t('workOrder.title')}</Text>
      <Text>{t('workOrder.status', { status: workOrder.status })}</Text>
    </View>
  );
}

Translation File Structure

// en.json
{
  "common": {
    "save": "Save",
    "cancel": "Cancel",
    "submit": "Submit",
    "loading": "Loading..."
  },
  "workOrder": {
    "title": "Work Order",
    "number": "Work Order #{{number}}",
    "status": "Status: {{status}}",
    "assignedTo": "Assigned to {{name}}",
    "priority": {
      "emergency": "Emergency",
      "high": "High",
      "standard": "Standard",
      "low": "Low"
    }
  },
  "notifications": {
    "assignmentReceived": "New work order assigned",
    "etaUpdate": "Technician arriving in {{minutes}} minutes"
  }
}

// es.json
{
  "common": {
    "save": "Guardar",
    "cancel": "Cancelar",
    "submit": "Enviar",
    "loading": "Cargando..."
  },
  "workOrder": {
    "title": "Orden de Trabajo",
    "number": "Orden de Trabajo #{{number}}",
    "status": "Estado: {{status}}",
    "assignedTo": "Asignado a {{name}}",
    "priority": {
      "emergency": "Emergencia",
      "high": "Alta",
      "standard": "Estándar",
      "low": "Baja"
    }
  },
  "notifications": {
    "assignmentReceived": "Nueva orden de trabajo asignada",
    "etaUpdate": "Técnico llegando en {{minutes}} minutos"
  }
}

File Management & Storage

Azure Blob Storage

Container Purpose Access Retention
work-order-photos Before/after service photos Private 7 years
signatures Customer signature images Private 7 years
invoices Generated invoice PDFs Private 7 years
documents Attachments, reports Private Per policy
templates Form templates, email templates Private Indefinite
public-assets UI assets, icons Public (CDN) Indefinite

Storage Architecture

flowchart TB
    mobile["Mobile App
    • Capture
    • Queue
    • Upload"]

    api["API Service
    • Validate
    • Process
    • Metadata"]

    blob["Blob Storage
    • Store
    • CDN
    • Lifecycle"]

    ai["AI Vision
    (Analysis)"]

    workorder["Work Order
    (Metadata)"]

    mobile --> api
    api --> blob
    api --> ai
    blob --> ai
    ai --> workorder
    mobile --> workorder

Photo Upload Service

public class PhotoStorageService
{
    private readonly BlobServiceClient _blobClient;
    private readonly IPhotoAnalysisService _analysisService;

    public async Task<PhotoUploadResult> UploadWorkOrderPhotoAsync(
        string workOrderId,
        Stream photoStream,
        PhotoType photoType,
        string mimeType)
    {
        // 1. Generate blob name
        var blobName = $"{workOrderId}/{photoType}/{Guid.NewGuid()}.jpg";

        // 2. Upload to blob storage
        var container = _blobClient.GetBlobContainerClient("work-order-photos");
        var blob = container.GetBlobClient(blobName);

        await blob.UploadAsync(photoStream, new BlobHttpHeaders
        {
            ContentType = mimeType
        });

        // 3. Trigger AI analysis (async)
        if (photoType == PhotoType.TireCondition)
        {
            await _analysisService.QueueForAnalysisAsync(blobName);
        }

        // 4. Return result
        return new PhotoUploadResult
        {
            BlobName = blobName,
            Url = blob.Uri.ToString(),
            UploadedAt = DateTimeOffset.UtcNow
        };
    }
}

Azure CDN Integration

For static assets and cached photo thumbnails:

Origin CDN Profile Caching
public-assets Azure CDN Standard 7 days
Thumbnails Azure CDN Standard 24 hours

Integration Layer

Azure API Management

Policy Purpose Configuration
Rate Limiting Prevent abuse 1000 req/min per subscription
Caching Reduce backend load 5 min for reference data
Authentication Validate JWT tokens Azure AD validation
Transformation Request/response shaping Header manipulation
CORS Cross-origin requests Configured origins

API Design Standards

# OpenAPI specification pattern
openapi: 3.0.3
info:
  title: Pomp's Dispatch API
  version: 1.0.0

paths:
  /api/v1/work-orders:
    get:
      summary: List work orders
      parameters:
        - name: storeId
          in: query
          schema:
            type: string
        - name: status
          in: query
          schema:
            type: string
            enum: [pending, assigned, in-progress, completed]
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
      responses:
        '200':
          description: Paginated list of work orders
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkOrderListResponse'

Asynchronous Messaging (Azure Service Bus)

Queue/Topic Purpose Consumer
work-order-events Work order lifecycle events Multiple subscribers
notification-queue Outbound notifications Notification service
sync-commands Sync operations Sync service
photo-analysis Photo processing requests AI Vision service
integration-dlq Dead letter queue Manual review

External Integration Summary

System Integration Type Documentation
MaddenCo ERP Proxy API (REST) INTEGRATION_ARCHITECTURE.md
REACH Portal Proxy API (Bidirectional) INTEGRATION_ARCHITECTURE.md
Dayforce HR Proxy API (REST) INTEGRATION_ARCHITECTURE.md
Genesys Cloud Proxy API (REST) INTEGRATION_ARCHITECTURE.md
GeoTab REST API (Streaming) INTEGRATION_ARCHITECTURE.md

Documentation Reference: INTEGRATION_ARCHITECTURE.md, INTEGRATION_PATTERNS.md


Maps & Location Services

Azure Maps (Web)

Feature Use Case API
Technician Map Real-time technician locations Render + GeoTab data
Route Display Show technician route to customer Route API
Geofencing Store service areas Spatial API
Address Validation Customer address verification Search API
// Vue.js Azure Maps component
import * as atlas from 'azure-maps-control';

export function useAzureMaps(containerId: string) {
  const map = ref<atlas.Map | null>(null);

  const initializeMap = async () => {
    map.value = new atlas.Map(containerId, {
      authOptions: {
        authType: atlas.AuthenticationType.subscriptionKey,
        subscriptionKey: import.meta.env.VITE_AZURE_MAPS_KEY
      },
      center: [-89.6501, 39.7817],  // Central Illinois
      zoom: 10
    });

    await map.value.ready;
  };

  const addTechnicianMarker = (tech: TechnicianLocation) => {
    const marker = new atlas.HtmlMarker({
      position: [tech.longitude, tech.latitude],
      htmlContent: `<div class="tech-marker ${tech.status}">${tech.initials}</div>`
    });
    map.value?.markers.add(marker);
  };

  return { map, initializeMap, addTechnicianMarker };
}

React Native Maps (Mobile)

Feature Use Case Integration
Navigation Turn-by-turn to customer Google/Apple Maps deep link
Current Location Technician position Device GPS
Customer Location Destination marker Geocoded address
Traffic Real-time traffic layer Google/Apple Maps
// React Native Maps component
import MapView, { Marker, PROVIDER_GOOGLE } from 'react-native-maps';

function TechnicianMapScreen() {
  const { currentLocation } = useLocation();
  const { workOrder } = useCurrentWorkOrder();

  const openNavigation = () => {
    const destination = `${workOrder.latitude},${workOrder.longitude}`;
    const url = Platform.select({
      ios: `maps://app?daddr=${destination}`,
      android: `google.navigation:q=${destination}`
    });
    Linking.openURL(url);
  };

  return (
    <View style={styles.container}>
      <MapView
        provider={PROVIDER_GOOGLE}
        style={styles.map}
        region={{
          latitude: currentLocation.latitude,
          longitude: currentLocation.longitude,
          latitudeDelta: 0.05,
          longitudeDelta: 0.05
        }}
      >
        <Marker
          coordinate={currentLocation}
          title="You"
          pinColor="blue"
        />
        <Marker
          coordinate={{ 
            latitude: workOrder.latitude, 
            longitude: workOrder.longitude 
          }}
          title={workOrder.customerName}
          pinColor="red"
        />
      </MapView>
      <Button title="Navigate" onPress={openNavigation} />
    </View>
  );
}

GeoTab Integration

Real-time vehicle tracking for dispatch view.

public class GeoTabLocationService
{
    private readonly GeoTabClient _geoTabClient;
    private readonly IHubContext<DispatchHub> _hubContext;

    public async Task StartLocationStreamAsync(CancellationToken cancellationToken)
    {
        await foreach (var location in _geoTabClient.StreamLocationsAsync(cancellationToken))
        {
            // Update local cache
            await _cacheService.SetAsync(
                $"tech-location:{location.VehicleId}",
                location,
                TimeSpan.FromSeconds(60));

            // Broadcast to dispatch clients
            await _hubContext.Clients
                .Group("dispatchers")
                .SendAsync("TechnicianLocationUpdate", location);
        }
    }
}

Security

Identity & Access Management

Component Technology Configuration
Identity Provider Azure AD (Entra ID) OIDC, SAML 2.0
Single Sign-On Azure AD Configured applications
Multi-Factor Auth Azure AD Conditional Access Required for admin roles
Role Management Custom RBAC + Azure AD Groups Claims-based authorization

RBAC Implementation

// Permission-based authorization
[Authorize(Policy = "CanManageWorkOrders")]
[HttpPost("work-orders")]
public async Task<ActionResult<WorkOrder>> CreateWorkOrder(CreateWorkOrderRequest request)
{
    // Implementation
}

// Policy configuration
services.AddAuthorization(options =>
{
    options.AddPolicy("CanManageWorkOrders", policy =>
        policy.RequireAssertion(context =>
            context.User.HasClaim("permission", "work_orders.create") ||
            context.User.IsInRole("Admin")));

    options.AddPolicy("CanViewBilling", policy =>
        policy.RequireAssertion(context =>
            context.User.HasClaim("permission", "billing.view") ||
            context.User.IsInRole("Finance") ||
            context.User.IsInRole("Admin")));
});

Secrets Management

Secret Type Storage Rotation
API Keys Azure Key Vault 90 days
Connection Strings Azure Key Vault Manual
Certificates Azure Key Vault Auto-renewal
Encryption Keys Azure Key Vault HSM Annual

Documentation Reference: SECURITY.md, ROLES.md


Disaster Recovery & High Availability

High Availability Architecture

flowchart TB
    traffic_mgr["Azure Traffic Manager
    (Global LB)"]

    subgraph primary["PRIMARY REGION (Central US)"]
        app1["• App Service
        • Azure SQL (Primary)
        • Redis Cache
        • Service Bus
        • Blob Storage"]
    end

    subgraph secondary["SECONDARY REGION (East-US 2)"]
        app2["• App Service
        • Azure SQL (Secondary)
        • Redis Cache
        • Service Bus
        • Blob Storage (Read-only)"]
    end

    traffic_mgr --> primary
    traffic_mgr --> secondary
    primary -->|Geo-Repl| secondary
    primary -->|GRS| secondary

Recovery Objectives

Component RTO RPO Strategy
Web Application 15 min 0 Active-passive with auto-failover
API Services 10 min 0 Active-passive with auto-failover
Database 5 min < 5 sec Auto-failover groups
Blob Storage 1 hour 15 min RA-GRS
Service Bus 30 min 0 Geo-DR paired namespace

Documentation Reference: NFR.md


Job Scheduling

Azure Functions (Timer Triggers)

Job Schedule (CRON) Description
SLA Check 0 */5 * * * * Check for SLA breaches every 5 min
Sync Reconciliation 0 0 2 * * * Daily data reconciliation at 2 AM
Report Generation 0 0 6 * * * Daily reports at 6 AM
Cleanup Old Data 0 0 3 * * 0 Weekly cleanup Sunday 3 AM
Health Check 0 */1 * * * * Integration health check every minute
public class ScheduledJobs
{
    [FunctionName("SLACheck")]
    public async Task RunSLACheck(
        [TimerTrigger("0 */5 * * * *")] TimerInfo timer,
        ILogger log)
    {
        log.LogInformation($"SLA check started at {DateTime.UtcNow}");

        var breaches = await _slaService.CheckForBreachesAsync();

        foreach (var breach in breaches)
        {
            await _alertService.CreateSLABreachAlertAsync(breach);
        }

        log.LogInformation($"SLA check completed. {breaches.Count} breaches found.");
    }

    [FunctionName("DailyReconciliation")]
    public async Task RunReconciliation(
        [TimerTrigger("0 0 2 * * *")] TimerInfo timer,
        ILogger log)
    {
        log.LogInformation($"Daily reconciliation started at {DateTime.UtcNow}");

        await _reconciliationService.ReconcileCustomersAsync();
        await _reconciliationService.ReconcileTechniciansAsync();
        await _reconciliationService.ReconcileWorkOrdersAsync();

        log.LogInformation("Daily reconciliation completed.");
    }
}

Azure Data Factory (Batch ETL)

Pipeline Schedule Source Target
Customer Sync Every 15 min MaddenCo Pomp's SQL
Technician Sync Every 30 min Dayforce Pomp's SQL
Billing Export Hourly Pomp's SQL MaddenCo
Analytics Load Daily Pomp's SQL Power BI Dataset

DevOps & Infrastructure

CI/CD Pipeline (Bamboo)

# Bamboo build configuration
stages:
  - Build
  - Test
  - Security Scan
  - Deploy Dev
  - Integration Tests
  - Deploy Staging
  - Deploy Production

Build:
  tasks:
    - script: dotnet restore
    - script: dotnet build --configuration Release
    - script: npm ci && npm run build  # Vue.js

Test:
  tasks:
    - script: dotnet test --logger trx
    - script: npm run test:unit

Security Scan:
  tasks:
    - script: dotnet tool run snyk test
    - script: npm audit

Deploy:
  tasks:
    - script: az webapp deploy --name pomps-dispatch-$env

Infrastructure as Code (Terraform)

# Main infrastructure modules
module "networking" {
  source = "./modules/networking"

  vnet_name           = "pomps-dispatch-vnet"
  address_space       = ["10.0.0.0/16"]
  location            = var.location
  resource_group_name = azurerm_resource_group.main.name
}

module "database" {
  source = "./modules/sql-database"

  server_name         = "pomps-dispatch-sql"
  database_name       = "DispatchDB"
  sku_name            = "BC_Gen5_8"
  location            = var.location
  resource_group_name = azurerm_resource_group.main.name

  geo_redundant_backup_enabled = true
  zone_redundant               = true
}

module "app_service" {
  source = "./modules/app-service"

  name                = "pomps-dispatch-api"
  sku_name            = "P2v3"
  location            = var.location
  resource_group_name = azurerm_resource_group.main.name

  app_settings = {
    "ASPNETCORE_ENVIRONMENT" = var.environment
  }
}

Document Description
README.md Project overview and getting started
INTEGRATION_ARCHITECTURE.md External system integrations
VIEWS.md User interface specifications
ROLES.md Role definitions and permissions
DISPATCH_ALERTS.md Alert system documentation
SECURITY.md Security architecture
NFR.md Non-functional requirements
LOGGING.md Logging standards
MONITORING.md Monitoring and observability
ALERTING.md System alerting
DEVELOPMENT_GUIDELINES.md Development standards

Document Version: 1.0
Last Updated: January 2026
Next Review: April 2026