Skip to content

feat(arch): migrate to multi-module architecture with convention plugins#12

Merged
mustalk merged 18 commits intomainfrom
refactor/multi-module-arch
Sep 29, 2025
Merged

feat(arch): migrate to multi-module architecture with convention plugins#12
mustalk merged 18 commits intomainfrom
refactor/multi-module-arch

Conversation

@mustalk
Copy link
Copy Markdown
Owner

@mustalk mustalk commented Jun 16, 2025

🏗️ Multi-Module Architecture Migration

Overview

This pull request implements a complete migration from a single :app module to a production-ready multi-module Android architecture. The migration was executed progressively over 17 commits while maintaining the application fully functional throughout the entire process, showcasing enterprise-scale patterns and scalable architecture design.

Migration Implementation

🏗️ Architecture & Modularization

  • Hybrid multi-layered strategy implemented: Combined feature-based and layer-based modularization across 10 modules
  • Clean Architecture compliance: Established clear architectural boundaries with inward-flowing dependencies
  • Platform independence: Created pure Kotlin modules for domain and common layers

🛠️ Convention Plugins & Build Logic

  • Production-ready build-logic infrastructure: Established mature convention plugins following Android best practices
  • Centralized configuration: Implemented consistent module setup across Android/JVM modules with automated quality enforcement
  • Reusable foundation: Created build logic that can be easily adapted for new projects
  • Performance optimizations: Integrated configuration cache compatibility and parallel execution support

📋 Progressive Migration Phases

  • Phase 1-2: Established build logic and extracted core model/domain/data layers with proper DIP resolution
  • Phase 3-4: Migrated common utilities and shared UI components with multi-module resource management
  • Phase 5-6: Created dual testing infrastructure and extracted feature modules (Dashboard, Mission)
  • Phase 7-8: Finalized app module cleanup and implemented advanced testing/coverage systems

🧪 Testing & Quality Infrastructure

  • 200 test suite: Migrated and enhanced all tests with structured test data patterns across modules
  • Three-tier coverage system: Implemented JVM, Android, and Overall Jacoco reporting with dynamic module detection
  • Professional test reporting: Created module categorization with success rate tracking and clickable coverage reports
  • Quality enforcement: Integrated Detekt, KtLint, Spotless across all modules via convention plugins

🚀 CI/CD & Performance

  • Multi-module CI optimization: Enhanced GitHub Actions pipeline with parallel execution and smart caching
  • Memory optimization: Implemented optimized JVM settings for large multi-module projects
  • Dynamic module detection: Created adaptive systems that automatically adjust to project structure changes
  • Developer experience: Added project utilities, coverage commands, and performance guidance

Technical Achievements

Complete 10-module architecture with clear boundaries
Maintained 100% functionality throughout the entire migration process
Enhanced test coverage from single-module to structured multi-module testing (200 tests)
Implemented convention plugins providing consistent configuration across all modules
Optimized CI/CD pipeline with performance improvement through parallel execution
Created reusable build-logic infrastructure suitable for new projects setup

Multi-Module Structure:

:app                    # Application entry point, navigation, DI coordination
├── :feature:dashboard  # Dashboard feature with mission results display
├── :feature:mission    # New Mission feature with JSON/Builder input modes
└── :core:*             # Shared infrastructure layers
    ├── :core:model           # Domain entities (pure Kotlin)
    ├── :core:common          # Shared utilities (pure Kotlin)
    ├── :core:domain          # Business logic interfaces (pure Kotlin)
    ├── :core:data            # Data implementations (Android)
    ├── :core:ui              # Shared UI components (Android)
    ├── :core:testing-jvm     # JVM testing utilities
    └── :core:testing-android # Android testing utilities

Benefits for Future Development

🎯 Scalable foundation supporting independent feature development and team growth
🎯 Production-ready patterns suitable for enterprise-scale applications
🎯 Reusable build infrastructure accelerating new project setup
🎯 Enhanced testing strategies with evidence-based quality metrics
🎯 Performance optimizations for large multi-module development workflows
🎯 Clear separation of concerns enabling parallel team development


