Break down tasks into smaller self-contained units-of-work.
Reduce dependencies across your codebase. This will improve future maintenance and reduce secondary effects.
It is easier to debug and test small blocks.
io.turbodsl allows code-segmentation through job{...} and asyncJob{...}:
Group all your task-related code into a job{...} as much as possible.
If you several tasks to be executed in parallel, just change job{...} to asyncJob{...}, and place them within an async expression.
Determine your asynchronous requirements to choose which async expression is the best:
Are the tasks to execute in parallel always the same?
Are the return values for each parallel task the same?
Does the order you register parallel tasks the same?
REMEMBER: By wrapping your code within job{...}, async{...}, or asyncJob{...} you can specify:
A scope name for debugging purposes.
Initial delay.
A timeout to define a maximum execution-threshold.
asyncMode to define how parallel steps are evaluated.
Specific coroutine context (Dispatchers).
For example, assume your code has several "steps" returning String - each step could be one or more expressions / statements - doesn't matter:
Implementing using io.turbodsl:
Over time, new requirements will add more (nested) structures, increasing indentation which affects code-readability. In the long term, source code will become harder to maintain.
Using Kotlin extension-functions we can extract DSL expressions into proper functions to provide more clarity.
This also encourages code-reusability since such extension-functions can be shared between other components:
In Kotlin, you can define local functions, private functions (within the same Kotlin file), or "public" functions.
You can specify a "receiver" - that is, the function is only available for a specific data-type → an extension-function⇗.
Using these concepts we can refactor the code as follows:
You can use your IDE features to collapse (IntelliJ Code Folding⇗) different parts of the code, which makes everything much easier to read: