> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://cla-demo.postmansolutions.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://cla-demo.postmansolutions.com/_mcp/server.

# FLUXX Splitter Service---Low Level Design

## Document Version

* **Version**: 1.0

* **Date**: March 6, 2026

* **Author**: System Design

* **Status**: Design Phase

## 1. Overview

### 1.1 Purpose

The FLUXX Splitter Service is a standalone microservice that takes classified PDF documents and splits them into individual form files based on classification results. This service is extracted from the splitting logic in the existing `ClassifyFileTool` implementation to provide document splitting capabilities as an independent, reusable service.

### 1.2 Scope

This service handles:

* Reading classification results from previous classification phase

* Extracting page ranges for individual forms

* Splitting multi-form PDFs into separate PDF files

* Handling contiguous and non-contiguous page ranges

* Creating file metadata records for split files

* Uploading split files to blob storage

* Generating sanitized filenames from form types

### 1.3 Reference Implementation

This design is based on the existing implementation in:

* `/backend/src/api/v1/agent/tools/ClassifyFileTool.ts` (lines 237-368)

* `/backend/src/utils/pdfProcessor.ts` (`splitPDFByPageRanges` function)

## 2. Architecture

### 2.1 System Context

```

┌─────────────────────────────────┐

│  FLUXX Classification Service   │

│  (Upstream)                     │

└────────┬────────────────────────┘

         │

         │ Classification Result

         ▼

┌─────────────────────────────────┐

│  FLUXX Splitter Service         │

│  ┌──────────────────────────┐  │

│  │  API Layer (Express)     │  │

│  ├──────────────────────────┤  │

│  │  Split Orchestrator      │  │

│  ├──────────────────────────┤  │

│  │  PDF Splitting Engine    │  │

│  ├──────────────────────────┤  │

│  │  File Management Layer   │  │

│  └──────────────────────────┘  │

└─────┬──────────────┬────────────┘

      │              

      ▼              

┌───────────┐ 
│ Azure Blob│  

│ Storage   │  

└───────────┘ 

```

### 2.2 Service Components

#### 2.2.1 API Layer

* Express.js REST API

* Request validation

* Authentication and authorization

* Response formatting

#### 2.2.2 Split Orchestrator

* Orchestrates the splitting workflow

* Handles classification result parsing

* Builds page ranges from form classifications

* Manages error recovery and rollback

#### 2.2.3 PDF Splitting Engine

* PDF page extraction using pdf-lib

* Multi-range splitting for non-contiguous pages

* PDF document assembly

* Buffer management

#### 2.2.4 File Management Layer

* Blob storage integration

* File metadata creation

* Filename generation and sanitization

* Parent-child file relationships

## 3. API Design

### 3.1 Endpoints

#### 3.1.1 POST /api/v1/split

**Request:**

```json

{

  "file_id": "string (optional, reference to original file)",

  "file_url": "string (blob storage URL of original PDF)",

  "classification_result": {

    "is_multi_form": true,

    "num_pages": 45,

    "forms": [

      {

        "page_start": 1,

        "page_end": 2,

        "form_type": "Form 1040",

        "confidence": 0.95

      },

      {

        "page_start": 3,

        "page_end": 5,

        "form_type": "Schedule C",

        "confidence": 0.92

      }

    ]

  },

  "user_id": "string",

  "client_id": "string",

  "original_filename": "tax_return_2024.pdf",

  "metadata": {

    "parent_file_id": "uuid",

    "classification_url": "https://..."

  }

}

```

**Response (Success - 200):**

```json

{

  "success": true,

  "split_id": "string (UUID)",

  "original_file_id": "string",

  "files_created": 2,

  "split_files": [

    {

      "file_id": "string",

      "filename": "tax_return_2024_Form_1040.pdf",

      "blob_url": "https://storage.blob.core.windows.net/.../tax_return_2024_Form_1040.pdf",

      "form_type": "Form 1040",

      "page_range": { "start": 1, "end": 2 },

      "file_size": 524288,

      "form_index": 0

    },

    {

      "file_id": "string",

      "filename": "tax_return_2024_Schedule_C.pdf",

      "blob_url": "https://storage.blob.core.windows.net/.../tax_return_2024_Schedule_C.pdf",

      "form_type": "Schedule C",

      "page_range": { "start": 3, "end": 5 },

      "file_size": 786432,

      "form_index": 1

    }

  ],

  "processing_time_ms": 1234

}

```

