Overview
The Rodando Driver application is built with Angular 17+ and Ionic Framework, following a modern, modular architecture designed for maintainability and scalability. The app implements a feature-based folder structure with clear separation of concerns.Project Structure
The architecture follows Angular’s standalone components approach, eliminating the need for NgModules and enabling tree-shaking for smaller bundle sizes.
Architectural Principles
1. Feature-Based Organization
The codebase is organized by features rather than technical layers. Each feature module is self-contained and can be lazy-loaded:Benefits: Improved code organization, faster initial load times, better developer experience with clear boundaries between features.
2. Core Module Pattern
Thecore/ directory contains singleton services that should only be instantiated once:
- Guards:
authGuardWithRefresh,guestGuardfor route protection - Interceptors: HTTP request/response transformation
auth.interceptor.ts- Adds JWT tokens to requestsapi-error.interceptor.ts- Centralizes error handlinghttp-logging.interceptor.ts- Request/response logging
- Services: Business logic and data access
- HTTP services for API communication
- WebSocket service for real-time updates
- Storage services for persistence
3. Shared Components
Reusable UI components live in two locations:app/components/- Application-wide shared componentsapp/shared/components/- Cross-feature reusable components
base-map- Map integration componentcontent-card- Card layout componentfloating-button- FAB componentreusable-modal- Generic modal wrapperdinamic-header- Dynamic header component
4. Layout Components
The app uses layout components to provide consistent UI structure:Default Layout
DefaultLayoutComponent- Standard layout with header, tabs, and sidebarMapLayoutComponent- Fullscreen layout for map views
Lazy Loading Strategy
Route-based code splitting
Features are lazy-loaded using Angular’s
loadComponent and loadChildren APIs.Example: Auth Module Lazy Loading
Dependency Injection
The app uses Angular’s moderninject() function for dependency injection:
The
inject() function enables better tree-shaking and allows dependency injection outside of constructor parameters.Core Services Architecture
HTTP Services
All HTTP communication goes through dedicated service classes:AuthService- Authentication endpointsTripApiService- Trip CRUD operationsSecureStorageService- Secure data persistence
@Injectable({ providedIn: 'root' }) to ensure singleton instances.
WebSocket Service
Real-time communication is handled byDriverWsService using Socket.io:
Error Handling
Centralized Error Interceptor
All HTTP errors are intercepted and normalized:State-level Error Handling
Each store maintains its own error state:Configuration Management
Environment-specific configuration:Development Tools
Logger Service
Centralized logging for debugging:HTTP Logging Interceptor
Logs all HTTP requests/responses in development:Performance Optimizations
Lazy Loading
Features loaded on-demand, reducing initial bundle size by ~60%.
Tree Shaking
Standalone components enable better dead code elimination.
Signal-based State
Angular Signals provide fine-grained reactivity with minimal overhead.
OnPush Detection
Components use OnPush change detection for better performance.
Security Patterns
- JWT Authentication: Tokens stored securely, automatically attached to requests
- Route Guards: Prevent unauthorized access to protected routes
- HTTP-Only Cookies: Refresh tokens stored in HTTP-only cookies (web)
- Secure Storage: Sensitive data encrypted in native storage (mobile)
- CORS Protection: API endpoints enforce origin validation
Testing Strategy
Each architectural layer has corresponding test files:Next Steps
Routing Structure
Learn about route configuration, guards, and navigation patterns.
State Management
Explore NgRx Signal Store implementation and state patterns.
WebSocket Integration
Understand real-time communication architecture.
API Integration
API endpoints and integration patterns.
Architectural Decisions
Why Standalone Components?
- Simpler mental model: No NgModule boilerplate
- Better tree-shaking: Unused components fully eliminated
- Faster compilation: Smaller dependency graphs
- Future-proof: Angular’s recommended approach
Why Signal Store over NgRx Store?
- Simpler API: Less boilerplate, easier to learn
- Better performance: Fine-grained reactivity
- Type safety: Full TypeScript inference
- Smaller bundle: No runtime dependencies
Why Feature-based folders?
- Scalability: Easy to add new features without restructuring
- Team collaboration: Clear ownership boundaries
- Code splitting: Natural alignment with lazy loading
- Discoverability: Related code lives together