This migration successfully transforms the project into a production-ready, scalable multi-module architecture while preserving all original functionality and establishing patterns suitable for enterprise-grade Android development.

mustalk added 18 commits June 2, 2025 12:07
…etup

- Add build-logic module with 7 convention plugins for consistent build configuration
- Implement marsrover.android.application plugin with Compose and Java 11 support
- Add marsrover.android.hilt plugin with automatic dependency injection setup
- Create marsrover.kotlin.library plugin for pure Kotlin modules
- Integrate quality tools: detekt and spotless convention plugins with direct coordinates
- Add QualityGateConventionPlugin for automated preBuild checks (CI-skippable)
- Remove legacy codestyle.gradle approach in favor of convention plugins
- Renamed lib.versions.toml back to its default name
- Setup build-logic to access main project version catalog
- Update :app module to temporarily use hardcoded dependency coordinates and versions for a smooth transition, so we can have a progressive migration with the app module functional and working during the migration process.

Foundation setup ready for the multi-module migration.
- Create :core:model JVM library module with proper convention plugin setup
- Move Direction, Position, Plateau, Rover from domain.model to core.model package
- Update all import statements throughout app module to use new package
- Add :core:model dependency to :app module and include in settings.gradle.kts
- Verify module compilation, app assembly, and tests work correctly

This successfully migrates the domain layer models, and the app remains fully functional.
- Create RoverMissionInstructions domain model
- Establish :core:domain module with pure Kotlin services and domain interfaces:
  * JsonParser interface returning RoverMissionInstructions
  * InputValidator interface using RoverMissionInstructions for validation
  * InputValidatorImpl with embedded validation constants and proper RoverError handling
  * RoverMovementService & RoverMovementServiceImpl moved from app domain layer
  * MarsRoverRepository interface using domain models and Result<String> return type
  * RoverError moved to core.domain.error package

- Establish :core:data module with DTO<->Domain mapping implementations:
  * Move DTOs (MarsRoverInput, TopRightCorner, RoverPosition) with serialization support
  * Move network models (MissionResponse, ErrorDetails) for API responses
  * JsonParserImpl implements domain interface, maps DTO -> RoverMissionInstructions
  * MarsRoverApiService moved with updated package references
  * MarsRoverRepositoryImpl maps RoverMissionInstructions <-> DTOs for network calls
  * DataModule provides Hilt bindings for data layer implementations to domain interfaces

- Keep :core:domain as pure Kotlin module and a clean dependency flow :core:data -> :core:domain -> :core:model

This successfully migrates the core domain and data architecture and the app remains fully functional.
- Create :core:common JVM library module for shared utilities
- Extract Constants with Network, Validation, UI, HttpStatus, and Examples configurations
- Extract NetworkResult monad for functional network operation handling with Success/Error/Loading states
- Extract exception classes (NetworkException, JsonParsingException, MissionExecutionException, ApiSimulationException)
- Adapt NetworkResult for pure Kotlin environment (removed Retrofit-specific dependencies)
- Update MarsRoverRepository interface in core:domain to return NetworkResult<String> instead of Result<String>
- Update MarsRoverRepositoryImpl in core:data to use NetworkResult.safeCall() for proper error wrapping
- Add core:common dependencies to core:domain and core:data modules
- Maintain comprehensive test coverage with NetworkResultTest in core:common
- Preserve clean architecture: core:common as pure Kotlin module with no Android dependencies

This successfully migrates common utilities and prepares NetworkResult integration for next steps of the migration, the app remains fully functional.
- Update app module dependencies to include core:data and core:common
- Refactor ExecuteRoverMissionUseCase to use core:domain interfaces and RoverMissionInstructions
- Refactor ExecuteNetworkMissionUseCase to use JsonParser and MarsRoverRepository from core:domain
- Update MissionSimulationInterceptor to use domain-driven flow with proper DIP
- Update all imports related to the migrated core: modules throughout the :app module

