# KROW Architecture Principles

This document is the **AUTHORITATIVE** source of truth for the KROW engineering architecture.
All agents and engineers must adhere strictly to these principles. Deviations are interpreted as errors.

## 1. High-Level Architecture

The KROW platform follows a strict **Clean Architecture** implementation within a **Melos Monorepo**.
Dependencies flow **inwards** towards the Domain.

```mermaid
graph TD
    subgraph "Apps (Entry Points)"
        ClientApp["apps/mobile/apps/client"]
        StaffApp["apps/mobile/apps/staff"]
    end

    subgraph "Features"
        ClientFeatures["apps/mobile/packages/features/client/*"]
        StaffFeatures["apps/mobile/packages/features/staff/*"]
    end

    subgraph "Services"
        DataConnect["apps/mobile/packages/data_connect"]
        DesignSystem["apps/mobile/packages/design_system"]
        CoreLocalization["apps/mobile/packages/core_localization"]
    end

    subgraph "Core Domain"
        Domain["apps/mobile/packages/domain"]
        Core["apps/mobile/packages/core"]
    end

    %% Dependency Flow
    ClientApp --> ClientFeatures & DataConnect & CoreLocalization
    StaffApp --> StaffFeatures & DataConnect & CoreLocalization

    ClientFeatures & StaffFeatures --> Domain
    ClientFeatures & StaffFeatures --> DesignSystem
    ClientFeatures & StaffFeatures --> CoreLocalization
    ClientFeatures & StaffFeatures --> Core

    DataConnect --> Domain
    DataConnect --> Core
    DesignSystem --> Core
    CoreLocalization --> Core
    Domain --> Core

    %% Strict Barriers
    linkStyle default stroke-width:2px,fill:none,stroke:gray
```

## 2. Repository Structure & Package Roles

### 2.1 Apps (`apps/mobile/apps/`)
- **Role**: Application entry points and Dependency Injection (DI) roots.
- **Responsibilities**:
  - Initialize Flutter Modular.
  - Assemble features into a navigation tree.
  - Inject concrete implementations (from `data_connect`) into Feature packages.
  - Configure environment-specific settings.
- **RESTRICTION**: NO business logic. NO UI widgets (except `App` and `Main`).

### 2.2 Features (`apps/mobile/packages/features/<APP_NAME>/<FEATURE_NAME>`)
- **Role**: Vertical slices of user-facing functionality.
- **Internal Structure**:
  - `domain/`: Feature-specific Use Cases(always extend the apps/mobile/packages/core/lib/src/domain/usecases/usecase.dart abstract clas) and Repository Interfaces.
  - `data/`: Repository Implementations.
  - `presentation/`: 
    - Pages, BLoCs, Widgets.
    - For performance make the pages as `StatelessWidget` and move the state management to the BLoC (always use a BlocProvider when providing the BLoC to the widget tree) or `StatefulWidget` to an external separate widget file.
- **Responsibilities**:
  - **Presentation**: UI Pages, Modular Routes.
  - **State Management**: BLoCs / Cubits.
  - **Application Logic**: Use Cases.
- **RESTRICTION**: Features MUST NOT import other features. Communication happens via shared domain events.

### 2.3 Domain (`apps/mobile/packages/domain`)
- **Role**: The stable heart of the system. Pure Dart.
- **Responsibilities**:
  - **Entities**: Immutable data models (Data Classes).
  - **Failures**: Domain-specific error types.
- **RESTRICTION**: NO Flutter dependencies. NO `json_annotation`. NO package dependencies (except `equatable`).

### 2.4 Data Connect (`apps/mobile/packages/data_connect`)
- **Role**: Interface Adapter for Backend Access (Datasource Layer).
- **Responsibilities**:
  - **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.
- **Responsibilities**:
  - UI components if needed. But mostly try to modify the theme file (apps/mobile/packages/design_system/lib/src/ui_theme.dart) so we can directly use the theme in the app, to use the default material widgets.
    - If not possible, and if that specific widget is used in multiple features, then try to create a shared widget in the `apps/mobile/packages/design_system/widgets`.
  - Theme definitions (Colors, Typography).
  - Assets (Icons, Images).
  - More details on how to use this package is available in the `apps/mobile/docs/02-design-system-usage.md`.
- **RESTRICTION**: 
  - CANNOT change colours or typography.
  - Dumb widgets only. NO business logic. NO state management (Bloc).
  - More details on how to use this package is available in the `apps/mobile/docs/02-design-system-usage.md`.

### 2.6 Core Localization (`apps/mobile/packages/core_localization`)
- **Role**: Centralized language and localization management.
- **Responsibilities**:
  - Define all user-facing strings in `l10n/` with i18n tooling support
  - Provide `LocaleBloc` for reactive locale state management
  - Export `TranslationProvider` for BuildContext-based string access
  - Map domain failures to user-friendly localized error messages via `ErrorTranslator`
- **Feature Integration**:
  - Features access strings via `context.strings.<key>` in presentation layer
  - BLoCs don't depend on localization; they emit domain failures
  - Error translation happens in UI layer (pages/widgets)
