Need/problem
We’ve had two final products for a while now – a CLI and a macOS menu bar app. The menu bar app reuses some CLI modules, like TuistSupport
or TuistServer
. Since the menu bar app has the same destination as the CLI, we haven’t run into any issues.
However, we are now bringing an iOS app to the mix and not all Tuist modules are compilable for iOS. One example is XcodeProj
that only compiles on macOS.
Motivation
We need to align on how to update our modularization strategy, so we can:
- Reuse common modules across destinations, like interacting with the server (
TuistServer
) - Modules imported by the iOS app should fully compile on iOS – without those modules having to do too many of destination-specific compiler directives or build rules.
Detailed design
There are two major changes we need to do.
Splitting monolith modules
We need to split monolith modules like TuistSupport
, so products like iOS app can only depend on the part of the graph that it actually needs – and doesn’t end up depending on parts of TuistSupport
that don’t compile on macOS.
How do we split it? I think the most sensible approach is for each utility in TuistSupport
to be its own module. It does mean we’ll end up with a lot of small modules, but I believe that’s fine.
So, TuistSupport/GitController.swift
would be in TuistGit
or TuistGitController
(slightly leaning to go without the Controller
suffix). TuistSupport/Constants.swift
would be in TuistConstants
, TuistSupport/Environment.swift
would be in Environment
. You get the idea.
In a sense, we’ll split TuistSupport
into a new layer of Support modules. Similarly, we’ll need to split TuistCore
into a new layer of Core modules. Support modules shouldn’t have dependencies between themselves. Core modules can depend on Support modules, but again, they should not have dependencies between themselves.
This might be a great opportunity for us to dogfood tuist graph --json
and build a custom linter that ensures the mentioned conditions are not broken.
Another convention we need to establish is for modules that are already scoped to a single domain, but still need to be split to make them compilable for the desired destination. Such an example is TuistServer
that we split into:
TuistServerCore
– compilable on all destinationsTuistServerCLI
- CLI-specific components, such as for interacting with the binary cacheTuistServerApp
– scoped to the iOS app
We expect to need this rarely. Splitting the monolith TuistSupport
and TuistCore
modules should be enough in most scenarios.
Drawbacks
We’ll end up with a lot more modules and imports. That’s not necessarily a con, but it does mean some extra complexity. Additionally, splitting the modules will take some effort. We should aim to do this iteratively.
Alternatives
The Tuist app doesn’t share any modules with the CLI. We’ll end up duplicating some efforts, such as communicating with the server – but we would end up with a simplified module hierarchy. However, I think the proposed modularization adjustments would be beneficial regardless.