- Introduced the Krow Platform System Bible detailing the executive summary, system vision, ecosystem overview, architecture, application responsibilities, use cases, and security model. - Created a detailed use case overview for the Krow Web Application, outlining workflows for Admin, Client, and Vendor roles. - Established non-negotiable agent development rules for mobile applications, emphasizing file structure, naming conventions, logic placement, localization, and error handling. - Defined architecture principles for the Krow mobile platform, focusing on clean architecture, dependency direction, and session management. - Documented design system usage guidelines to ensure UI consistency and adherence to design tokens across applications.
198 lines
9.7 KiB
Markdown
198 lines
9.7 KiB
Markdown
# 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 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 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**:
|
|
- 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.
|
|
|
|
### 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/03-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/03-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 named routes via Flutter Modular:
|
|
- **Pattern**: `Modular.to.navigate('route_name')`
|
|
- **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()`
|