This successfully migrates the app layer to use the new core architecture with proper DIP, and the app remains fully functional.
…e modules

- Move ExecuteRoverMissionUseCase and ExecuteNetworkMissionUseCase to core:domain
- Move MissionSimulationInterceptor and NetworkModule to core:data
- Move remaining core module tests to their respective module test directories
- Update all imports throughout app to reference new core module locations
- Add test dependencies to :core:data module (junit, mockk, truth, coroutines-test)
- Update Hilt DI configuration to provide use cases from new locations
- Move Json provider from NetworkModule to DataModule for better separation of concerns

This successfully migrates use cases, data infrastructure, and test infrastructure to core modules, the app remains fully functional.
…ation

Legacy duplicates were retained during migration to maintain app functionality while core modules were established

- Remove legacy core/utils directory (Constants, NetworkResult, exceptions) - duplicates of core:common
- Remove legacy domain directory (use cases, services, validators, models) - duplicates of core:domain
- Remove legacy data directory (repositories, DTOs, network models) - duplicates of core:data
- Remove empty network directory after interceptor migration to core:data
- App module now contains only app-specific concerns: MainActivity, Application, UI, navigation, ViewModels
- Verify all core modules build successfully and app assembles correctly

This successfully cleanup the remaining migration legacy files, the app remains fully functional.
…nfiguration

- Create AndroidLibraryComposeConventionPlugin for Android library modules with Compose
- Create AndroidApplicationComposeConventionPlugin for Android application modules with Compose
- Register new convention plugins in build-logic build.gradle.kts and libs.versions.toml

This establishes the foundation for compose-enabled modules following Android best practices with performance optimization.
…i-module resource management

- Create :core:ui module with marsrover.android.library.compose convention plugin
- Extract shared UI components to core:ui
- Move theme files to core.ui.theme package
- Implement multi-module resource management with proper UI strings and constants:
  * Create core:ui strings.xml following multi-module naming conventions with prefixes
  * Extract UI-specific constants from core:common to core:ui for better separation of concerns
  * Use core:ui string resources and UiConstants instead of external dependencies
  * Organize resources by functional groups (loading states, accessibility, button labels)
- Migrate UI component tests to core:ui module with optimal string resource implementation for maintainable testing:
  * Update all UI tests to use the strings.xml instead of hardcoded strings
  * Use 7 generic test strings to serve 50 tests across all UI components
  * Implement maximum string reuse (test_content_example, test_title_example, etc.)
  * Verify all tests still pass after the refactors.
- Update all app imports to use core.ui.components.* throughout the application
- Migrate UI component tests to core:ui module with updated package references

This successfully migrates shared UI infrastructure to core modules with proper resource management, the app remains fully functional
…tructured test data

- Create :core:testing-android Android library module with marsrover.android.library and hilt convention plugins
- Create :core:testing-jvm JVM library module for pure Kotlin/domain testing
- Implement MarsTestRunner with HiltTestApplication for dependency injection in Android tests
- Add MainDispatcherRule for coroutine testing with controlled test dispatchers
- Add TestDispatchersModule for automatic test dispatcher injection through Hilt DI in Android tests
- Create structured test data split by core layers (DomainTestData and MarsRoverTestData) for enhanced test suite maintainability
- Setup platform-specific testing dependencies (JVM: junit, mockk, truth + Android: hilt-testing, androidx-junit)
- Update app module to use com.mustalk.seat.marsrover.core.testing.android.MarsTestRunner
- Remove legacy test infrastructure (HiltTestRunner, TestMarsRoverApplication) from app module
- Establish multi-module testing foundation with clear separation of concerns

This successfully creates a centralized testing infrastructure with proper JVM/Android separation and structured test data, maintaining full app functionality.
…zed test data

- Replace all hardcoded JSON strings with structured test data objects from core:testing-jvm
- Add test data objects for all use case scenarios (boundary tests, error cases, edge cases)
- Enhance DomainTestData with specific test objects for each scenario
- Add missing tests on ExecuteRoverMissionUseCaseTest
- All tests now use consistent structured test data patterns instead of hardcoded values

Domain and data tests now follow consistent patterns with centralized test data for improved maintainability.
- Create AndroidFeatureConventionPlugin for reusable feature module configuration and dependencies
- Create :feature:dashboard module with directory structure and dependencies
- Move DashboardScreen, DashboardViewModel, DashboardUiState, and MissionResult to feature module
- Move dashboard components (MissionResultCard, NewMissionFab) to feature module
- Move all dashboard tests (unit and UI) to feature module with testing infrastructure integration
- Update app navigation to use feature module imports instead of app module
- Remove all dashboard code from app module and update dependencies
- Establish resource management pattern using core:ui string resources with proper naming convention
- Apply established testing patterns with structured test data
- Ensure all tests pass and builds are successful across all modules

This successfully completes the :feature:dashboard module migration, with proper resource management, the app remains fully functional.
- Create :feature:mission module with directory structure and dependencies
- Move NewMissionScreen, NewMissionViewModel, NewMissionUiState, and InputMode to feature module
- Move BuilderInputsForm component with all sub-components and validation logic
- Move all mission tests (unit and UI) to feature module with testing infrastructure integration
- Add feature-specific string resources with feature_mission_ prefix
- Implement resource IDs ViewModel string localization
- Add missing movement action strings to core:ui for reusable rover commands
- Update app navigation to use feature module imports instead of app module
- Remove all mission code from app module and update dependencies
- Apply established resource management and testing patterns with structured test data

This successfully completes the :feature:mission module migration, with proper resource management, the app remains fully functional.
…StringResourceProvider

- Create StringResourceProvider interface and implementation in core:ui module for shared access across features
- Add Hilt plugin to core:ui module and create StringResourceModule for dependency injection
- add parameterized string resources with dynamic content support
- Update NewMissionViewModel to use StringResourceProvider for all mission execution messages
- Add proper parameter support for all dynamic content (error details, positions, coordinates)
- Ensure null safety with "Unknown error" fallback for nullable exception messages
- Implement test infrastructure with an organized mock setup
- Establish consistent localization patterns ready for multiple languages and RTL support

All ViewModel strings now use proper parameterized localization with centralized string resource management and test coverage.
…uild infrastructure

- Streamline :app module to exclusively contain top-level concerns (MainActivity, Application, DI, Navigation)
- Optimize :app module's build.gradle.kts, leveraging convention plugins and reducing app-specific dependencies
- Optimize convention plugins to centralize dependencies and eliminate some duplications across modules
- Create AppUtilsConventionPlugin for app utilities, replacing former utils.gradle.kts tasks
- Fix MainActivity previews to display realistic content using DashboardContent with an empty state
- Ensure all Unit and UI tests pass across the entire multi-module project

This completes the app module migration and build optimization, resulting in a lean app module, robust convention plugin architecture, optimized dependencies, and a fully functional multi-module application.
…cture with CI integration

- Establish project-wide Jacoco test coverage coverage system via dedicated convention plugins applied across all 8 modules
- Centralize Jacoco configuration in build-logic with dynamic module detection, replacing hardcoded module lists for better maintainability
- Implement three-tier aggregated coverage reporting system:
  - JVM aggregate report for domain logic (pure Kotlin/JVM modules)
  - Android aggregate report for UI/app logic with smart module filtering
  - Overall aggregate report combining all modules with CI-optimized data-only variant
  - Unit-test-only Android coverage report for fast development workflow
- Create enhanced test execution infrastructure:
  - Add convenience tasks (testAllModules, testAllWithCoverage, testCompleteSuite, testCompleteSuiteWithCoverage, generateOverallCoverageReport) with dynamic module detection
  - Smart UI test detection for modules with androidTest source sets
  - Professional test summaries with per-module statistics and clickable coverage report paths
