React Native Clean Architecture — ResoCoder’s way
Once I was first introduced to clean architecture I was really struggling to understand the concepts, but even more its implementation. Most of the times tutorials and articles were focused on one of those two things and it was pretty hard for me to connect the dots correctly. Also, even though the concepts are far more important than their implementation the concrete example in the technology you are familiar with would make an unmeasurable difference.
This is where ResoCoder and their YT channel jumped in. They’ve offered the most comprehensive explanation of clean architecture with concrete implementation in Flutter ( Dart). It was a game-changer for me and it will probably be for you also if you are interested in mobile development, and especially Flutter.
I love Flutter ♥️! I really do, but circumstantially I am doing a fair share of development in React Native too, so, naturally, I’ve decided to use the aforementioned implementation in React Native.
We’ve decided to use a vertical folder structure where every feature (component, module) has its own folder.
This way we are gaining better isolation of features. In long run this leads to better code reusability, every feature can be its own npm module. Why is this important? This is probably a topic for itself, but in a nutshell, good software is expandable, maintainable, reliable, and reusable( in parts or as a whole). Developing software as independent modules leads to all of the above-mentioned.
The vertical folder structure can become overwhelming! Be aware of this, it is not a silver bullet, as everything in software engineering, the choice of folder structure is situational. If you are a solo developer, or your project have a fairly small number of features then horizontal structure makes much more sense since you won’t get drowned in a totally unnecessary pile of folder and files.
The anatomy of the react-native CLI project
Folders on the root level are:
- android — This folder is an actual native android app. Everything related to writing native android code or configuring android assets (package name, icons, etc.) will happen here.
- - ios — This folder is an actual native ios app. Everything related to writing native ios code or configuring ios assets (package name, icons, etc.) will happen here.
- - app — This is an actual React Native app. This is the most important folder of all and where the magic happens.
- - assets — All assets, like images or fonts go inside this folder.
- - docs — Documentation goes in here. Documentation should be part of the code, so it is easily versioned and accessible to anyone with access to the repository. Documentation is part of the code.
Clean App architecture
The app should follow clean architecture, and TDD and DDD concepts. Even the folder structure is more aligned with TDD, DDD concepts like domain validation will make a great benefit for the app itself.
What does this mean in practice? It is highly recommended for the reader to get familiar with the following:
So the app folder is actually divided into two folders:
- core — This folder is in essence also a feature (module) thus its structure is similar to the structure of the other features. It contains elements that are shared between other features, like type definitions, concrete data source adapters, error handling mechanisms, etc.
- features — every feature, usually, is divided into the following layers (folders):
- data — This is the data layer, it is essentially responsible for implementing interfaces defined inside the domain layer. Usually, it contains data sources, that are actually just different API access services implementation. The data source can be local, like the SQLite DB, or remote like Firebase or some other 3rd party API. This layer also contains concrete models that are used inside different data sources.
- domain — This is the domain layer. This layer is agnostic of concrete implementations, it offers the interfaces that should be used across other layers and implemented usually inside the data layer. It has a definition of entities that are needed for a given feature. Also, it should offer repositories interfaces that can be programmed to. One of the most important thing this layer contains are useCases. They are consumed by the presentation layer and represent the most atomic units of repository usage.
- presentation — This layer is responsible for defining screens in JSX and widgets specific to the feature. If the widgets are going to be used across features they should be lifted up to the core folder. If the logic of the screen or widget is too complex it should be moved to one of the hooks. Also, if hooks start to clutter appropriate design patterns should be chosen and put inside their own folder.
How data flows throughout the layers
Let's see how data flows throughout the layers on the example of the Onboarding screen. You want to show the onboarding screen only once, and you want to persist that information somewhere. It cannot be stored in temporary storage, like localStorage, but rather inside some permanent data storage like SQLite. For the sake of simplicity, let's have only the boolean variable isShown that should be stored in DB. So, our domain layer should contain an onboarding entity with that one property, then a repository interface that exposes a method for saving that entity and use case that uses that repository interface, and once the concrete object of the repository is passed it calls it to store the entity. The domain layer shouldn’t depend on any other layer thus we usually define it first. Once we have the domain layer we can create our SQLite model that is a concrete implementation of our domain entity data sources and concrete repository implementation inside the data layer. The concrete repository uses a data source interface in order to interact with data sources, calls the data source which has a reference to the ORM model. And, when we have everything, we can somehow inject it into our presentation layer. Since this is a really simple example you can create useCase inside the useEffect hook and create and pass concrete objects. And this is it, you have a perfectly layered feature that can be tested amazingly easily.
Enough writing, show me the code
Code will be inside the Subracker repository. This is the second article in the serious: Open source startup, and the third one will show you the concrete implementation of the concepts written here, so please be patient, or star the repository, since the code will be there first.
Open source startup series
Check out the next article in the series: React Native — Create onboarding screen — Subracker App
Check out the previous article: Open Source Startup: Part 1 — The idea.
Since I cannot stress enough, this architecture is highly inspired by ResoCoder’s clean architecture in Flutter.
If you have some extra cache and you would like to support me you can use these links:
1. ☕️ in Sturbacks: https://buy.stripe.com/4gw16bgzM6Bw1I44gh
2. 📱💻 gear: https://buy.stripe.com/5kAcOTdnA8JEcmI7su