Flutter Clean Architecture [1]: An Overview & Project Structure
Tags: flutter, mobile, architecture, frontend
Published: 2023-06-13 15:09:17

<pre>
This post is the first part of a tutorial series for Flutter App!.
</pre>

In their early stages, some software developers only struggle to pick up new skills quickly and create functional features; they don't worry about writing clean code or following the right architecture to improve application performance. When using a framework, some other developers often use the well-known MVC pattern, which enables us to separate our application into models, views, and controllers. This is not a problem when developing simple apps, but when we need to scale an application by adding new features, updating a library, or doing something similar, we encounter a number of problems because, as our code becomes more coupled, it becomes more challenging to make changes in our application without also making numerous changes throughout our code. As a result, before you begin developing your application, you must first design the appropriate architecture.

---

When designing your application architecture, it is essential that you respond to several principles, including:

- [**SOLID**](https://medium.com/@sanjayofficial94/s-o-l-i-d-principals-41a5b3b604eb): five object-oriented programming principles contribute to the readability, adaptability, and maintainability of OOP designs.

- [**KISS**](https://www.baeldung.com/cs/kiss-software-design-principle): a design principle stating that designs and/or systems should be as simple as possible in order to maximize user acceptance and interaction.

- [**DRY**](https://www.baeldung.com/cs/dry-software-design-principle): a software development principle which stands for 'don't repeat yourself,' that aims to reduce code duplication in favor of abstractions and avoiding redundancy.

In latest years, we have seen a variety of architecture ideas that implement the preceding principles, such as:

- [**Hexagonal Architecture**](https://medium.com/ssense-tech/hexagonal-architecture-there-are-always-two-sides-to-every-story-bc0780ed7d9c): the Ports and Adapters architecture is based on the idea of separating the application into loosely coupled components in order to isolate the core business logic from outside concerns.

- [**Onion Architecture**](https://medium.com/expedia-group-tech/onion-architecture-deed8a554423): this architecture is made up of multiple concentric layers that connect to the core, which represents the domain. In fact, this architecture is based on the principle of control inversion.

- [**The Clean architecture**](https://betterprogramming.pub/the-clean-architecture-beginners-guide-e4b7058c1165): Uncle Bob's architecture is based on the dependency inversion principle to define boundaries between high-level and low-level components. Furthermore, this architecture attempts to combine all previous architectures into a single actionable idea.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1h7v83udzc2snp9pm31o.gif)
So, as we can see, the fundamental objective of all these architectures is to achieve a clear separation of concerns. This is accomplished by dividing the software into distinct layers, typically including a layer for business rules and another for interfaces. Furthermore, each architecture establishes a well-defined system that is:
**1.**  <u>Independent of Frameworks.</u>
**2.** <u>Testable.</u>
**3.** <u>Independent of UI.</u>
**4.** <u>Independent of Database.</u>
**5.** <u>Independent of any external agency.</u>

---

After discussing the most popular architectures and their roles, I'll build an architecture for every Flutter application based on Uncle Bob's concept, which contains several architecture ideas.
The following diagram shows the proposed architecture, with separate layers represented by circles:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1v6aaw5ye9ttb2zw6111.png)

It should be noted that this architecture is not simply a folder structure that you can copy and paste into your project. It is the concept of separating your application into layers while adhering to Uncle Bob's Dependency Rule, which states that:

> **Nothing in an inner circle can know anything at all about something in an outer circle.**

In other words, no variable, class or function declared in an outer circle can be mentioned in an inner circle.

Let's start by integrating this architecture into a Flutter project because theory can only take you so far, and practical interaction is where you learn the most.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4zutlawakadkbu5hsbj6.gif)

### The Project That We Will Create

We will create a weather application which aims to visualize the weather of a specific city. This application has two interfaces: one for displaying the details of the city's weather and another for adding a new city. This app will include the most basic functionalities, such as getting data from an API, storing data in a local database, handling errors, and so on:

**Weather city details screen:**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e8qi1itwj25uuehv346r.png)

**Add nes city item screen:**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s4xb1jp3khtv95kx86eb.png)

### Flutter Clean Architecture and data flow

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fqeiepngw5aeco73r3wx.png)
I have decided to use the Provider technique for state management in this project, as seen in the figure above. However, as clean architecture is not connected to any particular state management methodology, you are free to choose an alternative strategy that fits with your preferences and project objectives.

The above image also represents the data flow of each user-triggered event. In fact, when a user interacts with the **Widget**, we communicate their action to the **Provider** class, which then connects to the **useCase** to retrieve the action's result. Following that, the useCase communicates with the **Repository** class in order to obtain the solution from a remote or local **DataSource**.

So, to begin implementing these layers, let's divide our project into subfolders.

---

### Project Organization

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7qj02wpauqrpsbvl6epu.png)

We create two subfolders in the lib folder. The first is called “**core**”, and it contains all of the shared and basic components, as well as the implementation of core features such as dependency injection. The second folder is titled "**features**," and it contains every "**feature**" of the app. For example, in our case, we only have one feature that represents weather information, and it contains two interfaces: one for details and another for adding new cities. Each feature will also be separated into three layers: **data**, **domain**, **presentation**, and **utils**. 
The utils folder is not necessary for every feature; it is only required when you have individual feature utilities such as enums, classes, extension functions, and so on.

### Explanation of feature layers

**- Presentation**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/adqs4zdzh50bc8mqowwi.png)

