From d404b6604dd697287e427ce79affa72b5d2cd67d Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Thu, 19 Feb 2026 13:20:43 -0500 Subject: [PATCH] feat: Update architecture documentation for Data Connect Connectors pattern and remove unused import in staff connector repository implementation --- .../staff_connector_repository_impl.dart | 2 - docs/MOBILE/01-architecture-principles.md | 32 +- .../03-data-connect-connectors-pattern.md | 273 ++++++++++++++++++ 3 files changed, 301 insertions(+), 6 deletions(-) create mode 100644 docs/MOBILE/03-data-connect-connectors-pattern.md diff --git a/apps/mobile/packages/data_connect/lib/src/connectors/staff/data/repositories/staff_connector_repository_impl.dart b/apps/mobile/packages/data_connect/lib/src/connectors/staff/data/repositories/staff_connector_repository_impl.dart index d13a665c..caebd123 100644 --- a/apps/mobile/packages/data_connect/lib/src/connectors/staff/data/repositories/staff_connector_repository_impl.dart +++ b/apps/mobile/packages/data_connect/lib/src/connectors/staff/data/repositories/staff_connector_repository_impl.dart @@ -1,8 +1,6 @@ import 'package:firebase_data_connect/firebase_data_connect.dart'; import 'package:krow_data_connect/krow_data_connect.dart'; -import '../../domain/repositories/staff_connector_repository.dart'; - /// Implementation of [StaffConnectorRepository]. /// /// Fetches staff-related data from the Data Connect backend using diff --git a/docs/MOBILE/01-architecture-principles.md b/docs/MOBILE/01-architecture-principles.md index 40bcb623..b8c6f460 100644 --- a/docs/MOBILE/01-architecture-principles.md +++ b/docs/MOBILE/01-architecture-principles.md @@ -85,10 +85,18 @@ graph TD ### 2.4 Data Connect (`apps/mobile/packages/data_connect`) - **Role**: Interface Adapter for Backend Access (Datasource Layer). - **Responsibilities**: - - Implement Firebase Data Connect connector and service layer. - - Map Domain Entities to/from Data Connect generated code. - - Handle Firebase exceptions and map to domain failures. - - Provide centralized `DataConnectService` with session management. + - **Connectors**: Centralized repository implementations for each backend connector (see `03-data-connect-connectors-pattern.md`) + - One connector per backend connector domain (staff, order, user, etc.) + - Repository interfaces and use cases defined at domain level + - Repository implementations query backend and map responses + - Implement Firebase Data Connect connector and service layer + - Map Domain Entities to/from Data Connect generated code + - Handle Firebase exceptions and map to domain failures + - Provide centralized `DataConnectService` with session management +- **RESTRICTION**: + - NO feature-specific logic. Connectors are domain-neutral and reusable. + - All queries must follow Clean Architecture (domain → data layers) + - See `03-data-connect-connectors-pattern.md` for detailed pattern documentation ### 2.5 Design System (`apps/mobile/packages/design_system`) - **Role**: Visual language and component library. @@ -195,3 +203,19 @@ Each app (`staff` and `client`) has different role requirements and session patt - **Session Store**: `ClientSessionStore` with `ClientSession(user: User, business: ClientBusinessSession?)` - **Lazy Loading**: `getUserSessionData()` fetches via `getBusinessById()` if session null - **Navigation**: On auth → `Modular.to.toClientHome()`, on unauth → `Modular.to.toInitialPage()` + +## 7. Data Connect Connectors Pattern + +See **`03-data-connect-connectors-pattern.md`** for comprehensive documentation on: +- How connector repositories work +- How to add queries to existing connectors +- How to create new connectors +- Integration patterns with features +- Benefits and anti-patterns + +**Quick Reference**: +- All backend queries centralized in `apps/mobile/packages/data_connect/lib/src/connectors/` +- One connector per backend connector domain (staff, order, user, etc.) +- Each connector follows Clean Architecture (domain interfaces + data implementations) +- Features use connector repositories through dependency injection +- Results in zero query duplication and single source of truth diff --git a/docs/MOBILE/03-data-connect-connectors-pattern.md b/docs/MOBILE/03-data-connect-connectors-pattern.md new file mode 100644 index 00000000..165a30bd --- /dev/null +++ b/docs/MOBILE/03-data-connect-connectors-pattern.md @@ -0,0 +1,273 @@ +# 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.