**Response (Error - 400/500):**

```json

{

  "success": false,

  "error": "string",

  "error_code": "string",

  "details": {

    "invalid_ranges": [],

    "failed_splits": []

  }

}

```

#### 3.1.2 POST /api/v1/split-batch

Batch splitting for multiple documents.

**Request:**

```json

{

  "split_requests": [

    { /* same as single split request */ },

    { /* same as single split request */ }

  ]

}

```

**Response:**

```json

{

  "success": true,

  "total_requests": 10,

  "successful": 9,

  "failed": 1,

  "results": [

    { /* individual split results */ }

  ]

}

```

#### 3.1.3 GET /api/v1/split/:split\_id

Retrieves information about a previous split operation.

**Response:**

```json

{

  "success": true,

  "split_id": "string",

  "created_at": "ISO8601 timestamp",

  "original_file_id": "string",

  "files_created": 2,

  "split_files": [...]

}

```

#### 3.1.4 GET /health

Health check endpoint.

**Response:**

```json

{

  "status": "healthy",

  "version": "1.0.0",

  "dependencies": {

    "blob_storage": "connected",

    "database": "connected"

  }

}

```

## 4. Core Algorithm

### 4.1 Splitting Workflow

```

START

  │

  ├─> Validate Input

  │     ├─> Check classification result structure

  │     ├─> Verify file_url is accessible

  │     └─> Validate page ranges

  │

  ├─> Determine if Splitting is Needed

  │     │

  │     ├─> Check is_multi_form flag

  │     ├─> Check forms array length

  │     │

  │     IF (NOT is_multi_form AND forms.length <= 1):

  │       └─> SKIP SPLITTING (return success without splits)

  │     ELSE:

  │       └─> PROCEED WITH SPLITTING

  │

  ├─> Download Original PDF

  │     └─> From blob storage (file_url)

  │

  ├─> Build Page Ranges

  │     │

  │     FOR each form in classification_result.forms:

  │       │

  │       ├─> Extract form_type

  │       │

  │       ├─> Check for non-contiguous ranges

  │       │     IF form.page_ranges EXISTS:

  │       │       FOR each range in form.page_ranges:

  │       │         └─> Add: {start, end, formType: "{form_type}_Part{N}"}

  │       │     ELSE:

  │       │       └─> Add: {start: page_start, end: page_end, formType: form_type}

  │       │

  │       └─> Store in pageRanges array

  │

  ├─> Validate Page Ranges

  │     │

  │     FOR each range in pageRanges:

  │       ├─> Check: range.start >= 1

  │       ├─> Check: range.end >= range.start

  │       ├─> Check: range.end <= total_pages

  │       │

  │       IF invalid:

  │         └─> THROW ERROR (invalid page ranges)

  │

  ├─> Split PDF Using pdf-lib

  │     │

  │     FOR each range in pageRanges:

  │       │

  │       ├─> Create new PDFDocument

  │       │

  │       ├─> Copy pages from original (0-indexed)

  │       │     FOR page = range.start-1 TO range.end-1:

  │       │       ├─> Copy page from original PDF

  │       │       └─> Add to new PDF

  │       │

  │       ├─> Save new PDF to buffer

  │       │

  │       └─> Store: {buffer, pageRange, formIndex, formType}

  │

  ├─> Build Response

  │     ├─> split_id: generated UUID

  │     ├─> files_created: splitResults.length

  │     ├─> split_files: array of file metadata

  │     └─> processing_time_ms: elapsed time

  │

  └─> Return Success Response

```

### 4.2 Page Range Building Logic

