This content originally appeared on Level Up Coding - Medium and was authored by Alex Dremov
iOS App as a Microservice — Build Robust App Architecture
MVVM, MVC, VIPER? How to sctructure your app on a larger scale?
What will you choose: MVVM, MVC, VIPER? Those all are local and problem-specific architectures. But how to structure your app on a larger scale to make it scalable and well-organized?
In this post, I will discuss microfeature architecture that is, simply said, amazing when implemented correctly in an iOS app.
Next Episodes
- Ideas on implementation with SwiftUI
- Using tuist to structure microfeature application
Core Idea
The idea comes from microservice server-side application infrastructure. The whole app is divided into logical components corresponding to different functional areas of the application.
💥 Considering how complex mobile apps can be, why not apply the same architecture to iOS apps?
Briefly, microfeature architecture implies splitting your app into different components that accept other components’ interfaces or data as explicit dependencies.
Therefore, your app can be represented as a graph of modules that explicitly interact with each other.
Main Benefits
- Improved maintainability — each component is small and so is easier to understand and change.
- Better testability — components explicitly define their public interface. So, they are easier to mock and test.
- Team organization — different teams can work on different components independently.
- Scalability, code reuse — when an app is a combination of modules, you can robustly change the app’s behavior by recombining modules. If you decide to create an app extension, watchOS app, or App Clip, just pick the required components and you’re all set up.
- Explicit dependencies — implicit dependencies are the one of the worst things that can happen to app’s architecture. This architecture requires to define explicit dependencies for each module.
Details
So, how to structure an iOS app once you decided to use microfeature architecture? The core concept is separation. But you still can use one Xcode project for that and separate features purely by architecture.
💥 You can put each feature into a separate Xcode project. This will push you to a strict separation of components.
I will cover how to do this effectively with tuist in the next episode!
Your codebase will be divided into several blocks:
Features
That’s where elements of your app live. Later in this post, I will show by example what this part includes.
Components are logical blocks of your app. Each component explicitly defines an interface to interact with it.
💡 Swift does not have namespaces, but you can use enums to hide internal module logic.
Apps
You can have a WatchOS app, widgets, and the main iOS app. Each app depends on features and builds the final app using features, combining them like bricks.
Tests + Testing Data And Mock
This logic also lies apart from the feature’s main parts. It’s separate because:
- We don’t want to use mock data accidentally in the app
- We don’t want to include irrelevant data into the final app binary
Feature design
The feature consists of four blocks. Tests and mocks may not be present, but the feature always has an interface and implementation.
Interface
This part defines parts visible for other features. Public interfaces and models or entities of the feature stay here.
Interfaces define ways that are used to interact with the feature.
Models or entities are simple structures with almost no logic that simply define data used to communicate with the feature.
You can include other components into the interface but remember that interface must not expose implementation details
💥 If the feature depends on another feature, then it depends on the other feature’s interface.
Features must not depend on other feature’s implementation
Implementation
Implementation depends on an interface and provides classes and structures conforming to defined protocols in the interface. Resources, images, and other implementation details also stay here.
💡 Separation Interface/Implementation forces you to write code conforming to the letter D from SOLID.
Dependency inversion happens naturally when other modules know about interfaces and not about implementations.
Knowing this information, we can add details to our app’s graph image:
Notice that none of the features depends on the other feature’s interface. Each feature interface strictly depends on the other feature’s interface.
Now you see that apps take building blocks and combine them to make an app.
Subscribe and don’t miss posts!
Case Example
Let’s architect a schedule app. It will have:
- Schedule view
- Add event/edit view
- Schedule WatchOS View
Pretty simple.
Let’s split this app into several features:
- UICommon
Contains common UI elements that can be used to create more complex views
- Schedule
Contains main schedule views and logic associated with them. The interface defines ways to interact with views or present them.
- WatchSchedule
Contains watch-specific schedule views and logic associated with them
- EventModification
Contains event modification logic and views
- ScheduleData
Data provider. Defines data structures and entities to obtain them.
The interface will contain simple data entities and model protocols defining ways of obtaining these entities.
Implementation defines models conforming to protocols defined in the interface. For example, you may want to define a local storage model or network model. It’s up to the final app to decide which option to use.
App Graph
As you see, WatchOS and the main iOS app reuse common components. Also, Each app decides which implementation of modules’ interfaces they pick. For example, the WatchOS app can choose different data sources in ScheduleData feature rather than the main iOS app.
In a monolithic app, you would probably need to write almost a second app and copy a lot of code
Next Episodes
In the next posts, I will share my ideas on using microfeature architecture with SwiftUI and tuist to structure code efficiently.
FAQ
When should I create a new feature and when It’s better not to?
It purely depends on the case and on what you think the best option is. If you can come up with some use case when your feature will be reused in some other context, then it’s a separate feature.
💥 Do not overcomplicate things!
Making a new feature for each class will do more harm than good.
If some block probably will not be reused, but you just feel that it’s logically separate functionality, then also go with a new feature as it will help to keep your architecture clean.
What to do with circular references?
Circular references can be a pain and they happen if two features depend on each other’s interfaces. If such a situation happens, critically consider if your feature separation is correct. There are two possible options.
- Two features are actually one feature. Then, you can merge these two features and get rid of circular references.
- Two features are actually three features. If features depend on each other, then there is some part that’s needed by both features. What if this part is an independent feature? If this is the case, extract the third feature and fix dependencies.
There’s a lot said about making dependencies explicit. What’s the point?
It’s nearly impossible to scale or modify big apps when components are implicitly dependent. Just imagine the mess that is going to happen if you modify some class that is a dependency of all other modules through a singleton.
Your app may start to have unexpected behavior here and there and you can’t even know how your modification will affect the whole app.
It’s like sitting on a box of TNT.
I encourage you to avoid implicit dependencies whenever possible. Microfeatures architecture will help you with doing that.
References
Level Up Coding
Thanks for being a part of our community! Before you go:
- 👏 Clap for the story and follow the author 👉
- 📰 View more content in the Level Up Coding publication
- 🔔 Follow us: Twitter | LinkedIn | Newsletter
🚀👉 Join the Level Up talent collective and find an amazing job
iOS App As a Microservice. Build Robust App Architecture was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.
This content originally appeared on Level Up Coding - Medium and was authored by Alex Dremov
Alex Dremov | Sciencx (2022-09-20T12:58:59+00:00) iOS App As a Microservice. Build Robust App Architecture. Retrieved from https://www.scien.cx/2022/09/20/ios-app-as-a-microservice-build-robust-app-architecture/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.