# Data Connect Connectors Pattern

## Overview

This document describes the **Data Connect Connectors** pattern implemented in the KROW mobile app. This pattern centralizes all backend query logic by mirroring backend connector structure in the mobile data layer.

## Problem Statement

**Without Connectors Pattern:**
- Each feature creates its own repository implementation
- Multiple features query the same backend connector → duplication
- When backend queries change, updates needed in multiple places
- No reusability across features

**Example Problem:**
```
staff_main/
  └── data/repositories/profile_completion_repository_impl.dart ← queries staff connector
profile/
  └── data/repositories/profile_repository_impl.dart ← also queries staff connector
onboarding/
  └── data/repositories/personal_info_repository_impl.dart ← also queries staff connector
```

## Solution: Connectors in Data Connect Package

All backend connector queries are implemented once in a centralized location, following the backend structure.

### Structure

```
apps/mobile/packages/data_connect/lib/src/connectors/
├── staff/
│   ├── domain/
│   │   ├── repositories/
│   │   │   └── staff_connector_repository.dart (interface)
│   │   └── usecases/
│   │       └── get_profile_completion_usecase.dart
│   └── data/
│       └── repositories/
│           └── staff_connector_repository_impl.dart (implementation)
├── order/
├── user/
├── emergency_contact/
└── ...
```

**Maps to backend structure:**
```
backend/dataconnect/connector/
├── staff/
├── order/
├── user/
├── emergency_contact/
└── ...
```

## Clean Architecture Layers

Each connector follows Clean Architecture with three layers:

### Domain Layer (`connectors/{name}/domain/`)

**Repository Interface:**
```dart
// staff_connector_repository.dart
abstract interface class StaffConnectorRepository {
  Future<bool> getProfileCompletion();
  Future<Staff> getStaffById(String id);
  // ... more queries
}
```

**Use Cases:**
```dart
// get_profile_completion_usecase.dart
class GetProfileCompletionUseCase {
  GetProfileCompletionUseCase({required StaffConnectorRepository repository});
  Future<bool> call() => _repository.getProfileCompletion();
}
```

**Characteristics:**
- Pure Dart, no framework dependencies
- Stable, business-focused contracts
- One interface per connector
- One use case per query or related query group

### Data Layer (`connectors/{name}/data/`)

**Repository Implementation:**
```dart
// staff_connector_repository_impl.dart
class StaffConnectorRepositoryImpl implements StaffConnectorRepository {
  final DataConnectService _service;

  @override
  Future<bool> getProfileCompletion() async {
    return _service.run(() async {
      final staffId = await _service.getStaffId();
      final response = await _service.connector
          .getStaffProfileCompletion(id: staffId)
          .execute();
      
      return _isProfileComplete(response);
    });
  }
}
```

**Characteristics:**
- Implements domain repository interface
- Uses `DataConnectService` to execute queries
- Maps backend response types to domain models
- Contains mapping/transformation logic only
- Handles type safety with generated Data Connect types

## Integration Pattern

### Step 1: Feature Needs Data

Feature (e.g., `staff_main`) needs profile completion status.

### Step 2: Use Connector Repository

Instead of creating a local repository, feature uses the connector:

```dart
// staff_main_module.dart
class StaffMainModule extends Module {
  @override
  void binds(Injector i) {
    // Register connector repository from data_connect
    i.addSingleton<StaffConnectorRepository>(
      StaffConnectorRepositoryImpl.new,
    );
    
    // Feature creates its own use case wrapper if needed
    i.addSingleton(
      () => GetProfileCompletionUseCase(
        repository: i.get<StaffConnectorRepository>(),
      ),
    );
    
    // BLoC uses the use case
    i.addSingleton(
      () => StaffMainCubit(
        getProfileCompletionUsecase: i.get<GetProfileCompletionUseCase>(),
      ),
    );
  }
}
```

### Step 3: BLoC Uses It

