9.8 KiB
9.8 KiB
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.
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
AppandMain).
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
StatelessWidgetand move the state management to the BLoC orStatefulWidgetto 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 (exceptequatable).
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
DataConnectServicewith 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.
- If not possible, and if that specific widget is used in multiple features, then try to create a shared widget in the
- 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.
- 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.
- 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
LocaleBlocfor reactive locale state management - Export
TranslationProviderfor BuildContext-based string access - Map domain failures to user-friendly localized error messages via
ErrorTranslator
- Define all user-facing strings in
- 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)
- Features access strings via
- App Integration:
- Apps import
LocalizationModule()in their module imports - Apps wrap the material app with
BlocProvider<LocaleBloc>()andTranslationProvider - Apps initialize
MaterialAppwith locale fromLocaleState
- Apps import
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
- Domain Independence:
apps/mobile/packages/domainknows NOTHING about the outer world. It defines what needs to be done, not how. - UI Agnosticism:
apps/mobile/packages/featuresdepends onapps/mobile/packages/design_systemfor looks andapps/mobile/packages/domainfor logic. It does NOT know about Firebase. - Data Isolation:
apps/mobile/packages/data_connectdepends onapps/mobile/packages/domainto 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
- Interface First: Define
abstract interface class <Name>RepositoryInterfacein feature domain layer. - 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
- Session Store Population: On successful auth, session stores are populated:
- Staff:
StaffSessionStore.instance.setSession(StaffSession(...)) - Client:
ClientSessionStore.instance.setSession(ClientSession(...))
- Staff:
- Lazy Loading: If session is null, fetch data via
getStaffById()orgetBusinessById()and update store.
5. Feature Isolation & Cross-Feature Communication
- Zero Direct Imports:
import 'package:feature_a/...'is FORBIDDEN insidepackage:feature_b.- Exception: Shared packages like
domain,core, anddesign_systemare always accessible.
- Exception: Shared packages like
- Navigation: Use named routes via Flutter Modular:
- Pattern:
Modular.to.navigate('route_name') - Configuration: Routes defined in
module.dartfiles; constants inpaths.dart
- Pattern:
- Data Sharing: Features do not share state directly. Shared data accessed through:
- Domain Repositories: Centralized data sources (e.g.,
AuthRepository) - Session Stores:
StaffSessionStoreandClientSessionStorefor app-wide user context - Event Streams: If needed, via
DataConnectServicestreams for reactive updates
- Domain Repositories: Centralized data sources (e.g.,
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:
StaffSessionStorewithStaffSession(user: User, staff: Staff?, ownerId: String?) - Lazy Loading:
getStaffName()fetches viagetStaffById()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:
ClientSessionStorewithClientSession(user: User, business: ClientBusinessSession?) - Lazy Loading:
getUserSessionData()fetches viagetBusinessById()if session null - Navigation: On auth →
Modular.to.toClientHome(), on unauth →Modular.to.toInitialPage()