- **App Integration**:
  - Apps import `LocalizationModule()` in their module imports
  - Apps wrap the material app with `BlocProvider<LocaleBloc>()` and `TranslationProvider`
  - Apps initialize `MaterialApp` with locale from `LocaleState`

### 2.7 Core (`apps/mobile/packages/core`)
- **Role**: Cross-cutting concerns.
- **Responsibilities**:
  - Extension methods.
  - Logger configuration.
  - Base classes for Use Cases or Result types (functional error handling).

## 3. Dependency Direction & Boundaries

1.  **Domain Independence**: `apps/mobile/packages/domain` knows NOTHING about the outer world. It defines *what* needs to be done, not *how*.
2.  **UI Agnosticism**: `apps/mobile/packages/features` depends on `apps/mobile/packages/design_system` for looks and `apps/mobile/packages/domain` for logic. It does NOT know about Firebase.
3.  **Data Isolation**: `apps/mobile/packages/data_connect` depends on `apps/mobile/packages/domain` to know what interfaces to implement. It does NOT know about the UI.

## 4. Data Connect Service & Session Management

All backend access is unified through `DataConnectService` with integrated session management:

### 4.1 Session Handler Mixin
- **Location**: `apps/mobile/packages/data_connect/lib/src/services/mixins/session_handler_mixin.dart`
- **Responsibilities**:
  - Automatic token refresh (triggered when token <5 minutes to expiry)
  - Firebase auth state listening
  - Role-based access validation
  - Session state stream emissions
  - 3-attempt retry logic with exponential backoff on token validation failure
- **Key Method**: `initializeAuthListener(allowedRoles: [...])` - call once on app startup

### 4.2 Session Listener Widget
- **Location**: `apps/mobile/apps/<app>/lib/src/widgets/session_listener.dart`
- **Responsibilities**:
  - Wraps entire app to listen to session state changes
  - Shows user-friendly dialogs for session expiration/errors
  - Handles navigation on auth state changes
- **Pattern**: `SessionListener(child: AppWidget())`

### 4.3 Repository Pattern with Data Connect
1.  **Interface First**: Define `abstract interface class <Name>RepositoryInterface` in feature domain layer.
2.  **Implementation**: Use `_service.run()` wrapper that automatically:
    -   Validates user is authenticated (if required)
    -   Ensures token is valid and refreshes if needed
    -   Executes the Data Connect query
    -   Handles exceptions and maps to domain failures
3.  **Session Store Population**: On successful auth, session stores are populated:
    - Staff: `StaffSessionStore.instance.setSession(StaffSession(...))`
    - Client: `ClientSessionStore.instance.setSession(ClientSession(...))`
4.  **Lazy Loading**: If session is null, fetch data via `getStaffById()` or `getBusinessById()` and update store.

## 5. Feature Isolation & Cross-Feature Communication

-   **Zero Direct Imports**: `import 'package:feature_a/...'` is FORBIDDEN inside `package:feature_b`.
    -   Exception: Shared packages like `domain`, `core`, and `design_system` are always accessible.
-   **Navigation**: Use **Typed Navigators** and **Safe Navigation** via Flutter Modular:
    -   **Safe Methods**: ALWAYS use `safeNavigate()`, `safePush()`, `popSafe()`, and `safePushNamedAndRemoveUntil()` from `NavigationExtensions`.
    -   **Fallback**: All safe methods automatically fall back to the Home page (Staff or Client) if the target route is invalid or the operation fails.
    -   **Typed Navigator Pattern**: Prefer using typed methods on `Modular.to` (e.g., `Modular.to.toShiftDetails(id)`) which are implemented in `ClientNavigator` and `StaffNavigator` using these safe extensions.
    -   **Configuration**: Routes defined in `module.dart` files; constants in `paths.dart`.
-   **Data Sharing**: Features do not share state directly. Shared data accessed through:
    -   **Domain Repositories**: Centralized data sources (e.g., `AuthRepository`)
    -   **Session Stores**: `StaffSessionStore` and `ClientSessionStore` for app-wide user context
    -   **Event Streams**: If needed, via `DataConnectService` streams for reactive updates

## 6. App-Specific Session Management

Each app (`staff` and `client`) has different role requirements and session patterns:

### 6.1 Staff App Session
- **Location**: `apps/mobile/apps/staff/lib/main.dart`
- **Initialization**: `DataConnectService.instance.initializeAuthListener(allowedRoles: ['STAFF', 'BOTH'])`
- **Session Store**: `StaffSessionStore` with `StaffSession(user: User, staff: Staff?, ownerId: String?)`
- **Lazy Loading**: `getStaffName()` fetches via `getStaffById()` if session null
- **Navigation**: On auth → `Modular.to.toStaffHome()`, on unauth → `Modular.to.toInitialPage()`

### 6.2 Client App Session
- **Location**: `apps/mobile/apps/client/lib/main.dart`
- **Initialization**: `DataConnectService.instance.initializeAuthListener(allowedRoles: ['CLIENT', 'BUSINESS', 'BOTH'])`
- **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

## 8. Prop Drilling Prevention & Direct BLoC Access

