Coming up next I will review briefly the main flaws of two common file grouping strategies used in Xcode projects. Then I will explain a grouping strategy that overcomes those flaws.
Strategies you should avoid
Grouping by component
Usually seen in monolithic projects and projects with lightweight architectures.
Description
-
All source files are grouped by component kind.
-
Each Component Group can be divided in subgroups according to some other criteria, for example the parent feature/functionality of the component.
Example
├── DataModels
│ ├── Login
│ ├── Feed
│ └── ...
├── DataSources
│ ├── Login
│ ├── Feed
│ └── ...
├── Models
│ ├── Login
│ ├── Feed
│ └── ...
├── Repositories
│ ├── Login
│ ├── Feed
│ └── ...
├── Views
│ ├── Login
│ ├── Feed
│ └── ...
└── ...
Review
The problem with grouping by component is that it breaks the Common Closure Principle (sort of Single Responsibility Principle at component-level).
The Common Closure Principle (CCP)
THE CLASSES IN A PACKAGE SHOULD BE CLOSED TOGETHER
AGAINST THE SAME KINDS OF CHANGES. A CHANGE THAT
AFFECTS A PACKAGE AFFECTS ALL THE CLASSES IN THAT
PACKAGE.
Ultimately, components that need to interact with each other will be too distant, making it harder to locate the files you need to edit to perform a change in the code.
Grouping by business capability
Usually seen in projects where modularization reflects the layers of the architecture and projects where the architecture emphasizes the grouping of components by the screen they belong to.
Description
-
Presentation and Business layers source files are grouped by the Feature or UI flow (or Viper Module or Clean Swift Scene) they belong to.
-
Other source files, that are more prone to be used by more than one Feature group, may be organized (A) outside the Feature groups, using a different criteria, or (B) in subgroups of the Feature group they are more closely related to.
Example
(A) variant example:
├─ Data
│ ├── Models
│ └── Sources
├── Domain
│ ├── Models
│ └── Repositories
├── Scenes
│ ├── Feed
│ ├── Login
│ └── ...
└ ...
(B) variant with by component Scene subgrouping example:
├── Scenes
│ ├── Feed
│ │ ├── DataSources
│ │ ├── Models
│ │ ├── DataModels
│ │ ├── Repositories
│ │ └── Views
│ ├── Login
│ │ ├── DataSources
│ │ ├── Models
│ │ ├── DataModels
│ │ ├── Repositories
│ │ └── Views
│ └── ...
└ ...
(B) variant without further Scene subgrouping example:
├── Scenes
│ ├── Feed
│ ├── Login
│ └── ...
└ ...
Review
The problem with grouping by business capability is that, depending on how the files inside the Feature groups are organized, it will suffer from the same problem already exposed for the grouping by component approach.
In its shallowest form, where Feature groups members are not organized in subgroups, two files that need to change together will be close to each other, as implied by the Common Closure Principle. But that approach breaks the Common Reuse Principle (sort of Interface Segregation Principle at component-level).
The Common Reuse Principle (CRP)
THE CLASSES IN A PACKAGE ARE REUSED TOGETHER. IF YOU
REUSE ONE OF THE CLASSES IN A PACKAGE, YOU REUSE THEM
ALL.
Ultimately the lack of a folder hierarchy will make it more difficult to locate a particular component inside a group and to understand how it relates to the other components in that group.
Proposed strategy
Grouping by relative visibility
Effective in projects wholly modularized by business capability (higher module granularity by technical capabilities or functional objectives is fine) and projects where architecture emphasizes single-direction communication between components.
Description
-
Source files are organized in a hierarchical tree structure that represents the relative visibility between architecture-defined components.
-
Top-level nodes (groups) are reserved for the outermost components of each layer (i.e. components visible to adjacent layer).
-
Inner nodes (subgroups) represent dependencies of the parent node (i.e. components the parent group component depends on).
Example
├── Feed
│ ├── Models
│ │ └── DataModels
│ ├── Repositories
│ │ └── DataSources
│ └── ...
├── Login
│ ├── Models
│ │ └── DataModels
│ ├── Repositories
│ │ └── DataSources
│ └── ...
└ ...
Review
Advantages of grouping by relative visibility include:
- It does not break any of the package cohesion principles.
- It has a predictable group hierarchy inside each module or feature group.
- It helps to spot undesired coupling between components.
- It makes easier to focus only in the files you need to edit to perform a change in the code.
See for instance what the Feed screen from the previous examples could look like following this grouping strategy, implemented as a module/target and using MVVM with Use Cases and Repositories: