This Flutter application follows a hybrid architecture combining Model-View-Controller (MVC), Aspect-Oriented Programming (AOP), and Service-Oriented Architecture (SOA) principles. This integration ensures clean separation of concerns, reusability, maintainability, and scalability.
Your current architecture already follows MVC principles. The integration makes it explicit and formalized while maintaining all your existing AOP and SOA benefits.
┌─────────────────────────────────────────┐
│ VIEW (Screens/Widgets) │
│ - Uses ErrorHandlingMixin (AOP) │
│ - Uses ValidationMixin (AOP) │
│ - Consumes Providers via Provider │
└─────────────────────────────────────────┘
↕
┌─────────────────────────────────────────┐
│ CONTROLLER (Providers) │
│ - Uses Services (SOA) │
│ - Manages state │
└─────────────────────────────────────────┘
↕
┌─────────────────────────────────────────┐
│ MODEL (Services + Data Models) │
│ - Services use LoggingMixin (AOP) │
│ - Services are independent (SOA) │
│ - HTTP via InterceptorChain (AOP) │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ VIEW (Screens/Widgets) │
│ - BaseViewMixin (MVC + AOP) │
│ - ErrorHandlingMixin (AOP) │
│ - ValidationMixin (AOP) │
│ - Consumes Controllers via Provider │
└─────────────────────────────────────────┘
↕
┌─────────────────────────────────────────┐
│ CONTROLLER (Providers) │
│ - BaseController (MVC + AOP) │
│ - Uses Services (SOA) │
│ - Automatic logging & error handling │
└─────────────────────────────────────────┘
↕
┌─────────────────────────────────────────┐
│ MODEL (Services + Data Models) │
│ - BaseService (SOA) │
│ - LoggingMixin (AOP) │
│ - HTTP via InterceptorChain (AOP) │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ VIEW LAYER (UI) │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Screens, Widgets (Flutter UI Components) │ │
│ │ - Uses AOP: ErrorHandlingMixin, ValidationMixin│ │
│ │ - Consumes Controllers via Provider │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
↕ (State Management)
┌─────────────────────────────────────────────────────────┐
│ CONTROLLER LAYER (State) │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Providers (ChangeNotifier) │ │
│ │ - Uses AOP: LoggingMixin │ │
│ │ - Coordinates between View and Model │ │
│ │ - Delegates business logic to Services │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
↕ (Service Calls)
┌─────────────────────────────────────────────────────────┐
│ MODEL LAYER (Business Logic) │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Services (SOA) │ │
│ │ - Uses AOP: LoggingMixin │ │
│ │ - Independent, reusable services │ │
│ │ - Handle HTTP communication │ │
│ └───────────────────────────────────────────────────┘ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Data Models (Domain Objects) │ │
│ │ - User, Booking, Resource, etc. │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
↕ (HTTP Requests)
┌─────────────────────────────────────────────────────────┐
│ AOP INTERCEPTOR CHAIN (Cross-Cutting) │
│ ┌───────────────────────────────────────────────────┐ │
│ │ AuthInterceptor, LoggingInterceptor, │ │
│ │ ErrorInterceptor, RetryInterceptor │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────────┐
│ BACKEND SERVICES │
│ (auth-service, user-service, catalog-service, etc.) │
└─────────────────────────────────────────────────────────┘
- ✅ View: UI components (Screens, Widgets)
- ✅ Controller: State management (Providers)
- ✅ Model: Business logic (Services) + Data (Models)
- ✅ View: ErrorHandlingMixin, ValidationMixin, BaseViewMixin
- ✅ Controller: LoggingMixin (via BaseController)
- ✅ Model: LoggingMixin (via Services), HTTP Interceptors
- ✅ Services remain independent
- ✅ Clear service boundaries
- ✅ Reusable across controllers
- Existing code continues to work
- Migration is optional and gradual
- Backward compatible
- Automatic aspect registration
- Consistent logging across layers
- Centralized error handling
- Explicit MVC separation
- Better code organization
- Easier to understand and maintain
- BaseController handles common patterns
- Less repetitive code
- More consistent implementations
Purpose: Represents data and business logic
Components:
- Data Models:
User,Booking,Resource, etc. (inlib/models/) - Services (SOA):
AuthService,UserService,BookingService, etc. (inlib/services/)
AOP Integration:
- Services use
LoggingMixinfor automatic logging - Services use
BaseServicepattern (SOA)
Example:
// Model: Data class
class User {
final int id;
final String username;
// ...
}
// Model: Service (SOA)
class UserService with LoggingMixin { // AOP: Logging aspect
final ApiClient _apiClient = ApiClient.instance; // SOA: Service independence
Future<User> getUserById(int id) async {
logMethodEntry('getUserById', {'id': id}); // AOP: Logging
// Business logic
}
}Purpose: Represents UI components (Screens, Widgets)
Components:
- Screens:
LoginScreen,HomeScreen,AdminDashboard, etc. (inlib/screens/) - Widgets: Reusable UI components (in
lib/widgets/)
AOP Integration:
- Views use
ErrorHandlingMixinfor error handling - Views use
ValidationMixinfor form validation - Views consume Controllers via Provider
Example:
// View: Screen
class LoginScreen extends StatefulWidget {
// Uses ErrorHandlingMixin, ValidationMixin (AOP aspects)
// Consumes AuthProvider (Controller) via Provider
}
class _LoginScreenState extends State<LoginScreen>
with ErrorHandlingMixin, ValidationMixin { // AOP: Error handling, validation
@override
Widget build(BuildContext context) {
return Consumer<AuthProvider>( // MVC: View consumes Controller
builder: (context, authProvider, _) {
// UI rendering
},
);
}
}Purpose: Coordinates between View and Model, manages state
Components:
- Providers:
AuthProvider,UserProvider,BookingProvider, etc. (inlib/providers/) - Base Classes:
BaseController(inlib/core/mvc/base_controller.dart)
AOP Integration:
- Controllers use
LoggingMixinfor logging (viaBaseController) - Controllers use
executeWithErrorHandling()for automatic error handling - Controllers delegate business logic to Services (SOA)
Example:
// Controller: Provider
class AuthProvider extends BaseController { // MVC: Controller base class
final AuthService _authService = AuthService(); // MVC: Uses Model (Service)
@override
String get controllerName => 'AuthProvider';
Future<bool> login(String username, String password) async {
return await executeWithErrorHandling(() async { // AOP: Error handling
logMethodEntry('login'); // AOP: Logging
final response = await _authService.login(...); // MVC: Delegates to Model
// Update state
notifyListeners(); // MVC: Notify View
return true;
}) ?? false;
}
}AOP separates cross-cutting concerns (logging, error handling, authentication, validation) from business logic, allowing these concerns to be applied uniformly across the application.
- Purpose: Centralized interceptor management for HTTP requests/responses
- Pattern: Chain of Responsibility pattern
- Aspects Applied:
- Authentication (JWT token injection)
- Logging (request/response logging)
- Error Handling (error transformation)
- Retry Logic (automatic retry with exponential backoff)
AuthInterceptorEnhanced (lib/core/interceptors/auth_interceptor_enhanced.dart)
- Aspect: Authentication
- Cross-cutting Concern: JWT token management, user ID injection
- Applied To: All authenticated HTTP requests
- Implementation: Implements
Interceptorinterface
LoggingInterceptorEnhanced (lib/core/interceptors/logging_interceptor_enhanced.dart)
- Aspect: Logging
- Cross-cutting Concern: Request/response logging, performance timing
- Applied To: All HTTP requests/responses
- Implementation: Implements
Interceptorinterface
ErrorInterceptorEnhanced (lib/core/interceptors/error_interceptor_enhanced.dart)
- Aspect: Error Handling
- Cross-cutting Concern: Error transformation, user-friendly error messages
- Applied To: All HTTP responses and errors
- Implementation: Implements
Interceptorinterface
RetryInterceptorEnhanced (lib/core/interceptors/retry_interceptor_enhanced.dart)
- Aspect: Resilience
- Cross-cutting Concern: Automatic retry with exponential backoff
- Applied To: Failed HTTP requests (5xx errors, network errors)
- Implementation: Implements
Interceptorinterface
LoggingMixin (lib/core/mixins/logging_mixin.dart)
- Aspect: Logging
- Applied To: Services, Providers, and other classes
- Methods:
logDebug(),logInfo(),logWarning(),logError(),logMethodEntry(),logMethodExit()
ErrorHandlingMixin (lib/core/mixins/error_handling_mixin.dart)
- Aspect: Error Handling
- Applied To: Screens, Widgets, and UI components
- Methods:
handleError(),showErrorSnackBar(),showErrorDialog(),executeWithErrorHandling()
ValidationMixin (lib/core/mixins/validation_mixin.dart)
- Aspect: Validation
- Applied To: Forms and input validation
- Methods:
validateEmail(),validatePassword(),validateRequired(),validateUsername(), etc.
- Purpose: Centralized registry for managing AOP aspects
- Usage: Register and retrieve aspects at runtime
- Benefits: Dynamic aspect management, aspect discovery
- Purpose: Apply aspects to classes (documentation and future extensibility)
- Note: In Dart, aspects are primarily applied via mixins at compile time
HTTP Request
↓
[AuthInterceptor] → Add JWT token, X-User-Id header
↓
[LoggingInterceptor] → Log request
↓
[HTTP Client] → Execute request
↓
[ErrorInterceptor] → Check for errors, transform
↓
[LoggingInterceptor] → Log response
↓
[RetryInterceptor] → Retry if needed (wraps entire flow)
↓
Response/Error
User Action (View)
↓
Controller Method (with LoggingMixin - AOP)
↓
Service Method (with LoggingMixin - AOP)
↓
HTTP Request
↓
Interceptor Chain (AOP)
├─ AuthInterceptor (adds JWT)
├─ LoggingInterceptor (logs request)
├─ ErrorInterceptor (handles errors)
└─ RetryInterceptor (retries on failure)
↓
Backend Service
↓
Response
↓
Interceptor Chain (AOP)
├─ ErrorInterceptor (transforms errors)
└─ LoggingInterceptor (logs response)
↓
Service (Model) - processes response
↓
Controller - updates state
↓
View - rebuilds UI
| Layer | AOP Aspect | Implementation | Purpose |
|---|---|---|---|
| View | Error Handling | ErrorHandlingMixin |
User-friendly error messages |
| View | Validation | ValidationMixin |
Form input validation |
| Controller | Logging | LoggingMixin (via BaseController) |
Method entry/exit logging |
| Controller | Error Handling | BaseController.executeWithErrorHandling() |
Automatic error handling |
| Model (Service) | Logging | LoggingMixin |
Service method logging |
| Model (Service) | Authentication | AuthInterceptor |
JWT token injection |
| Model (Service) | Retry Logic | RetryInterceptor |
Automatic retry |
| All Layers | HTTP Interceptors | InterceptorChain |
Cross-cutting HTTP concerns |
SOA organizes application functionality into independent, reusable services, each responsible for a specific business domain.
Each service is a standalone, independent component:
- AuthService: Authentication and user registration
- UserService: User management operations
- ResourceService: Resource catalog operations
- BookingService: Booking management operations
- PolicyService: Policy management operations
- NotificationService: Notification operations
- AnalyticsService: Analytics and reporting operations
All services follow a consistent pattern:
- Use shared
ApiClientinstance (service independence) - Implement logging via
LoggingMixin(AOP aspect) - Handle errors consistently
- Provide health check methods
- Follow RESTful API patterns
- Protocol: HTTP/REST
- Format: JSON
- Gateway: API Gateway pattern (all requests go through
ApiClient) - Authentication: Handled by
AuthInterceptor(AOP aspect)
Each service corresponds to a backend microservice:
Frontend Service → Backend Microservice
─────────────────────────────────────────────────
AuthService → auth-service
UserService → user-service
ResourceService → catalog-service
BookingService → booking-service
PolicyService → policy-service
NotificationService → notification-service
AnalyticsService → analytics-service
class ServiceName with LoggingMixin { // AOP: Logging aspect
// Shared API client (SOA independence)
final ApiClient _apiClient = ApiClient.instance;
// Service methods (SOA operations)
Future<ReturnType> operationName(Parameters params) async {
logMethodEntry('operationName', params); // AOP: Logging
try {
// Business logic
final response = await _apiClient.method(endpoint, ...);
// AOP: Error handling via ErrorInterceptor (automatic)
// AOP: Logging via LoggingInterceptor (automatic)
// Process response
logMethodExit('operationName', result); // AOP: Logging
return result;
} catch (e, stackTrace) {
logError('Operation error', e, stackTrace); // AOP: Logging
rethrow; // AOP: Error handling
}
}
// Health check (SOA service discovery)
Future<String> healthCheck() async { ... }
}- Model Layer = Services (SOA): Business logic organized as independent services
- Controller Layer: Uses services (doesn't contain business logic)
- View Layer: Doesn't directly access services (goes through controllers)
// MVC: View
class BookingScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<BookingProvider>( // MVC: View → Controller
builder: (context, provider, _) {
return ListView(
children: provider.bookings.map((booking) =>
BookingCard(booking: booking)
).toList(),
);
},
);
}
}
// MVC: Controller
class BookingProvider extends BaseController { // MVC: Controller
final BookingService _bookingService = BookingService(); // SOA: Service
@override
String get controllerName => 'BookingProvider';
Future<void> loadBookings() async {
await executeWithErrorHandling(() async { // AOP: Error handling
logMethodEntry('loadBookings'); // AOP: Logging
final bookings = await _bookingService.getAllBookings(); // SOA: Service call
_bookings = bookings;
notifyListeners(); // MVC: Notify View
logMethodExit('loadBookings', '${bookings.length} bookings');
});
}
}
// SOA: Service (Model)
class BookingService with LoggingMixin { // AOP: Logging
final ApiClient _apiClient = ApiClient.instance; // SOA: Shared client
Future<List<Booking>> getAllBookings() async {
logMethodEntry('getAllBookings'); // AOP: Logging
final response = await _apiClient.get('/api/bookings'); // SOA: HTTP call
// Process response
return bookings;
}
}1. User Action (View)
↓
2. Controller Method (BaseController - MVC)
- Uses executeWithErrorHandling() (AOP)
- Logs method entry/exit (AOP)
↓
3. Service Method (SOA)
- Uses LoggingMixin (AOP)
- Makes HTTP request
↓
4. HTTP Interceptor Chain (AOP)
- AuthInterceptor (adds JWT)
- LoggingInterceptor (logs request)
- ErrorInterceptor (handles errors)
- RetryInterceptor (retries on failure)
↓
5. Backend Service (SOA)
↓
6. Response flows back through interceptors (AOP)
↓
7. Service processes response (SOA)
↓
8. Controller updates state (MVC)
↓
9. View rebuilds (MVC)
-
Cross-Cutting Concerns Applied Automatically
- All services automatically get logging (via
LoggingMixin) - All HTTP requests automatically get authentication (via
AuthInterceptor) - All errors are handled consistently (via
ErrorInterceptor)
- All services automatically get logging (via
-
Separation of Concerns
- Business logic (services) is separate from cross-cutting concerns (interceptors/mixins)
- Services focus on domain logic
- AOP handles infrastructure concerns
-
Reusability
- Interceptors are reusable across all services
- Mixins can be applied to any class
- No code duplication for cross-cutting concerns
-
Clear Layer Separation
- View: UI components with AOP aspects (ErrorHandlingMixin, ValidationMixin)
- Controller: State management with AOP aspects (LoggingMixin, error handling)
- Model: Services (SOA) with AOP aspects (LoggingMixin, HTTP interceptors)
-
Consistent Patterns
- All layers use AOP aspects appropriately
- Controllers coordinate between Views and Services
- Services remain independent (SOA)
-
Maintainability
- Changes in one layer don't affect others
- AOP aspects can be modified without changing business logic
- Services can be modified without affecting UI
- MVC: Separates UI (View), State (Controller), and Logic (Model)
- AOP: Separates cross-cutting concerns (logging, error handling)
- SOA: Separates business domains (services)
- AOP: Aspects (interceptors, mixins) reusable across all layers
- SOA: Services reusable across different controllers/views
- MVC: Controllers reusable with different views
- Clear layer boundaries
- Easy to locate and modify code
- Changes in one layer don't affect others
- AOP: Changes to logging/error handling affect entire app automatically
- SOA: Changes to one service don't affect others
- Each layer can be tested independently
- Mock services for controller testing
- Mock controllers for view testing
- AOP: Aspects can be tested independently
- SOA: Services can be mocked and tested in isolation
- Easy to add new services (SOA)
- Easy to add new aspects (AOP)
- Easy to add new views/controllers (MVC)
- AOP: New aspects can be added without modifying existing code
- SOA: New services can be added without affecting existing ones
-
lib/core/mvc/base_controller.dart- Base class for Controllers (Providers)
- Integrates AOP logging and error handling
- Reduces boilerplate
-
lib/core/mvc/base_model.dart- Base class for Models (optional)
- Integrates with Services and AOP
-
lib/core/mvc/base_view.dart- Mixin for Views
- Registers views with AspectRegistry (AOP)
-
Create Model (Service):
class MyService with LoggingMixin { // AOP: Logging final ApiClient _apiClient = ApiClient.instance; // SOA Future<MyData> getData() async { logMethodEntry('getData'); // AOP: Logging final response = await _apiClient.get('/api/data'); // SOA: HTTP call // Process response return data; } }
-
Create Controller:
class MyProvider extends BaseController { // MVC: Controller final MyService _service = MyService(); // MVC: Uses Model @override String get controllerName => 'MyProvider'; List<MyData> _data = []; List<MyData> get data => _data; Future<void> loadData() async { await executeWithErrorHandling(() async { // AOP: Error handling _data = await _service.getData(); // SOA: Service call notifyListeners(); // MVC: Notify View }); } }
-
Create View:
class MyScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Consumer<MyProvider>( // MVC: View consumes Controller builder: (context, provider, _) { if (provider.isLoading) { return LoadingIndicator(); } return ListView( children: provider.data.map((item) => MyCard(data: item) ).toList(), ); }, ); } }
-
Register Provider:
MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => MyProvider()), ], )
- Migrate Providers one at a time to
BaseController - No rush, existing code works fine
- Benefits accumulate as you migrate
- Use MVC base classes for new features
- Keep existing code as-is
- Natural evolution over time
- Migrate all Providers to
BaseController - Maximum consistency
- Requires more upfront work
- Interceptor interface defined
- Interceptor chain implemented
- Auth interceptor (JWT token injection)
- Logging interceptor (request/response logging)
- Error interceptor (error transformation)
- Retry interceptor (automatic retry)
- Logging mixin (method logging)
- Error handling mixin (UI error handling)
- Validation mixin (form validation)
- Aspect registry (aspect management)
- Aspect weaver (aspect application)
- BaseController with AOP integration (MVC)
- Base service pattern defined
- AuthService (authentication domain)
- UserService (user management domain)
- ResourceService (resource catalog domain)
- BookingService (booking management domain)
- PolicyService (policy management domain)
- NotificationService (notification domain)
- AnalyticsService (analytics domain)
- Service independence (shared ApiClient)
- Consistent service interface
- Health check methods
- BaseController class (with AOP integration)
- BaseModel class (with SOA integration)
- BaseViewMixin (with AOP integration)
- View layer (Screens, Widgets)
- Controller layer (Providers)
- Model layer (Services + Data Models)
- Clear separation of concerns
- Keep aspects focused: Each aspect should handle one concern
- Use mixins for compile-time aspects: Better performance
- Use interceptors for runtime aspects: More flexible
- Document aspect application: Clear what aspects are applied where
- One service per domain: Clear service boundaries
- Service independence: Services don't depend on each other
- Consistent interface: All services follow same pattern
- Error handling: Services throw exceptions, let AOP handle them
- Logging: All services use LoggingMixin (AOP aspect)
- Controller coordinates: Controllers coordinate between View and Model
- No business logic in View: Views only handle UI
- No business logic in Controller: Controllers delegate to Services
- Use BaseController: Inherit from BaseController for AOP benefits
- Consistent state management: Use Provider pattern consistently
| Pattern | Purpose | Location | AOP Integration |
|---|---|---|---|
| MVC | Separation of UI, State, Logic | Entire app | All layers use AOP aspects |
| AOP | Cross-cutting concerns | lib/core/interceptors/, lib/core/mixins/ |
Applied to all MVC layers |
| SOA | Service independence | lib/services/ |
Services use AOP logging |
lib/
├── core/
│ ├── aspects/ # AOP: Aspect registry and weaver
│ ├── interceptors/ # AOP: HTTP interceptors
│ ├── mixins/ # AOP: Logging, Error handling, Validation
│ ├── mvc/ # MVC: Base classes (Controller, Model, View)
│ └── services/ # SOA: Base service
├── models/ # MVC: Data models
├── services/ # MVC: Services (SOA) - Business logic
├── providers/ # MVC: Controllers - State management
└── screens/ # MVC: Views - UI components
- Performance monitoring aspect
- Caching aspect
- Rate limiting aspect
- Request/response transformation aspect
- Service discovery mechanism
- Service versioning
- Service health monitoring
- Service circuit breaker pattern
- Enhanced BaseController with more utilities
- View state management improvements
- Better error propagation patterns
Yes, MVC integrates perfectly with your existing AOP and SOA architecture!
The integration:
- ✅ Maintains all existing AOP and SOA benefits
- ✅ Enhances with explicit MVC structure
- ✅ Reduces boilerplate code
- ✅ Improves maintainability and consistency
- ✅ Remains backward compatible
Your architecture is now a hybrid MVC + AOP + SOA architecture, where:
- MVC provides clear layer separation
- AOP handles cross-cutting concerns
- SOA ensures service independence
All three patterns complement each other perfectly! 🎉
The integration of MVC + AOP + SOA provides:
- ✅ Clean separation of concerns (MVC)
- ✅ Reusable cross-cutting functionality (AOP)
- ✅ Independent, scalable services (SOA)
- ✅ Maintainable, testable codebase
- ✅ Easy to extend and modify
This architecture ensures that each pattern complements the others, creating a robust, scalable, and maintainable application. The three patterns work together seamlessly:
- MVC provides clear layer separation
- AOP handles cross-cutting concerns across all layers
- SOA ensures service independence and reusability
All three patterns enhance each other, resulting in a well-architected, production-ready application.