Recently I started off working for Unicred Mobile Banking as a Senior Android Engineer.
The app had a huge monolithic and legacy codebase written in Java. So, my first task at Unicred was to define a strategy of modularization and refactoring on the app.
We had to separate the app, initially, into three layers which are: the domain
, data
, and presentation
.
Since the code was very tightly coupled, I decided, at first, to place all application entities within the domain layer
.
However, we know how many boilerplate we need to write to create a new feature-module
, don't we? ViewModel
, Activity or Fragment
, UseCases
, Many interfaces
, Datasources and Repositories
and so on. So in order to turn this process easier, I built this "architecture-code-generator" to create all of the templates we need on the project for each new feature.
implementation 'com.mctech.architecture:generator:2.0.0'
Mvvm Architecture Toolkit - It is a personal MVVM architecture library that contains the base codebase generated here.
Here is a Real Android App implementing this library to define its architecture.
To start your generator you will need a ProjectSettings instance and a FeatureSettings instance. Here is an example:
fun main() {
val projectSettings = ProjectSettings(
basePackageName = Package("com.mctech.architecture")
)
val featureSettings = FeatureSettings(
createDependencyInjectionModules = false,
createBothRemoteAndLocalDataSources = true,
presentationViewModel = PresentationMode.ActivityAndFragment,
projectSettings = projectSettings,
fileDuplicatedStrategy = FileDuplicatedStrategy.Replace
)
// Here is an empty feature generated
FeatureGenerator(
settings = featureSettings,
featureName = "FeatureEmpty"
).newFeature {}
}
All you are going to do is run this main function
and the files are going to be generated.
- FeatureEmptyDataSource.kt
- LocalFeatureEmptyDataSource.kt
- RemoteFeatureEmptyDataSource.kt - Check out
createBothRemoteAndLocalDataSources
onFeatureSettings
- FeatureEmptyRepository.kt
- FeatureEmptyAPI.kt - Check out
createBothRemoteAndLocalDataSources
onFeatureSettings
- settings.gradle - Add new module on the current project file.
- build.gradle
- AndroidManifest.xml
- strings.xml
- activity_feature_empty.xml - Check out
PresentationMode
onFeatureSettings
- fragment_feature_empty.xml - Check out
PresentationMode
onFeatureSettings
- FeatureEmptyActivity.kt- Check out
PresentationMode
onFeatureSettings
- FeatureEmptyFragment.kt - Check out
PresentationMode
onFeatureSettings
- FeatureEmptyViewModel.kt
To start your generator you will need a ProjectSettings instance and a FeatureSettings instance. Here is an example:
fun main() {
...
FeatureGenerator.newFeature(
settings = featureSettings,
featureName = "ComplexFeature"
) {
// Add fields on entity
addEntityField(Parameter(
name = "id", type = Type.Long
))
addEntityField(Parameter(
name = "name", type = Type.String
))
addEntityField(Parameter(
name = "anotherFeature", type = Type.CustomType(
packageValue = "com.mctech.architecture.domain.feature_empty.entity",
typeReturn = "FeatureEmpty"
)
))
// Create an use case that will call the repository and delegate it to the data sources and so on.
addUseCase {
UseCaseBuilder(
name = "LoadAllItemsCase",
returnType = Type.ListOfGeneratedEntity,
isDaggerInjectable = false
)
}
addUseCase {
UseCaseBuilder(
name = "LoadItemDetailCase",
returnType = Type.ResultOf(Type.GeneratedEntity),
parameters = listOf(
Parameter(
name = "item",
type = Type.GeneratedEntity
),
Parameter(
name = "simpleList",
type = Type.CustomType(
packageValue = "com.mctech.architecture.domain.feature_empty.entity",
typeReturn = "FeatureEmpty"
)
)
),
isDaggerInjectable = false
)
}
addLiveData {
LiveDataBuilder(
name = "items",
type = Type.ListOfGeneratedEntity
)
}
addLiveData {
LiveDataBuilder(
name = "userName",
type = Type.String
)
}
addComponentState {
ComponentStateBuilder(
name = "listEntities",
type = Type.ListOfGeneratedEntity
)
}
addUserInteraction {
UserInteractionBuilder(
name = "LoadList",
connectedState = findStateByName("listEntities"),
connectedUseCase = findUseCaseByName("LoadAllItemsCase")
)
}
addUserInteraction {
UserInteractionBuilder(
name = "OpenDetails",
parameters = listOf(
Parameter(
name = "item",
type = Type.GeneratedEntity
)
),
connectedUseCase = findUseCaseByName("LoadItemDetailCase")
)
}
}
}
- ComplexFeature.kt
- ComplexFeatureService.kt - With delegation methods of use cases
- LoadAllItemsCase.kt
- LoadItemDetailCase.kt
- ComplexFeatureDataSource.kt - With delegation methods of use cases
- LocalComplexFeatureDataSource.kt - With delegation methods of use cases
- RemoteComplexFeatureDataSource.kt - With delegation methods of use cases
- ComplexFeatureRepository.kt - With delegation methods of use cases
- ComplexFeatureAPI.kt - With delegation methods of use cases
- settings.gradle - Add new module on the current project file.
- build.gradle
- AndroidManifest.xml
- strings.xml
- activity_complex_feature.xml
- fragment_complex_feature.xml
- ComplexFeatureActivity.kt - With delegation methods of LiveData
- ComplexFeatureFragment.kt - With delegation methods of LiveData
- ComplexFeatureViewModel.kt - With delegation methods of LiveData
- ComplexFeatureUserInteraction.kt
- Improve code (Yeah, I know the code is not good, but again, this library was a personal generator before it became open source) :P
- Make the generator easier to be used
- Improve the templates logic.
- Create an extension library and organize all existing functions.
- Create all unit tests.