```

FUNCTION buildPageRanges(forms: FormClassification[]): PageRange[]

  pageRanges = []

  FOR idx = 0 TO forms.length - 1:

    form = forms[idx]

    formType = form.form_type || form.formType || form.type || "Form_{idx+1}"

    // Check for non-contiguous page ranges

    IF form.page_ranges EXISTS AND is_array(form.page_ranges):

      // Multiple non-contiguous ranges for this form

      FOR rangeIdx = 0 TO form.page_ranges.length - 1:

        range = form.page_ranges[rangeIdx]

        // If multiple ranges, append part number

        IF form.page_ranges.length > 1:

          actualFormType = "{formType}_Part{rangeIdx + 1}"

        ELSE:

          actualFormType = formType

        pageRanges.push({

          start: range.start,

          end: range.end,

          formType: actualFormType

        })

    ELSE:

      // Single contiguous range

      start = form.start_page || form.page_start || form.pageStart || form.start || 1

      end = form.end_page || form.page_end || form.pageEnd || form.end || 1

      pageRanges.push({

        start: start,

        end: end,

        formType: formType

      })

  RETURN pageRanges

```

### 4.3 PDF Splitting Logic (pdf-lib)

```

FUNCTION splitPDFByPageRanges(

  pdfBuffer: Buffer,

  pageRanges: PageRange[]

): SplitResult[]

  // Load original PDF

  originalPdf = await PDFDocument.load(pdfBuffer)

  results = []

  FOR idx = 0 TO pageRanges.length - 1:

    range = pageRanges[idx]

    // Create new PDF document

    newPdf = await PDFDocument.create()

    // Convert to 0-indexed

    startPage = range.start - 1

    endPage = range.end - 1

    // Copy pages

    FOR pageNum = startPage TO endPage:

      [copiedPage] = await newPdf.copyPages(originalPdf, [pageNum])

      newPdf.addPage(copiedPage)

    // Save to buffer

    pdfBytes = await newPdf.save()

    results.push({

      buffer: Buffer.from(pdfBytes),

      pageRange: range,

      formIndex: idx,

      formType: range.formType

    })

  RETURN results

```

## 5. Data Models

### 5.1 Split Request

```typescript

interface SplitRequest {

  file_id?: string;

  file_url: string;

  classification_result: ClassificationResult;

  user_id: string;

  client_id: string;

  original_filename: string;

  metadata?: Record<string, any>;

}

  

interface ClassificationResult {

  is_multi_form: boolean;

  num_pages: number;

  forms: FormClassification[];

}

  

interface FormClassification {

  page_start?: number;

  page_end?: number;

  form_type: string;

  confidence: number;

  page_ranges?: Array<{start: number; end: number}>;

}

```

### 5.2 Split Response

```typescript

interface SplitResponse {

  success: boolean;

  split_id: string;

  original_file_id?: string;

  files_created: number;

  split_files: SplitFileInfo[];

  processing_time_ms: number;

  metadata?: Record<string, any>;

}

  

interface SplitFileInfo {

  file_id: string;

  filename: string;

  blob_url: string;

  form_type: string;

  page_range: { start: number; end: number };

  file_size: number;

  form_index: number;

}

```

### 5.3 Internal Models

```typescript

interface PageRange {

  start: number;

  end: number;

  formType: string;

}

  

interface SplitResult {

  buffer: Buffer;

  pageRange: PageRange;

  formIndex: number;

  formType: string;

}

 

```

## 6. Error Handling

### 6.1 Error Categories

\| Error Code | HTTP Status | Description | Recovery |

\|------------|-------------|-------------|----------|

\| `INVALID_CLASSIFICATION` | 400 | Invalid classification result | Fix classification data |

\| `INVALID_PAGE_RANGES` | 400 | Page ranges out of bounds | Reclassify document |

\| `FILE_NOT_ACCESSIBLE` | 404 | Cannot download original PDF | Verify file\_url |

\| `PDF_LOAD_ERROR` | 500 | Failed to load PDF with pdf-lib | Check PDF integrity |

\| `SPLIT_FAILED` | 500 | PDF splitting operation failed | Retry or manual review |

\| `STORAGE_ERROR` | 503 | Blob storage failure | Retry with backoff |

\| `PARTIAL_SPLIT_SUCCESS` | 207 | Some splits failed, others succeeded | Review partial results |

### 6.2 Error Response Format

```typescript

interface ErrorResponse {

  success: false;

  error: string;

  error_code: string;

  details?: {

    invalid_ranges?: PageRange[];

    failed_splits?: number[];

    partial_results?: SplitFileInfo[];

  };

  timestamp: string;

  request_id: string;

}

```

### 6.3 Partial Failure Handling

When some splits succeed but others fail:

* Return HTTP 207 (Multi-Status)

* Include successful split files in response

* Detail which splits failed and why

* Do NOT delete successful splits (allow partial success)

* Client can retry failed splits individually

## 7. Configuration

### 7.1 Environment Variables

```bash

# Service Configuration

PORT=3002

SERVICE_NAME=fluxx-splitter

VERSION=1.0.0

  

# Azure Blob Storage

AZURE_STORAGE_CONNECTION_STRING=xxx

AZURE_STORAGE_CONTAINER_NAME=fluxx-splits

  

# Splitter Settings

MAX_PDF_SIZE_MB=100

MAX_SPLIT_FILES=100

FILENAME_MAX_LENGTH=255

  

# Logging

LOG_LEVEL=info

```

### 7.2 Service Configuration

```typescript

// config/splitter.ts

export const splitterConfig = {

  maxPdfSizeMB: parseInt(process.env.MAX_PDF_SIZE_MB || '100'),

  maxSplitFiles: parseInt(process.env.MAX_SPLIT_FILES || '100'),

  filenameMaxLength: parseInt(process.env.FILENAME_MAX_LENGTH || '255'),

  blobContainerName: process.env.AZURE_STORAGE_CONTAINER_NAME || 'paperboy',

};

```

## 8. Implementation Pseudocode

### 8.1 Main Split Function

```typescript

async function splitDocument(request: SplitRequest): Promise<SplitResponse> {

  const startTime = Date.now();

  const splitId = generateUUID();

  try {

    // 1. Validate input

    validateSplitRequest(request);

    // 2. Check if splitting is needed

    const shouldSplit = request.classification_result.is_multi_form ||

                        request.classification_result.forms.length > 1;

    if (!shouldSplit || request.classification_result.forms.length === 0) {

      return {

        success: true,

        split_id: splitId,

        files_created: 0,

        split_files: [],

        processing_time_ms: Date.now() - startTime,

        message: 'Document does not require splitting'

      };

    }

    // 3. Download original PDF

    const pdfBuffer = await downloadFromBlobStorage(request.file_url);

    // 4. Build page ranges

    const pageRanges = buildPageRanges(request.classification_result.forms);

    // 5. Validate page ranges

    validatePageRanges(pageRanges, request.classification_result.num_pages);

    // 6. Split PDF

    const splitResults = await splitPDFByPageRanges(pdfBuffer, pageRanges);

    // 7. Upload and create file records

    const splitFiles: SplitFileInfo[] = [];

    const errors: Array<{index: number; error: string}> = [];

    for (let i = 0; i < splitResults.length; i++) {

      try {

        const splitResult = splitResults[i];

        const formType = pageRanges[i].formType;

        // Generate filename

        const baseName = request.original_filename.replace('.pdf', '');

        const sanitizedFormType = sanitizeForFilename(formType);

        const fileName = `${baseName}_${sanitizedFormType}.pdf`;

        // Upload to blob storage

        const blobUrl = await uploadToBlobStorage(

          request.user_id,

          request.client_id,

          fileName,

          splitResult.buffer

        );

        splitFiles.push({

          file_id: fileId,

          filename: fileName,

          blob_url: blobUrl,

          form_type: formType,

          page_range: splitResult.pageRange,

          file_size: splitResult.buffer.length,

          form_index: i

        });

      } catch (error) {

        console.error(`Failed to process split ${i}:`, error);

        errors.push({ index: i, error: error.message });

      }

    }

    // 8. Build response

    if (errors.length > 0 && splitFiles.length === 0) {

      // Total failure

      throw new Error(`All splits failed: ${JSON.stringify(errors)}`);

    }

    const response: SplitResponse = {

      success: true,

      split_id: splitId,

      original_file_id: request.file_id,

      files_created: splitFiles.length,

      split_files: splitFiles,

      processing_time_ms: Date.now() - startTime

    };

    if (errors.length > 0) {

      // Partial success

      response.partial_errors = errors;

      response.status_code = 207; // Multi-Status

    }

    return response;

  } catch (error) {

    throw classifySplitError(error, splitId);

  }

}

```

