Pull to refresh

Clean Decomposition

Reading time 4 min
Views 1.6K
Original author: Volodymyr Chernyshov
In this article, I want to consider an approach to splitting tasks into subtasks when using Clean Architecture.

The decomposition problem was encountered by the NullGravity mobile development team and below how we solved it and what get in the end.



Prehistory


It was the fall of 2018, we were developing the next application for a telecom operator. But this time was different. The terms were quite tight and tied to the client’s marketing campaign. Android team has grown from 3 to 6-7 developers. Several tasks were taken in the sprint and the question was how to decompose them effectively.

What do we mean when we speak effectively:

  1. The maximum number of parallel tasks.
    This makes it possible to use all available resources.
  2. Reducing the size of merge requests.
    They will not be watched for show, and you can still catch potential problems at the stage of code review.
  3. Reduce the number of merge conflicts.
    Tasks will merge faster and there is no need to switch the developer to resolve conflicts.
  4. An opportunity to collect statistics of expenses of time.
  5. Automate task creation in Jira.

How did we solve the problem?


We divide all subtasks into the following types:

  • Data
  • Domain
  • Empty
  • UI
  • Item
  • Custom
  • Integration

Data and Domain correspond to layers in Clean Architecture. Empty, UI, Item and Custom refer to the presentation layer. Integration applies to both the domain and presentation layers.


Figure 1. Location of tasks relative to Clean Architecture layers

Let's look at each type individually.

Data


DTOs, API, work with database, datasource, etc.

Domain


Repository interface, business models, interactors.

As well as repository interface implemented in the data layer. Such a somewhat illogical, at first glance, separation made it possible to isolate tasks of the data and domain types as much as possible.

UI


Creating a basic screen layout and additional states, if they exist.

Item


If the screen is a list of elements, then for each type you need to create a model — Item. To map Item to the layout, you need AdapterDelegate. We use the concept of adapter delegates but with some improvements.
Next, create an example of working with a list item in PresentationModel.

Empty


Base classes required for tasks like ui or item: PresentationModel, Framgent, layout, DI module, AdapterDelagate factory. Binding interfaces and implementations. Create an entry point on the screen.

The result of the task is the application screen. It contains Toolbar, RecyclerView, ProgressView, etc. that is, common interface elements, the addition of which could be duplicated by different developers and would lead to inevitable merge conflicts.

Custom


Implementation of custom UI component.

An additional type is needed to separate the development of a new component from a task of type UI.

Integration


Integration of domain and presentation layers.

As a rule, this is one of the most time-consuming tasks. It is necessary to reduce the two layers and refine the points that could have been missed in the previous stages.

Task Order


Tasks like data, empty and custom can be started immediately after the sprint starts. They are independent of other tasks.

The domain task is executed after the data task.

The ui and item tasks after the empty task.

The integration task is the last to be completed as it requires the completion of all previous tasks.


Figure 2. Timeline of task execution

Despite the fact that some tasks are blocked by other tasks, they can be started at the same time or with a slight delay. Such tasks include domain, ui, and item. Thus, the development process is accelerated.


Figure 3. Timeline of performing tasks with locks

For each specific functionality, the set of tasks can vary. There may be a different number of tasks empty, ui, item and integration, and some types may simply be absent.

Process automation and statistics collection


To collect statistics when creating a task, label is assigned to it. This mechanism in the future will allow you to analyze the time spent on each type, and to form the average costs. The collected information can be applied when evaluating a new project.

For automation, we also managed to find a solution. Since tasks are typical, why their description in Jira should be different. We developed templates for summary and description. At first it was just a json file, the Python parser of this file, and the Jira REST API was connected to generate tasks.

In this form, the script lasted almost a year. Today it has turned into a full-fledged desktop application written in Python using PyQt and MVP architecture.

Maybe MVP was overhead, but when the first version on Tkinter crashed MacOS version 10.14.6 and not all teams could use the application, we easily rewrote view for PyQt in half a day and it worked. Once again, we were convinced that the use of architectural approaches, even for such simple tasks, has its advantages. A screenshot of the JiraSubTaskCreator is shown in Figure 4.


Figure 4. The main screen of JiraSubTaskCreator

Conclusions


  1. We have developed an approach to decomposition of tasks into subtasks minimally dependent on each other;
  2. Generated templates for describing tasks;
  3. We got small merge requests, which makes it possible to carefully review and change the code in isolation
  4. Reduced the number of conflicts with merge request;
  5. We got the opportunity to more accurately estimate and analyze the time spent on each type of task;
  6. Automated part of the routine work.
Tags:
Hubs:
0
Comments 0
Comments Leave a comment

Articles