399 lines
18 KiB
Markdown
399 lines
18 KiB
Markdown
# Warehouse Feature - Architecture Diagram
|
|
|
|
## Clean Architecture Layers
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ PRESENTATION LAYER │
|
|
│ (UI, State Management, User Interactions) │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌─────────────────────┐ ┌──────────────────────────────────┐ │
|
|
│ │ WarehouseCard │ │ WarehouseSelectionPage │ │
|
|
│ │ - Shows warehouse │ │ - Displays warehouse list │ │
|
|
│ │ information │ │ - Handles user selection │ │
|
|
│ └─────────────────────┘ │ - Pull to refresh │ │
|
|
│ │ - Loading/Error/Empty states │ │
|
|
│ └──────────────────────────────────┘ │
|
|
│ ↓ │
|
|
│ ┌──────────────────────────────────┐ │
|
|
│ │ WarehouseNotifier │ │
|
|
│ │ (StateNotifier) │ │
|
|
│ │ - loadWarehouses() │ │
|
|
│ │ - selectWarehouse() │ │
|
|
│ │ - refresh() │ │
|
|
│ └──────────────────────────────────┘ │
|
|
│ ↓ │
|
|
│ ┌──────────────────────────────────┐ │
|
|
│ │ WarehouseState │ │
|
|
│ │ - warehouses: List │ │
|
|
│ │ - selectedWarehouse: Warehouse? │ │
|
|
│ │ - isLoading: bool │ │
|
|
│ │ - error: String? │ │
|
|
│ └──────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
↓ uses
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ DOMAIN LAYER │
|
|
│ (Business Logic, Entities, Use Cases) │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
│ │ GetWarehousesUseCase │ │
|
|
│ │ - Encapsulates business logic for fetching warehouses │ │
|
|
│ │ - Single responsibility │ │
|
|
│ │ - Returns Either<Failure, List<WarehouseEntity>> │ │
|
|
│ └──────────────────────────────────────────────────────────┘ │
|
|
│ ↓ uses │
|
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
│ │ WarehouseRepository (Interface) │ │
|
|
│ │ + getWarehouses(): Either<Failure, List<Warehouse>> │ │
|
|
│ └──────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
│ │ WarehouseEntity │ │
|
|
│ │ - id: int │ │
|
|
│ │ - name: String │ │
|
|
│ │ - code: String │ │
|
|
│ │ - description: String? │ │
|
|
│ │ - isNGWareHouse: bool │ │
|
|
│ │ - totalCount: int │ │
|
|
│ │ + hasItems: bool │ │
|
|
│ │ + isNGType: bool │ │
|
|
│ └──────────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
↓ implements
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ DATA LAYER │
|
|
│ (API Calls, Data Sources, Models) │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
│ │ WarehouseRepositoryImpl │ │
|
|
│ │ - Implements WarehouseRepository interface │ │
|
|
│ │ - Coordinates data sources │ │
|
|
│ │ - Converts exceptions to failures │ │
|
|
│ │ - Maps models to entities │ │
|
|
│ └──────────────────────────────────────────────────────────┘ │
|
|
│ ↓ uses │
|
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
│ │ WarehouseRemoteDataSource (Interface) │ │
|
|
│ │ + getWarehouses(): Future<List<WarehouseModel>> │ │
|
|
│ └──────────────────────────────────────────────────────────┘ │
|
|
│ ↓ │
|
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
│ │ WarehouseRemoteDataSourceImpl │ │
|
|
│ │ - Makes API calls using ApiClient │ │
|
|
│ │ - Parses ApiResponse wrapper │ │
|
|
│ │ - Throws ServerException or NetworkException │ │
|
|
│ └──────────────────────────────────────────────────────────┘ │
|
|
│ ↓ uses │
|
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
│ │ WarehouseModel │ │
|
|
│ │ - Extends WarehouseEntity │ │
|
|
│ │ - Adds JSON serialization (fromJson, toJson) │ │
|
|
│ │ - Maps API fields to entity fields │ │
|
|
│ └──────────────────────────────────────────────────────────┘ │
|
|
│ ↓ uses │
|
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
│ │ ApiClient (Core) │ │
|
|
│ │ - Dio HTTP client wrapper │ │
|
|
│ │ - Adds authentication headers │ │
|
|
│ │ - Handles 401 errors │ │
|
|
│ │ - Logging and error handling │ │
|
|
│ └──────────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Data Flow
|
|
|
|
### 1. Loading Warehouses Flow
|
|
|
|
```
|
|
User Action (Pull to Refresh / Page Load)
|
|
↓
|
|
WarehouseSelectionPage
|
|
↓ calls
|
|
ref.read(warehouseProvider.notifier).loadWarehouses()
|
|
↓
|
|
WarehouseNotifier.loadWarehouses()
|
|
↓ sets state
|
|
state = state.setLoading() → UI shows loading indicator
|
|
↓ calls
|
|
GetWarehousesUseCase.call()
|
|
↓ calls
|
|
WarehouseRepository.getWarehouses()
|
|
↓ calls
|
|
WarehouseRemoteDataSource.getWarehouses()
|
|
↓ makes HTTP request
|
|
ApiClient.get('/warehouses')
|
|
↓ API Response
|
|
{
|
|
"Value": [...],
|
|
"IsSuccess": true,
|
|
"IsFailure": false,
|
|
"Errors": [],
|
|
"ErrorCodes": []
|
|
}
|
|
↓ parse
|
|
List<WarehouseModel> from JSON
|
|
↓ convert
|
|
List<WarehouseEntity>
|
|
↓ wrap
|
|
Right(warehouses) or Left(failure)
|
|
↓ update state
|
|
state = state.setSuccess(warehouses)
|
|
↓
|
|
UI rebuilds with warehouse list
|
|
```
|
|
|
|
### 2. Error Handling Flow
|
|
|
|
```
|
|
API Error / Network Error
|
|
↓
|
|
ApiClient throws DioException
|
|
↓
|
|
_handleDioError() converts to custom exception
|
|
↓
|
|
ServerException or NetworkException
|
|
↓
|
|
WarehouseRemoteDataSource catches and rethrows
|
|
↓
|
|
WarehouseRepositoryImpl catches exception
|
|
↓
|
|
Converts to Failure:
|
|
- ServerException → ServerFailure
|
|
- NetworkException → NetworkFailure
|
|
↓
|
|
Returns Left(failure)
|
|
↓
|
|
GetWarehousesUseCase returns Left(failure)
|
|
↓
|
|
WarehouseNotifier receives Left(failure)
|
|
↓
|
|
state = state.setError(failure.message)
|
|
↓
|
|
UI shows error state with retry button
|
|
```
|
|
|
|
### 3. Warehouse Selection Flow
|
|
|
|
```
|
|
User taps on WarehouseCard
|
|
↓
|
|
onTap callback triggered
|
|
↓
|
|
_onWarehouseSelected(warehouse)
|
|
↓
|
|
ref.read(warehouseProvider.notifier).selectWarehouse(warehouse)
|
|
↓
|
|
state = state.setSelectedWarehouse(warehouse)
|
|
↓
|
|
Navigation: context.push('/operations', extra: warehouse)
|
|
↓
|
|
OperationSelectionPage receives warehouse
|
|
```
|
|
|
|
## Dependency Graph
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────┐
|
|
│ Riverpod Providers │
|
|
├─────────────────────────────────────────────────┤
|
|
│ │
|
|
│ secureStorageProvider │
|
|
│ ↓ │
|
|
│ apiClientProvider │
|
|
│ ↓ │
|
|
│ warehouseRemoteDataSourceProvider │
|
|
│ ↓ │
|
|
│ warehouseRepositoryProvider │
|
|
│ ↓ │
|
|
│ getWarehousesUseCaseProvider │
|
|
│ ↓ │
|
|
│ warehouseProvider (StateNotifierProvider) │
|
|
│ ↓ │
|
|
│ WarehouseSelectionPage watches this provider │
|
|
│ │
|
|
└─────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## File Dependencies
|
|
|
|
```
|
|
warehouse_selection_page.dart
|
|
↓ imports
|
|
- warehouse_entity.dart
|
|
- warehouse_card.dart
|
|
- warehouse_provider.dart (via DI setup)
|
|
|
|
warehouse_card.dart
|
|
↓ imports
|
|
- warehouse_entity.dart
|
|
|
|
warehouse_provider.dart
|
|
↓ imports
|
|
- warehouse_entity.dart
|
|
- get_warehouses_usecase.dart
|
|
|
|
get_warehouses_usecase.dart
|
|
↓ imports
|
|
- warehouse_entity.dart
|
|
- warehouse_repository.dart (interface)
|
|
|
|
warehouse_repository_impl.dart
|
|
↓ imports
|
|
- warehouse_entity.dart
|
|
- warehouse_repository.dart (interface)
|
|
- warehouse_remote_datasource.dart
|
|
|
|
warehouse_remote_datasource.dart
|
|
↓ imports
|
|
- warehouse_model.dart
|
|
- api_client.dart
|
|
- api_response.dart
|
|
|
|
warehouse_model.dart
|
|
↓ imports
|
|
- warehouse_entity.dart
|
|
```
|
|
|
|
## State Transitions
|
|
|
|
```
|
|
┌──────────────┐
|
|
│ Initial │
|
|
│ isLoading: F │
|
|
│ error: null │
|
|
│ warehouses:[]│
|
|
└──────────────┘
|
|
↓
|
|
loadWarehouses()
|
|
↓
|
|
┌──────────────┐
|
|
│ Loading │
|
|
│ isLoading: T │────────────────┐
|
|
│ error: null │ │
|
|
│ warehouses:[]│ │
|
|
└──────────────┘ │
|
|
↓ │
|
|
Success Failure
|
|
↓ ↓
|
|
┌──────────────┐ ┌──────────────┐
|
|
│ Success │ │ Error │
|
|
│ isLoading: F │ │ isLoading: F │
|
|
│ error: null │ │ error: "..." │
|
|
│ warehouses:[…]│ │ warehouses:[]│
|
|
└──────────────┘ └──────────────┘
|
|
↓ ↓
|
|
Selection Retry
|
|
↓ ↓
|
|
┌──────────────┐ (back to Loading)
|
|
│ Selected │
|
|
│ selected: W │
|
|
└──────────────┘
|
|
```
|
|
|
|
## API Response Parsing
|
|
|
|
```
|
|
Raw API Response (JSON)
|
|
↓
|
|
{
|
|
"Value": [
|
|
{
|
|
"Id": 1,
|
|
"Name": "Warehouse A",
|
|
"Code": "001",
|
|
...
|
|
}
|
|
],
|
|
"IsSuccess": true,
|
|
...
|
|
}
|
|
↓
|
|
ApiResponse.fromJson() parses wrapper
|
|
↓
|
|
ApiResponse<List<WarehouseModel>> {
|
|
value: [WarehouseModel, WarehouseModel, ...],
|
|
isSuccess: true,
|
|
isFailure: false,
|
|
errors: [],
|
|
errorCodes: []
|
|
}
|
|
↓
|
|
Check isSuccess
|
|
↓
|
|
if (isSuccess && value != null)
|
|
return value!
|
|
else
|
|
throw ServerException(errors.first)
|
|
↓
|
|
List<WarehouseModel>
|
|
↓
|
|
map((model) => model.toEntity())
|
|
↓
|
|
List<WarehouseEntity>
|
|
```
|
|
|
|
## Separation of Concerns
|
|
|
|
### Domain Layer
|
|
- **No dependencies** on Flutter, Dio, or other frameworks
|
|
- Contains **pure business logic**
|
|
- Defines **contracts** (repository interfaces)
|
|
- **Independent** and **testable**
|
|
|
|
### Data Layer
|
|
- **Implements** domain contracts
|
|
- Handles **external dependencies** (API, database)
|
|
- **Converts** between models and entities
|
|
- **Transforms** exceptions to failures
|
|
|
|
### Presentation Layer
|
|
- **Depends** only on domain layer
|
|
- Handles **UI rendering** and **user interactions**
|
|
- Manages **local state** with Riverpod
|
|
- **Observes** changes and **reacts** to state updates
|
|
|
|
## Testing Strategy
|
|
|
|
```
|
|
Unit Tests
|
|
├── Domain Layer
|
|
│ ├── Test entities (equality, methods)
|
|
│ ├── Test use cases (mock repository)
|
|
│ └── Verify business logic
|
|
├── Data Layer
|
|
│ ├── Test models (JSON serialization)
|
|
│ ├── Test data sources (mock ApiClient)
|
|
│ └── Test repository (mock data source)
|
|
└── Presentation Layer
|
|
├── Test notifier (mock use case)
|
|
└── Test state transitions
|
|
|
|
Widget Tests
|
|
├── Test UI rendering
|
|
├── Test user interactions
|
|
└── Test state-based UI changes
|
|
|
|
Integration Tests
|
|
├── Test complete flow
|
|
└── Test with real dependencies
|
|
```
|
|
|
|
## Benefits of This Architecture
|
|
|
|
1. **Testability**: Each layer can be tested independently with mocks
|
|
2. **Maintainability**: Changes in one layer don't affect others
|
|
3. **Scalability**: Easy to add new features following the same pattern
|
|
4. **Reusability**: Domain entities and use cases can be reused
|
|
5. **Separation**: Clear boundaries between UI, business logic, and data
|
|
6. **Flexibility**: Easy to swap implementations (e.g., change API client)
|
|
|
|
---
|
|
|
|
**Last Updated:** 2025-10-27
|
|
**Version:** 1.0.0
|