### 8.2 Filename Sanitization

```typescript

function sanitizeForFilename(formType: string): string {

  // Remove special characters, keep alphanumeric and basic punctuation

  let sanitized = formType.replace(/[^a-zA-Z0-9\-_]/g, '_');

  // Remove consecutive underscores

  sanitized = sanitized.replace(/_+/g, '_');

  // Remove leading/trailing underscores

  sanitized = sanitized.replace(/^_+|_+$/g, '');

  // Limit length

  if (sanitized.length > 100) {

    sanitized = sanitized.substring(0, 100);

  }

  // Default if empty

  if (sanitized.length === 0) {

    sanitized = 'Form';

  }

  return sanitized;

}

```

### 8.3 Page Range Validation

```typescript

function validatePageRanges(pageRanges: PageRange[], totalPages: number): void {

  const invalidRanges: PageRange[] = [];

  for (const range of pageRanges) {

    if (range.start < 1 ||

        range.end < range.start ||

        range.end > totalPages) {

      invalidRanges.push(range);

    }

  }

  if (invalidRanges.length > 0) {

    throw new ValidationError(

      'INVALID_PAGE_RANGES',

      `Invalid page ranges detected`,

      { invalid_ranges: invalidRanges, total_pages: totalPages }

    );

  }

  // Check for overlaps (optional, may be valid in some cases)

  for (let i = 0; i < pageRanges.length; i++) {

    for (let j = i + 1; j < pageRanges.length; j++) {

      const r1 = pageRanges[i];

      const r2 = pageRanges[j];

      if (rangesOverlap(r1, r2)) {

        console.warn(`Warning: Page ranges overlap: ${JSON.stringify(r1)} and ${JSON.stringify(r2)}`);

      }

    }

  }

}

  

function rangesOverlap(r1: PageRange, r2: PageRange): boolean {

  return !(r1.end < r2.start || r2.end < r1.start);

}

```

## 9. Deployment

### 9.1 Docker Container

```dockerfile

FROM node:18-alpine

  

WORKDIR /app

  

# Install dependencies

COPY package*.json ./

RUN npm ci --only=production

  

# Copy source

COPY . .

  

# Build TypeScript

RUN npm run build

  

EXPOSE 3002

  

CMD ["node", "dist/server.js"]

```

### 9.2 Kubernetes Deployment

```yaml

apiVersion: apps/v1

kind: Deployment

metadata:

  name: fluxx-splitter

  labels:

    app: fluxx-splitter

spec:

  replicas: 3

  selector:

    matchLabels:

      app: fluxx-splitter

  template:

    metadata:

      labels:

        app: fluxx-splitter

    spec:

      containers:

      - name: fluxx-splitter

        image: fluxx-splitter:1.0.0

        ports:

        - containerPort: 3002

        env:

        - name: PORT

          value: "3002"

        - name: AZURE_STORAGE_CONNECTION_STRING

          valueFrom:

            secretKeyRef:

              name: fluxx-secrets

              key: storage-connection-string

        - name: DATABASE_HOST

          valueFrom:

            secretKeyRef:

              name: fluxx-secrets

              key: database-host

        resources:

          requests:

            memory: "1Gi"

            cpu: "500m"

          limits:

            memory: "4Gi"

            cpu: "2000m"

        livenessProbe:

          httpGet:

            path: /health

            port: 3002

          initialDelaySeconds: 30

          periodSeconds: 10

        readinessProbe:

          httpGet:

            path: /health

            port: 3002

          initialDelaySeconds: 10

          periodSeconds: 5

```

## 10. Testing Strategy

### 10.1 Unit Tests

* Page range building logic

* Page range validation

* Filename sanitization

* Error handling for invalid inputs

### 10.2 Integration Tests

* PDF splitting with pdf-lib

* Blob storage upload

* Database file metadata creation

* End-to-end split workflow

### 10.3 Test Cases

\| Test Case | Input | Expected Output |

\|-----------|-------|-----------------|

\| Single Form | Classification with 1 form | No splits created |

\| Multi-Form (2 forms) | 1040 + Schedule C | 2 separate PDFs |

\| Non-contiguous Pages | Form with page\_ranges | Multiple PDFs for same form type |

\| Invalid Page Range | page\_start=10, page\_end=5 | Error: INVALID\_PAGE\_RANGES |

\| Page Range Out of Bounds | page\_end=100, but PDF has 50 pages | Error: INVALID\_PAGE\_RANGES |

\| Large Multi-Form | 50 forms | 50 separate PDFs |

\| Special Characters in Form Type | "Form 1099-MISC" | Sanitized filename |

\| Partial Split Failure | Valid data, but storage fails for 1 file | HTTP 207, partial results |

## 11. Performance Considerations

### 11.1 Expected Performance

* **Small PDF (2 forms, 10 pages)**: 1-2 seconds

* **Medium PDF (10 forms, 50 pages)**: 3-5 seconds

* **Large PDF (50 forms, 200 pages)**: 10-15 seconds

### 11.2 Performance Factors

* PDF page copying with pdf-lib (CPU intensive)

* Buffer creation for each split file

* Blob storage upload time (network I/O)

* Database inserts for file metadata

### 11.3 Optimization Opportunities

1. **Parallel uploads**: Upload split files to blob storage concurrently

2. **Batch database inserts**: Insert multiple file metadata records in single transaction

3. **Compression**: Compress PDF buffers before upload

4. **Caching**: Cache PDF documents for repeated split operations

### 11.4 Resource Requirements

* **Memory**: \~50MB per split operation + PDF file size

* **CPU**: Moderate (pdf-lib page copying)

* **Network**: High (downloading original, uploading splits)

## 12. Monitoring & Observability

### 12.1 Metrics

* Split requests per minute

* Average split time

* Files created per split

* Blob upload latency

* Database insert latency

* Error rate by error code

* Partial failure rate

### 12.2 Logging

* Structured JSON logging

* Input validation errors

* Split operation details (page ranges, form types)

* File creation success/failure

* Error stack traces

### 12.3 Alerting

* Error rate > 5%

* Average split time > 30 seconds

* Blob storage failures > 2%

* Database connectivity issues

* Partial failure rate > 10%

## 13. Security

### 13.1 Authentication

* API key authentication

* Azure AD integration (optional)

* User/client authorization

### 13.2 Data Protection

* TLS in transit

* Encrypted blob storage

* Temporary buffer cleanup

* PII redaction in logs

### 13.3 Input Validation

* Page range bounds checking

* Form type sanitization

* File size limits

* PDF format validation

### 13.4 Rate Limiting

* Per-user split limits

* Global service rate limits

* Maximum splits per request

## 14. Integration with Classification Service

### 14.1 Workflow Integration

```

┌─────────────────────────────┐

│  Client Application         │

└──────────┬──────────────────┘

           │

           │ 1. POST /classify

           ▼

┌─────────────────────────────┐

│  FLUXX Classification       │

│  Service                    │

└──────────┬──────────────────┘

           │

           │ 2. Classification Result

           ▼

┌─────────────────────────────┐

│  Client Application         │  

│  (Decision Logic)           │

└──────────┬──────────────────┘

           │

           │ 3. IF is_multi_form:

           │    POST /split

           ▼

┌─────────────────────────────┐

│  FLUXX Splitter Service     │

└──────────┬──────────────────┘

           │

           │ 4. Split Files

           ▼

┌─────────────────────────────┐

│  Client Application         │

│  (Process individual forms) │

└─────────────────────────────┘

```

### 14.2 Data Flow

1. **Classification Phase**:

\- Client sends PDF to classification service

\- Receives classification result with form types and page ranges

2. **Decision Phase**:

\- Client checks `is_multi_form` flag

\- If true, proceeds to splitting

3. **Splitting Phase**:

\- Client sends classification result + file URL to splitter service

\- Receives array of split file references

4. **Processing Phase**:

\- Client processes each split file individually

\- Each file is a single form type

### 14.3 API Contract

The splitter service expects the exact output format from the classification service:

```typescript

// Classification Service Output = Splitter Service Input

{

  "is_multi_form": boolean,

  "num_pages": number,

  "forms": [

    {

      "page_start": number,

      "page_end": number,

      "form_type": string,

      "confidence": number,

      "page_ranges"?: Array<{start: number, end: number}>  // For non-contiguous

    }

  ]

}

```

