The Clean Architecture is the most powerful solutions for building clean apps that multiple teams can work on, independent data layers, scalable for adding/removing features, testable, independent frameworks/tools, and can be easily maintained at any time.
As proposed by Uncle Bob, we should all strive to separate code into independent layers and depend on abstractions instead of concrete implementations.
Clean architecture is not about a particular state management technique, architecting your app properly will make the choice of state management almost inconsequential.
The architecture produce systems that are:
Entities encapsulate Enterprise wide business rules/ business objects of the application. An entity can be an object with methods, or it can be a set of data structures and functions.
The software in this layer contains application specific business rules. It encapsulates and implements all of the use cases of the system.
These use cases orchestrate the flow of data to and from the entities, and direct those entities to use their enterprise wide business rules to achieve the goals of the use case.
The outermost layer is generally composed of frameworks and tools such as the Database, the Web Framework, etc.
This layer is where all the details go. The Web is a detail. The database is a detail. We keep these things on the outside where they can do little harm.
The horizontal arrows (→) represent dependency flow. For example, Entities do not depend on anything, Use Cases depend only on Entities etc.
The concentric circles represent different areas of software. In general, the further in you go, the higher level the software becomes. The outer circles are mechanisms. The inner circles are policies.
Source code dependencies can only point inwards.
Nothing in an inner circle can know anything at all about something in an outer circle. Nothing in an outer circle should impact the inner circles.
As you move inwards the level of abstraction increases. The outermost circle is low level concrete detail. As you move inwards the software grows more abstract, and encapsulates higher level policies. The inner most circle is the most general.
By separating the software into layers, and conforming to The Dependency Rule, you will create a system that is intrinsically testable, with all the benefits that implies. When any of the external parts of the system become obsolete, like the database, or the web framework, you can replace those obsolete elements with a minimum of fuss.
The software in this layer is a set of adapters that convert data from the format most convenient for the use cases and entities, to the format most convenient for some external agency such as the Database or the Web.
No code inward of this circle should know anything at all about the database.
Also in this layer is any other adapter necessary to convert data from some external form, such as an external service, to the internal form used by the use cases and entities.
An App can have many features and every feature of the app will be divided into 3 layers - presentation, domain and data.
This is the inner layer and is completely independent meaning it shouldn't be susceptible to the whims of changing data sources or porting our app to Angular Dart.
It will contain only the core business logic (use cases) and business objects (entities). It should be totally independent of every other layer.
It contains Use Cases, Entities and Repositories.
Entities are the data model that our app will need but just the data model.
Use Cases are classes which encapsulate all the business logic of a particular use case of the app.
The domain layer is independent of Presentation layer but the repositories are at the border of domain and data layer so to make domain independent of data layer we use dependency inversion and keep only the contract of repository in domain.
Dependency inversion principle is the last of the SOLID principles. It basically states that the boundaries between layers should be handled with interfaces (abstract classes in Dart).
<aside> 💡 There are some other folders as well.
</aside>
The raw data from different data sources (REST API, GraphQl, Sqlite, …etc) will be mapped or converted into models (dart objects) using some serialization methods to serialize/deserialize data (Json, Xml, …etc) from and into.
The models in the data layer are different from the entities in the Domain layer, and the reason why we have this method is because in some cases if we want to change the raw data serialization from (Json) to (xml), this changes will not affect the internal entities (Domain’s Entities). The models in the data layers will extends the properties from the Domain’s entities and it will always depend on the Domain layer since the models are extending all of the properties from the entities.
Repositories returns entities and not models, because the contract written inside the Domain layer.
Both Presentation and Data layers depend on this Domain layer, since the data layer will implement what ever contracts written here and the presentation layer will use those contracts with the implementations to be used as an injected dependencies.
The presentation layer will only gets data as entities and not models, Big projects may even use a kind of mapping objects in each layer so they make sure that each layer will not interact or depend on other.
A use cases are individual classes that depends on repositories we defined, and normally a use case should only perform one precise action (getting articles, post data, signing in, …etc).
One use case may takes multiple repositories injected to it, and the viewmodel may also takes multiple use cases injected to it.
<aside> ⚠️ We should think about our application as a group of use cases that describe the intent of the applications and group of plugins that give those use cases access to outside world.
</aside>
The actual coding process will happen from the inner, most stable layers of the architecture outwards. This means we'll first implement the domain layer starting with the Entity.
Use Cases are where the business logic gets executed.