Skip to content

Frontend-Backend Workflow Alignment

This document provides guidance on aligning the frontend and backend workflow implementations in the PRS system, with a focus on the Zustand stores in the frontend and database status fields in the backend.

Current Architecture

The PRS system uses a layered architecture with distinct frontend and backend components:

  • Frontend: React application with Zustand for state management and React Query for API integration
  • Backend: Node.js application with Fastify framework and Sequelize ORM for database access

Workflow Visualization

The following diagram illustrates the overall workflow architecture, showing how user actions flow through the system:

graph TD
    User([User]) --> UI[Frontend UI]
    UI --> Actions[Action Handlers]
    Actions --> ZustandStores[Zustand Stores]
    Actions --> ReactQuery[React Query]
    ReactQuery --> Backend[Backend API]
    Backend --> Database[(Database)]
    ReactQuery --> UI
    ZustandStores --> UI

    Draft[Draft] --> Submitted[Submitted]
    Submitted --> Approved[Approved]
    Submitted --> Rejected[Rejected]

    CSDraft[CS Draft] --> CSForApproval[For CS Approval]
    CSForApproval --> CSApproved[CS Approved]

Workflow Sequence Diagram

The following sequence diagram shows the interaction between frontend and backend components during a typical workflow:

sequenceDiagram
    participant User
    participant UI
    participant ZS as Zustand
    participant RQ as ReactQuery
    participant API
    participant DB as Database

    User->>UI: Create Requisition
    UI->>ZS: Initialize store
    User->>UI: Add items
    UI->>ZS: Update store
    User->>UI: Submit

    UI->>RQ: Submit mutation
    RQ->>API: PUT request
    API->>DB: Update status
    API->>RQ: Return response
    RQ->>UI: Update UI

Current Challenges

The PRS system currently faces several challenges related to frontend-backend workflow alignment:

  1. Inconsistent Status Representation:
  2. Frontend and backend use different status values
  3. Status mapping is scattered across multiple components
  4. No centralized status constants

  5. No Central Workflow Management:

  6. Status transitions are handled independently in frontend and backend
  7. No shared validation logic between frontend and backend
  8. Workflow rules are duplicated across components

  9. Optimistic Updates Without Validation:

  10. Frontend may update UI before backend confirms status change
  11. No rollback mechanism if backend rejects status change
  12. Potential for inconsistent state

  13. Incomplete Permission Checks:

  14. Frontend may show actions that backend would reject
  15. Permission checks are not consistently applied
  16. No centralized permission management

  17. Zustand Store and Database Synchronization:

  18. Zustand stores in frontend don't properly synchronize with backend database status fields
  19. Local state may become out of sync with server state
  20. No clear mechanism for handling concurrent updates

Recommendations for Improving Alignment

1. Create Shared Status Constants

Create a shared module for status constants:

JavaScript
// src/constants/statusConstants.js
export const REQUISITION_STATUS = Object.freeze({
  DRAFT: 'draft',
  SUBMITTED: 'submitted',
  APPROVED: 'approved',
  REJECTED: 'rejected',
  // ...
});

export const CANVASS_STATUS = Object.freeze({
  DRAFT: 'draft',
  FOR_APPROVAL: 'for_approval',
  APPROVED: 'approved',
  REJECTED: 'rejected',
  // ...
});

export const PO_STATUS = Object.freeze({
  FOR_PO_REVIEW: 'for_po_review',
  FOR_PO_APPROVAL: 'for_po_approval',
  FOR_DELIVERY: 'for_delivery',
  REJECT_PO: 'reject_po',
  CANCELLED_PO: 'cancelled_po',
  // ...
});

2. Create a Status Display Utility

Create a utility for consistent status display:

JavaScript
// src/utils/statusUtils.js
import { REQUISITION_STATUS, CANVASS_STATUS, PO_STATUS } from '../constants/statusConstants';

const STATUS_DISPLAY_MAP = {
  [REQUISITION_STATUS.DRAFT]: 'Draft',
  [REQUISITION_STATUS.SUBMITTED]: 'For Approval',
  [REQUISITION_STATUS.APPROVED]: 'Approved',
  [REQUISITION_STATUS.REJECTED]: 'Rejected',
  // ...
};

const STATUS_CLASS_MAP = {
  [REQUISITION_STATUS.DRAFT]: 'draft',
  [REQUISITION_STATUS.SUBMITTED]: 'for_approval',
  [REQUISITION_STATUS.APPROVED]: 'approved',
  [REQUISITION_STATUS.REJECTED]: 'rejected',
  // ...
};

export const getStatusDisplay = (status) => STATUS_DISPLAY_MAP[status] || status;
export const getStatusClass = (status) => STATUS_CLASS_MAP[status] || 'default';

3. Implement a Workflow State Service

Create a service to manage workflow state:

JavaScript
// src/services/workflowService.js
import { REQUISITION_STATUS, CANVASS_STATUS, PO_STATUS } from '../constants/statusConstants';

const REQUISITION_TRANSITIONS = {
  [REQUISITION_STATUS.DRAFT]: [REQUISITION_STATUS.SUBMITTED],
  [REQUISITION_STATUS.SUBMITTED]: [REQUISITION_STATUS.APPROVED, REQUISITION_STATUS.REJECTED],
  [REQUISITION_STATUS.APPROVED]: [REQUISITION_STATUS.ASSIGNING],
  // ...
};

export const isValidTransition = (currentStatus, nextStatus, workflowType) => {
  let transitionMap;

  switch (workflowType) {
    case 'requisition':
      transitionMap = REQUISITION_TRANSITIONS;
      break;
    case 'canvass':
      transitionMap = CANVASS_TRANSITIONS;
      break;
    case 'purchaseOrder':
      transitionMap = PO_TRANSITIONS;
      break;
    default:
      return false;
  }

  return transitionMap[currentStatus]?.includes(nextStatus) || false;
};

4. Enhance React Query Mutations

Enhance React Query mutations with proper error handling and invalidation:

JavaScript
// src/hooks/useSubmitRequisition.js
import { useMutation, useQueryClient } from 'react-query';
import { submitRequisition } from '../api/requisitionApi';
import { REQUISITION_STATUS } from '../constants/statusConstants';
import { isValidTransition } from '../services/workflowService';

export const useSubmitRequisition = () => {
  const queryClient = useQueryClient();

  return useMutation(
    (requisition) => {
      // Pre-validation
      if (!isValidTransition(requisition.status, REQUISITION_STATUS.SUBMITTED, 'requisition')) {
        throw new Error('Invalid status transition');
      }

      return submitRequisition(requisition);
    },
    {
      onSuccess: (data) => {
        // Invalidate relevant queries
        queryClient.invalidateQueries(['requisition']);
        queryClient.invalidateQueries(['requisitions']);

        // Show success message
        toast.success('Requisition submitted successfully');
      },
      onError: (error) => {
        // Show error message
        toast.error(`Failed to submit requisition: ${error.message}`);
      }
    }
  );
};

Implementation Roadmap

To improve frontend-backend workflow alignment, follow this phased approach:

Phase 1: Documentation and Analysis

  1. Document current status values and transitions
  2. Identify inconsistencies between frontend and backend
  3. Create a status mapping document
  4. Define valid status transitions for each workflow

Phase 2: Standardization

  1. Create shared status constants
  2. Implement status display utilities
  3. Update UI components to use the utilities
  4. Standardize status values in the backend

Phase 3: Workflow Management

  1. Implement workflow state service
  2. Add validation for status transitions
  3. Enhance error handling for invalid transitions
  4. Implement proper query invalidation

Phase 4: Testing and Verification

  1. Test all workflow transitions
  2. Verify that frontend and backend stay in sync
  3. Test edge cases like concurrent updates
  4. Document the improved workflow management system