# 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 getProfileCompletion(); Future getStaffById(String id); // ... more queries } ``` **Use Cases:** ```dart // get_profile_completion_usecase.dart class GetProfileCompletionUseCase { GetProfileCompletionUseCase({required StaffConnectorRepository repository}); Future 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 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( StaffConnectorRepositoryImpl.new, ); // Feature creates its own use case wrapper if needed i.addSingleton( () => GetProfileCompletionUseCase( repository: i.get(), ), ); // BLoC uses the use case i.addSingleton( () => StaffMainCubit( getProfileCompletionUsecase: i.get(), ), ); } } ``` ### Step 3: BLoC Uses It ```dart class StaffMainCubit extends Cubit { StaffMainCubit({required GetProfileCompletionUseCase usecase}) { _loadProfileCompletion(); } Future _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 getStaffById(String id); } ``` 2. **Implement in repository:** ```dart @override Future 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().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` ## 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.