```dart
class StaffMainCubit extends Cubit<StaffMainState> {
  StaffMainCubit({required GetProfileCompletionUseCase usecase}) {
    _loadProfileCompletion();
  }
  
  Future<void> _loadProfileCompletion() async {
    final isComplete = await _getProfileCompletionUsecase();
    emit(state.copyWith(isProfileComplete: isComplete));
  }
}
```

## Export Pattern

Connectors are exported from `krow_data_connect` for easy access:

```dart
// lib/krow_data_connect.dart
export 'src/connectors/staff/domain/repositories/staff_connector_repository.dart';
export 'src/connectors/staff/domain/usecases/get_profile_completion_usecase.dart';
export 'src/connectors/staff/data/repositories/staff_connector_repository_impl.dart';
```

**Features import:**
```dart
import 'package:krow_data_connect/krow_data_connect.dart';
```

## Adding New Queries to Existing Connector

When backend adds `getStaffById()` query to staff connector:

1. **Add to interface:**
   ```dart
   abstract interface class StaffConnectorRepository {
     Future<Staff> getStaffById(String id);
   }
   ```

2. **Implement in repository:**
   ```dart
   @override
   Future<Staff> getStaffById(String id) async {
     return _service.run(() async {
       final response = await _service.connector
           .getStaffById(id: id)
           .execute();
       return _mapToStaff(response.data.staff);
     });
   }
   ```

3. **Use in features:**
   ```dart
   // Any feature can now use it
   final staff = await i.get<StaffConnectorRepository>().getStaffById(id);
   ```

## Adding New Connector

When backend adds new connector (e.g., `order`):

1. Create directory: `apps/mobile/packages/data_connect/lib/src/connectors/order/`

2. Create domain layer with repository interface and use cases

3. Create data layer with repository implementation

4. Export from `krow_data_connect.dart`

5. Features can immediately start using it

## Benefits

✅ **No Duplication** - Query implemented once, used by many features  
✅ **Single Source of Truth** - Backend change → update one place  
✅ **Clean Separation** - Connector logic separate from feature logic  
✅ **Reusability** - Any feature can request any connector data  
✅ **Testability** - Mock the connector repo to test features  
✅ **Scalability** - Easy to add new connectors as backend grows  
✅ **Mirrors Backend** - Mobile structure mirrors backend structure  

## Anti-Patterns

❌ **DON'T**: Implement query logic in feature repository
❌ **DON'T**: Duplicate queries across multiple repositories  
❌ **DON'T**: Put mapping logic in features  
❌ **DON'T**: Call `DataConnectService` directly from BLoCs  

**DO**: Use connector repositories through use cases in features.

## Current Implementation

### Staff Connector

**Location**: `apps/mobile/packages/data_connect/lib/src/connectors/staff/`

**Available Queries**:
- `getProfileCompletion()` - Returns bool indicating if profile is complete
  - Checks: personal info, emergency contacts, tax forms, experience (skills/industries)

**Used By**:
- `staff_main` - Guards bottom nav items requiring profile completion

**Backend Queries Used**:
- `backend/dataconnect/connector/staff/queries/profile_completion.gql`

### Shifts Connector

**Location**: `apps/mobile/packages/data_connect/lib/src/connectors/shifts/`

**Available Queries**:
- `listShiftRolesByVendorId()` - Fetches shifts for a specific vendor with status mapping
- `applyForShifts()` - Handles shift application with error tracking

**Backend Queries Used**:
- `backend/dataconnect/connector/shifts/queries/list_shift_roles_by_vendor.gql`
- `backend/dataconnect/connector/shifts/mutations/apply_for_shifts.gql`

## Future Expansion

As the app grows, additional connectors will be added:
- `order_connector_repository` (queries from `backend/dataconnect/connector/order/`)
- `user_connector_repository` (queries from `backend/dataconnect/connector/user/`)
- `emergency_contact_connector_repository` (queries from `backend/dataconnect/connector/emergencyContact/`)
- etc.

Each following the same Clean Architecture pattern implemented for Staff Connector.