This layer, as its name implies, is in charge of presenting data to the user via widgets. This layer is also responsible for listening for states and connecting to the Provider class, which will delegate all work to the specific use case. In most cases, the presentation layer's primary task is to handle basic input validations, animations, and user interactions. 

#### Applied to the Weather App

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/46lf04qyfgq67o2gk6zb.png)

Every interface in our case will have two subfolders, one for the view file and the other for its widgets. We also include the provider file.

**- Domain**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iyb7mx6z090ntvxohotg.png)
This is the glue layer that connects the other two layers. It will use business objects (**entities**) and use cases to implement our application's business logic. In addition, every use case relies on the Repository to retrieve data.

> 
**Entity:** Encapsulate the enterprise-wide high-level business rules. Only the fields that make sense from a "business standpoint" are held by an entity. Furthermore, the entity is the final result that the view will use, and it is the least likely to change when there are external changes. Moreover, it is not required that the entity have the same fields returned by the web service; you can customize your fields based on what your interface requires. For example, if the call model returns a timestamp date format, your entity must contain the converted date that will be parsed.

> 
**Use case:** The use case, as Uncle Bob mentioned, “orchestrates the flow of data to and from the entity”. A use case, in its most basic form, represents a user action such as retrieving weather data by city, retrieving weather information by coordinates, and so on. To be compliant with the Single Responsibility SOLID principle, each use case must be independent of the others.

The domain layer should be completely independent of all other layers. But how can it be independent when any use case obtains data from a Repository that is shared with the data layer? Now comes the art of **dependency inversion**, one of the SOLID principles. In fact, in the domain layer, we create an abstract Repository class to define a contract for what the Repository must do, and then in the data layer, we write its actual implementation.

> **Repository:** A repository class serves as a single source of truth, separating the logic that retrieves data and maps it to the entity model. In fact, this class collects data from various sources (REST APIs, local databases, cache, etc.) and provides it to the rest of the app. The other components have no idea where the data comes from; they simply consume it.

#### Applied to the Weather App

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f75tz0q460mbp95ra3ko.png)
In our case, the domain layer will be divided into three distinct layers: entities, repository contracts, and use cases.

**- Data**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6706chn57ra08g4zvov0.png)

This layer is responsible for data retrieval from multiple sources. It consists of a repository class that implements the domain contract and determines whether to return fresh or cached data and when to cache it. Additionally, the data source class handles fetching data from a specific source, typically a remote API or local database.

#### Applied to the Weather App

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ea1auk5kx7nhvvnfx76h.png)

Three layers will make up our data layer: datasources, implementations of repositories, and models. Due to the fact that every data source delivers it, we have the models folder rather than entities. In fact, each model is responsible for converting unstructured data (JSON, etc.) into Dart objects by using certain methods (fromJSON, toJSON, etc.).

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zbbt6qv6uwkyswstd03e.gif)

### Next up

After exploring the structure of the weather app, we'll start implementing the domain, which is the most independent layer: [**Next**](https://dev.to/marwamejri/flutter-clean-architecture-2-an-overview-project-structure-7o2)