### 8.1 The Problem: Prop Drilling

Passing data through intermediate widgets creates maintenance headaches:
- Every intermediate widget must accept and forward props
- Changes to data structure ripple through multiple widget constructors
- Reduces code clarity and increases cognitive load

**Anti-Pattern Example**:
```dart
// ❌ BAD: Drilling status through 3 levels
ProfilePage(status: status)
  → ProfileHeader(status: status)
    → ProfileLevelBadge(status: status)  // Only widget that needs it!
```

### 8.2 The Solution: Direct BLoC Access with BlocBuilder

Use `BlocBuilder` to access BLoC state directly in leaf widgets:

**Correct Pattern**:
```dart
// ✅ GOOD: ProfileLevelBadge accesses ProfileCubit directly
class ProfileLevelBadge extends StatelessWidget {
  const ProfileLevelBadge({super.key});

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<ProfileCubit, ProfileState>(
      builder: (context, state) {
        final Staff? profile = state.profile;
        if (profile == null) return const SizedBox.shrink();
        
        final level = _mapStatusToLevel(profile.status);
        return LevelBadgeUI(level: level);
      },
    );
  }
}
```

### 8.3 Guidelines for Avoiding Prop Drilling

1. **Leaf Widgets Get Data from BLoC**: Widgets that need specific data should access it directly via BlocBuilder
2. **Container Widgets Stay Simple**: Parent widgets like `ProfileHeader` only manage layout and positioning
3. **No Unnecessary Props**: Don't pass data to intermediate widgets unless they need it for UI construction
4. **Single Responsibility**: Each widget should have one reason to exist

**Decision Tree**:
```
Does this widget need data?
├─ YES, and it's a leaf widget → Use BlocBuilder
├─ YES, and it's a container → Use BlocBuilder in child, not parent
└─ NO → Don't add prop to constructor
```

## 9. BLoC Lifecycle & State Emission Safety

### 9.1 The Problem: StateError After Dispose

When async operations complete after a BLoC is closed, attempting to emit state causes:
```
StateError: Cannot emit new states after calling close
```

**Root Causes**:
1. **Transient BLoCs**: `BlocProvider(create:)` creates new instance on every rebuild → disposed prematurely
2. **Singleton Disposal**: Multiple BlocProviders disposing same singleton instance
3. **Navigation During Async**: User navigates away while `loadProfile()` is still running

### 9.2 The Solution: Singleton BLoCs + Error Handler Defensive Wrapping

#### Step 1: Register as Singleton

```dart
// ✅ GOOD: ProfileCubit as singleton
i.addSingleton<ProfileCubit>(
  () => ProfileCubit(useCase1, useCase2),
);

// ❌ BAD: Creates new instance each time
i.add(ProfileCubit.new);
```

#### Step 2: Use BlocProvider.value() for Singletons

```dart
// ✅ GOOD: Use singleton instance
ProfileCubit cubit = Modular.get<ProfileCubit>();
BlocProvider<ProfileCubit>.value(
  value: cubit,  // Reuse same instance
  child: MyWidget(),
)

// ❌ BAD: Creates duplicate instance
BlocProvider<ProfileCubit>(
  create: (_) => Modular.get<ProfileCubit>(),  // Wrong!
  child: MyWidget(),
)
```

#### Step 3: Defensive Error Handling in BlocErrorHandler Mixin

The `BlocErrorHandler<S>` mixin provides `_safeEmit()` wrapper:

**Location**: `apps/mobile/packages/core/lib/src/presentation/mixins/bloc_error_handler.dart`

```dart
void _safeEmit(void Function(S) emit, S state) {
  try {
    emit(state);
  } on StateError catch (e) {
    // Bloc was closed before emit - log and continue gracefully
    developer.log(
      'Could not emit state: ${e.message}. Bloc may have been disposed.',
      name: runtimeType.toString(),
    );
  }
}
```

**Usage in Cubits/Blocs**:
```dart
Future<void> loadProfile() async {
  emit(state.copyWith(status: ProfileStatus.loading));

  await handleError(
    emit: emit,
    action: () async {
      final profile = await getProfile();
      emit(state.copyWith(status: ProfileStatus.loaded, profile: profile));
      // ✅ If BLoC disposed before emit, _safeEmit catches StateError gracefully
    },
    onError: (errorKey) {
      return state.copyWith(status: ProfileStatus.error);
    },
  );
}
```

### 9.3 Pattern Summary

| Pattern | When to Use | Risk |
|---------|------------|------|
| Singleton + BlocProvider.value() | Long-lived features (Profile, Shifts, etc.) | Low - instance persists |
| Transient + BlocProvider(create:) | Temporary widgets (Dialogs, Overlays) | Medium - requires careful disposal |
| Direct BlocBuilder | Leaf widgets needing data | Low - no registration needed |

**Remember**:
- Use **singletons** for feature-level cubits accessed from multiple pages
- Use **transient** only for temporary UI states
- Always wrap emit() in `_safeEmit()` via `BlocErrorHandler` mixin
- Test navigation away during async operations to verify graceful handling

```