- Integrate quality checks (Detekt, Spotless) directly into convention plugins, ensuring extensive code analysis across all modules
- Optimize CI/CD workflow with memory-optimized steps:
  - Separate unit tests, UI tests, and coverage generation for resource-constrained environments
  - Enhanced Gradle performance with caching, parallel execution, and optimized memory settings
  - CI workflow adaptation for ALL tests execution using convenience tasks
  - Overall aggregated Jacoco report generation with PR comment integration
- Add developer experience utilities:
  - Project information tasks (showCoverageInfo, showTestInfo, printProjectStructure) with dynamic module detection
  - Configuration cache compatibility for optimal build performance
  - Pattern-based execution data collection for device-agnostic coverage
  - Smart filtering to exclude modules without relevant test sources for accurate coverage percentages
- Update documentation (README) to reflect new three-tier coverage system and testing workflows

This provides a robust, scalable, and memory-optimized multi-module testing infrastructure with three-tier coverage reporting,
extensive quality checks, enhanced developer experience, proper task separation for different workflows, and optimized CI/CD
pipeline for improved code quality insights and developer productivity.

(Note: Iterative development commits for this feature were squashed into this commit to maintain a clean and focused git history.)
…nViewModel

- Refactor `NewMissionViewModel` to implement proper concurrency control using `Job` tracking, preventing race conditions during mission execution.
- Consolidate `StateManager` and `MissionExecutor` helper classes directly into the ViewModel, simplifying the architecture and reducing boilerplate.
- Ensure that any ongoing mission is canceled before a new one starts or when the input mode is switched, improving UI stability and responsiveness.
- Add `onCleared()` override to cancel any active jobs when the ViewModel is destroyed, preventing memory leaks.
- Enhance tests with new scenarios specifically for concurrency, race conditions, and state management during rapid UI interactions and mode switching.
- Add and update KDoc comments to reflect the new concurrency handling strategy and simplified internal architecture.

This refactoring makes the mission execution logic more robust, prevents race conditions, and simplifies the ViewModel's internal structure while maintaining all existing functionality.
…ecture

Updates the README to provide a detailed overview of the project's
multi-module implementation, covering:

- Hybrid multi-layered modularization strategy and considerations for large teams.
- Convention plugins infrastructure (`build-logic` setup and reusability).
- Progressive migration phases and architectural evolution.
- Advanced testing strategy, including structured test data patterns and test execution summary.
- Three-tier Jacoco coverage system (JVM, Android, Overall).
- Multi-module CI/CD optimizations (parallel execution, performance).
- `build-logic` reference for advanced configuration and setup.

This revised documentation serves as a complete overview for anyone
implementing or understanding production-ready multi-module
Android architectures with scalable patterns and robust infrastructures.
@mustalk mustalk self-assigned this Jun 16, 2025
@mustalk mustalk added documentation Improvements or additions to documentation enhancement New feature or request labels Jun 16, 2025
@mustalk mustalk requested a review from kent-beck June 16, 2025 19:59
@mustalk mustalk removed the request for review from kent-beck July 14, 2025 13:49
@mustalk mustalk force-pushed the integration/multi-module branch from a50bd2d to 9622b3f Compare September 28, 2025 10:53
@mustalk mustalk force-pushed the refactor/multi-module-arch branch 2 times, most recently from a28fd19 to 1d7cbee Compare September 28, 2025 19:05
@mustalk mustalk changed the base branch from integration/multi-module to main September 28, 2025 19:12
@mustalk mustalk merged commit 14949ba into main Sep 29, 2025
mustalk added a commit that referenced this pull request Sep 29, 2025
…ins (#12)

This commit merges the complete architectural refactoring of the application, transforming it from a single-module monolith to a scalable, 10-module architecture based on Clean Architecture principles.

The migration was executed progressively while maintaining 100% application functionality.

Key achievements include:
- A production-ready `build-logic` infrastructure with convention plugins for centralized and consistent module configuration.
- A robust testing strategy with over 200 tests, a dual JVM/Android testing architecture, and a three-tier Jacoco coverage reporting system.
- An optimized CI/CD pipeline on GitHub Actions with parallel execution, smart caching, and dynamic module detection for improved performance and developer experience.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant