Skip to main content

Arsitektur Aplikasi

MStore Mobile dibangun dengan Clean Architecture dan BLoC Pattern untuk memastikan separation of concerns, testability, dan maintainability.

๐Ÿ—๏ธ Arsitektur Overview

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Presentation Layer                    โ”‚
โ”‚  (UI, Widgets, Pages, BLoC/Cubit State Management)      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                           โ†“
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                     Domain Layer                         โ”‚
โ”‚     (Business Logic, Use Cases, Entities, Repos)        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                           โ†“
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      Data Layer                          โ”‚
โ”‚  (Repository Impl, API, Local DB, Data Sources)         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                           โ†“
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                   Infrastructure                         โ”‚
โ”‚     (Dio, Retrofit, Isar, Firebase, MQTT)               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ“ Struktur Folder

lib/
โ”œโ”€โ”€ app.dart                    # Root application widget
โ”œโ”€โ”€ main_development.dart       # Development entry point
โ”œโ”€โ”€ main_staging.dart          # Staging entry point
โ”œโ”€โ”€ main_production.dart       # Production entry point
โ”‚
โ”œโ”€โ”€ core/                      # Core business logic (Domain + Data)
โ”‚   โ”œโ”€โ”€ auth/                 # Authentication domain
โ”‚   โ”‚   โ”œโ”€โ”€ auth_api.dart            # Retrofit API
โ”‚   โ”‚   โ”œโ”€โ”€ auth_repository.dart     # Repository interface
โ”‚   โ”‚   โ”œโ”€โ”€ auth_service.dart        # Business logic
โ”‚   โ”‚   โ””โ”€โ”€ models/                  # Domain models
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ product/              # Product domain
โ”‚   โ”œโ”€โ”€ inventory/            # Inventory domain
โ”‚   โ”œโ”€โ”€ transaction/          # Transaction domain
โ”‚   โ”œโ”€โ”€ dashboard/            # Dashboard domain
โ”‚   โ”œโ”€โ”€ payments/             # Payments domain
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ network/              # Networking infrastructure
โ”‚   โ”‚   โ”œโ”€โ”€ dio_client.dart          # Dio factory
โ”‚   โ”‚   โ”œโ”€โ”€ interceptors/            # HTTP interceptors
โ”‚   โ”‚   โ””โ”€โ”€ network_exceptions.dart  # Error handling
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ mqtt/                 # MQTT real-time sync
โ”‚   โ”œโ”€โ”€ firebase/             # Firebase services
โ”‚   โ”œโ”€โ”€ observers/            # BLoC observers
โ”‚   โ””โ”€โ”€ ...
โ”‚
โ”œโ”€โ”€ features/                 # Feature modules (Presentation)
โ”‚   โ”œโ”€โ”€ cashier/             # Kasir feature
โ”‚   โ”‚   โ”œโ”€โ”€ bloc/                   # BLoC state management
โ”‚   โ”‚   โ”œโ”€โ”€ pages/                  # UI pages
โ”‚   โ”‚   โ”œโ”€โ”€ widgets/                # Feature widgets
โ”‚   โ”‚   โ””โ”€โ”€ models/                 # UI models
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ inventory/           # Inventory feature
โ”‚   โ”œโ”€โ”€ dashboard/           # Dashboard feature
โ”‚   โ”œโ”€โ”€ auth/                # Auth UI
โ”‚   โ”œโ”€โ”€ home/                # Home feature
โ”‚   โ””โ”€โ”€ ...
โ”‚
โ”œโ”€โ”€ di/                      # Dependency Injection
โ”‚   โ”œโ”€โ”€ injection.dart              # GetIt setup
โ”‚   โ”œโ”€โ”€ injection.config.dart       # Generated DI config
โ”‚   โ””โ”€โ”€ network_module.dart         # Network DI module
โ”‚
โ”œโ”€โ”€ pkg/                     # Shared utilities & components
โ”‚   โ”œโ”€โ”€ component/                  # Reusable widgets
โ”‚   โ”œโ”€โ”€ theme/                      # Theme configuration
โ”‚   โ”œโ”€โ”€ utils/                      # Helper functions
โ”‚   โ””โ”€โ”€ common/                     # Constants
โ”‚
โ”œโ”€โ”€ database/                # Local database schemas
โ”‚   โ””โ”€โ”€ isar/                       # Isar collections
โ”‚
โ”œโ”€โ”€ i18n/                    # Internationalization
โ”‚   โ”œโ”€โ”€ strings.g.dart              # Generated translations
โ”‚   โ”œโ”€โ”€ strings_id.g.dart           # Indonesian
โ”‚   โ””โ”€โ”€ strings_en.g.dart           # English
โ”‚
โ””โ”€โ”€ middlewares/             # Router middlewares
    โ””โ”€โ”€ auth_middleware.dart        # Auth guard

๐ŸŽฏ Layer Responsibilities

1. Presentation Layer (features/)

Tanggung jawab:
  • Menampilkan UI
  • Menangani user interaction
  • Mengelola UI state dengan BLoC/Cubit
  • Tidak boleh mengakses data source langsung
Komponen:
  • Pages: Full screen widgets
  • Widgets: Reusable UI components
  • BLoC/Cubit: State management
  • Models: UI-specific models (jika berbeda dari domain)
Contoh:
// features/cashier/bloc/cashier_bloc.dart
class CashierBloc extends Bloc<CashierEvent, CashierState> {
  final ProductRepository _productRepository;
  
  CashierBloc(this._productRepository) : super(CashierInitial()) {
    on<LoadProducts>(_onLoadProducts);
  }
  
  Future<void> _onLoadProducts(
    LoadProducts event,
    Emitter<CashierState> emit,
  ) async {
    emit(CashierLoading());
    final result = await _productRepository.getProducts();
    result.fold(
      (failure) => emit(CashierError(failure.message)),
      (products) => emit(CashierLoaded(products)),
    );
  }
}

2. Domain Layer (core/*/)

Tanggung jawab:
  • Business logic
  • Domain models (entities)
  • Repository interfaces
  • Use cases (optional, untuk logic kompleks)
Prinsip:
  • Pure Dart (no Flutter dependencies)
  • Framework-agnostic
  • Testable
Contoh:
// core/product/product_repository.dart
abstract class ProductRepository {
  Future<Either<Failure, List<Product>>> getProducts();
  Future<Either<Failure, Product>> getProductById(String id);
  Future<Either<Failure, void>> createProduct(Product product);
}

3. Data Layer (core/*/)

Tanggung jawab:
  • Implementasi repository
  • API calls (Retrofit)
  • Local database operations (Isar)
  • Data mapping (DTO โ†” Entity)
  • Caching strategy
Komponen:
  • Repository Implementation: Concrete repository
  • API: Retrofit interfaces
  • Local Data Source: Isar collections
  • DTOs: Data Transfer Objects
Contoh:
// core/product/product_repository_retrofit.dart
class ProductRepositoryRetrofit implements ProductRepository {
  final Dio dio;
  final String baseUrl;
  
  @override
  Future<Either<Failure, List<Product>>> getProducts() async {
    try {
      final api = ProductApi(dio, baseUrl: baseUrl);
      final response = await api.getProducts();
      return Right(response.data);
    } on DioException catch (e) {
      return Left(NetworkException.fromDioError(e));
    }
  }
}

4. Infrastructure Layer

Tanggung jawab:
  • HTTP client (Dio)
  • Database (Isar)
  • Firebase services
  • MQTT client
  • External services

๐Ÿ”„ Data Flow

Request Flow (User Action โ†’ API)

User Interaction
      โ†“
   Widget
      โ†“
BLoC Event Dispatch
      โ†“
BLoC Event Handler
      โ†“
Repository Method Call
      โ†“
API Call (Retrofit)
      โ†“
Dio Interceptors Pipeline:
  - CorrelationInterceptor (X-Correlation-ID)
  - HeadersInterceptor (Common headers)
  - AuthInterceptor (Authorization token)
  - RefreshTokenInterceptor (Auto refresh)
  - RetryInterceptor (Retry on failure)
  - LoggingInterceptor (Logging)
      โ†“
HTTP Request โ†’ Backend
      โ†“
HTTP Response
      โ†“
Repository returns Either<Failure, Data>
      โ†“
BLoC emits new State
      โ†“
Widget rebuilds with new State

Offline-First Flow

User Action
      โ†“
BLoC Event
      โ†“
Repository checks local cache (Isar)
      โ†“
If cached: Return immediately
      โ†“
Background: Sync with API
      โ†“
Update local cache
      โ†“
Emit updated state

๐Ÿงฉ Design Patterns

1. Repository Pattern

Abstraksi akses data, memisahkan business logic dari data source.
abstract class ProductRepository {
  Future<Either<Failure, List<Product>>> getProducts();
}

class ProductRepositoryRetrofit implements ProductRepository {
  // Implementation with Retrofit
}

class ProductRepositoryMock implements ProductRepository {
  // Mock implementation for testing
}

2. BLoC Pattern

State management dengan event-driven architecture.
// Event
abstract class ProductEvent {}
class LoadProducts extends ProductEvent {}

// State
abstract class ProductState {}
class ProductLoading extends ProductState {}
class ProductLoaded extends ProductState {
  final List<Product> products;
  ProductLoaded(this.products);
}

// BLoC
class ProductBloc extends Bloc<ProductEvent, ProductState> {
  // Handle events and emit states
}

3. Dependency Injection

Menggunakan GetIt + Injectable untuk DI.
@module
abstract class NetworkModule {
  @LazySingleton()
  Dio dio(@Named('baseUrl') String baseUrl) => createDio(baseUrl: baseUrl);
  
  @LazySingleton(as: ProductRepository)
  ProductRepositoryRetrofit productRepository(Dio dio) {
    return ProductRepositoryRetrofit(dio: dio);
  }
}

4. Either Pattern (Functional Error Handling)

Menggunakan Dartz untuk error handling yang eksplisit.
Future<Either<Failure, Product>> getProduct(String id) async {
  try {
    final product = await api.getProduct(id);
    return Right(product);
  } catch (e) {
    return Left(ServerFailure('Failed to fetch product'));
  }
}

// Usage
final result = await repository.getProduct('123');
result.fold(
  (failure) => print('Error: ${failure.message}'),
  (product) => print('Success: ${product.name}'),
);

๐Ÿ” Security Architecture

1. Authentication Flow

Login Request
      โ†“
AuthRepository.login()
      โ†“
API returns: accessToken, refreshToken
      โ†“
Store in secure storage (Isar)
      โ†“
AuthInterceptor adds token to all requests
      โ†“
On 401 response:
  RefreshTokenInterceptor triggers
      โ†“
  Silent refresh with refreshToken
      โ†“
  Update tokens
      โ†“
  Retry original request

2. Token Management

  • Access Token: Short-lived (15 minutes)
  • Refresh Token: Long-lived (30 days)
  • Auto-refresh: Handled by RefreshTokenInterceptor
  • Storage: Encrypted in Isar database

๐Ÿ“ก Real-Time Architecture

MQTT Integration

App Start
      โ†“
MqttService.InitMQTT()
      โ†“
Connect to MQTT broker
      โ†“
Subscribe to topics:
  - branch/{branchId}/inventory
  - branch/{branchId}/transactions
  - user/{userId}/notifications
      โ†“
On message received:
  Parse payload
      โ†“
  Update local database (Isar)
      โ†“
  Emit BLoC event
      โ†“
  UI updates automatically

๐Ÿ—„๏ธ Database Architecture

Isar (Local NoSQL Database)

@collection
class ProductLocal {
  Id id = Isar.autoIncrement;
  
  @Index()
  late String productId;
  
  late String name;
  late double price;
  late int stock;
  
  @Index()
  late DateTime updatedAt;
}
Strategi:
  • Write-through cache
  • Background sync
  • Conflict resolution (last-write-wins)

๐ŸŽจ UI Architecture

Platform-Adaptive Design

if (Platform.isIOS) {
  return CupertinoApp.router(
    theme: iosTheme,
    routerConfig: GlobalRouter,
  );
} else {
  return MaterialApp.router(
    theme: androidTheme,
    routerConfig: GlobalRouter,
  );
}

Theme System

  • AppTheme: Centralized theme configuration
  • Theme Tailor: Type-safe theme extensions
  • Dark Mode: Full support

๐Ÿ“Š Observability

Logging

// AppLog - Structured logging
AppLog.i('category', 'message', meta: {'key': 'value'});
AppLog.e('category', 'error', exception, stackTrace);

// BLoC Observer
class AppBlocObserver extends BlocObserver {
  @override
  void onEvent(Bloc bloc, Object? event) {
    AppLog.i('bloc.${bloc.runtimeType}', 'event: $event');
  }
}

Crash Reporting

  • Firebase Crashlytics: Production crashes
  • AppLog: Development logging
  • Sentry (optional): Error tracking

๐Ÿงช Testing Architecture

test/
โ”œโ”€โ”€ unit/              # Unit tests (business logic)
โ”œโ”€โ”€ widget/            # Widget tests (UI)
โ”œโ”€โ”€ integration/       # Integration tests
โ””โ”€โ”€ mocks/             # Mock implementations

Next Steps


Arsitektur ini dirancang untuk:
  • โœ… Scalability
  • โœ… Maintainability
  • โœ… Testability
  • โœ… Separation of Concerns
  • โœ… Code Reusability