## 15. Migration from Existing System

### 15.1 Migration Path

1. Deploy FLUXX Splitter Service alongside existing system

2. Update ClassifyFileTool to optionally call splitter service instead of inline splitting

3. Add feature flag to control routing

4. Run in parallel for validation period

5. Gradually migrate all splitting to new service

6. Remove inline splitting code from ClassifyFileTool

### 15.2 Backward Compatibility

* Maintain same file metadata structure

* Preserve parent\_file\_id relationships

* Keep form\_index and form\_type fields

* Support existing blob storage paths

## 16. Future Enhancements

### 16.1 Phase 2 Features

* Parallel split file uploads

* Batch database inserts

* PDF compression options

* Custom filename templates

* Dry-run mode (validate without creating files)

### 16.2 Phase 3 Features

* Merge operation (combine forms back into single PDF)

* Re-ordering of forms before splitting

* Watermarking of split files

* PDF/A conversion for long-term archival

* Split preview (return file sizes without creating)

## 17. Dependencies

### 17.1 NPM Packages

```json

{

  "express": "^4.18.0",

  "pdf-lib": "^1.17.1",

  "@azure/storage-blob": "^12.15.0",

  "uuid": "^9.0.0",

  "zod": "^3.22.0"

}

```

### 17.2 External Services

* Azure Blob Storage (for file storage)

* Database (for file metadata - SQL Server or PostgreSQL)

## 18. Comparison with Current Implementation

### 18.1 Similarities

* Same pdf-lib splitting logic

* Same filename sanitization approach

* Same file metadata structure

* Same parent-child file relationships

### 18.2 Differences

\| Aspect | Current (ClassifyFileTool) | FLUXX Splitter Service |

\|--------|---------------------------|------------------------|

\| **Coupling** | Tightly coupled with classification | Loosely coupled, independent service |

\| **Reusability** | Only via tool invocation | Standard REST API, any client |

\| **Error Handling** | Fails entire classification if split fails | Supports partial success, better error detail |

\| **Batch Support** | One document at a time | Batch splitting supported |

\| **Monitoring** | Limited logging | Comprehensive metrics and logging |

\| **Scalability** | Scales with entire backend | Independent horizontal scaling |

\| **Testing** | Integration tests only | Unit + integration tests |

## 19. Rollback Strategy

### 19.1 Rollback Triggers

* Error rate > 10%

* Data corruption detected

* Performance degradation > 50%

* Critical bug discovered

### 19.2 Rollback Process

1. Switch feature flag to route traffic back to ClassifyFileTool

2. Stop FLUXX Splitter Service

3. Verify inline splitting is working

4. Investigate and fix issue

5. Re-deploy with fix

6. Gradually re-enable new service

### 19.3 Data Cleanup

* Split files created by service have traceable metadata

* Can query by creation timestamp to identify service-created files

* Rollback does not require deleting split files (backward compatible)

## 20. Conclusion

The FLUXX Splitter Service provides a robust, scalable solution for PDF document splitting based on classification results. By separating splitting from classification, we achieve:

**Benefits:**

* **Modularity**: Each service has a single, well-defined responsibility

* **Reusability**: Any client can use the splitter, not just Paperboy

* **Scalability**: Split operations can scale independently from classification

* **Observability**: Dedicated metrics and monitoring for splitting operations

* **Testability**: Easier unit and integration testing

* **Flexibility**: Clients can choose when and how to split

**Key Design Decisions:**

* **Stateless design**: Enables horizontal scaling

* **Standard REST API**: Easy integration

* **Partial failure support**: Better error resilience

* **Comprehensive validation**: Guards against invalid inputs

* **Cloud-native**: Designed for Kubernetes with Azure services

**Next Steps:**

1. Implement service following this design

2. Create comprehensive test suite

3. Deploy to staging environment

4. Integration testing with Classification Service

5. Performance benchmarking

6. Production deployment with feature flag

7. Gradual traffic migration

This service complements the FLUXX Classification Service to provide a complete document processing pipeline from classification to individual form extraction.