Clean Architecture with states_rebuilder has Never Been Cleaner
By: Mellati Fatah
Tags: Software Architecture, Flutter, Android, Dart, Clean Architecture

No matter how good your programming skills are and how many design patterns you master, for the enterprise-wide application, you have to architect your application in a way that makes it readable, maintainable, testable, and extensible. In this article, I will show you how to implement The Clean Architecture by taking advantage of the KISSness of states_rebuilder.

What is an Architecture and why?

َ An architecture is a way to factor your code into functional segments (inside folders and files) and keep clean boundaries between them so that it is easy to understand, easy to refer yourself inside and easy to modify as the project grows.

First scenario: A real-life example of no architecture:

Imagine that your home library has about ten books. No matter how you organize it, you can easily find the book you want the time you want.

Second scenario: A real-life example of an architecture:

Now, imagine you work in a university library with thousands of books. Then, you have to follow a convention of book arrangement. You may classify the books by subjects, language, author name or alphabetically. No matter your philosophy of the arrangement, you have to stick to one architecture so that when a student needs a particular book, you know exactly where to find it with your eyes closed.

In the software development realm:

You encounter the first scenario in tutorials and demo apps. The tutor aims to explain a particular aspect of the programming language. He can, without loss of clarity, put all the code inside one file. In demo apps, the app is in general small without any concern for future change. In these cases, wasting time following a particular architecture and organizing the code in folders and files will be overkilling for the tutor and confusion for the reader.

The second scenario is the situation of real-world enterprise apps. Real apps request for maintainability, extensibility, testability. To fulfill this request, the app code must be clean. The cornerstone of clean code is clean and effective architecture.

the benefit of a good architecture

1- Maintainability: no messed code, no cross-referencing between segments.

2- Scalability: You can add new functionality more easily

3- Testability: You can mock dependencies.

All the above three features are tightly related to change. No matter how well you design an application, over time an application must change. It must change to fix bugs (maintainability). It must change to grow (Scalability). But what about testability and how it is linked to change? Look at this sample of code:

int counter=0;

void increment(){

counter++;

}

It is easy to write a test to the increment() method to verify that the counter variable is incremented by one after executing the method. But what a big deal! Isn’t this ridiculous and a waste of time! No matter how complex the method is, one can verify that it works as expected by other means than unit testing. Why testing? the answer is the change. Let’s say that the requirements changed and you edited the increment() method to be :

int counter=0;

void increment(){

incrementerService(counter);

}

Now after this change, by running the test again you can track any unwanted behavior following the change (perhaps incrementerService(counter) return the wrong result). If you don’t have any test and the change causes bugs in your program, it will be difficult to track where did you get it wrong. Testing is an early detection utility of bugs after code refactoring.

Testing your code has another important benefit. If you follow good design patterns and clean architecture, you make your code testable, on the other flip side the easiness of writing tests witnesses the good quality of your code and in the same sense trouble writing tests is a good sign of the bad quality of your code. If you want to take your skills to the next level, shift the gears and consider Test Driven Development TDD. In TDD, you write the test before even writing a single line of code. It seems a little strange to test inexistent code and one can not resist coding first especially at the early stage of the app when you are not clear about what exactly you want. Just look at this approach the same way as setting your goals in project management before starting your project. If your vision is clear, your goals will be clear and the reverse is true.

In case of bug fixing or new feature adding, TDD is a must. Before fixing a bug try to write a test that reproduces the bug; if succeded you find yourself understanding the origin of the bug. And as has been said, understanding the problem is half of the solution. After fixing the bug, and by running all new and old tests you are one hundred percent sure that you fixed the problem without any unwanted side effect.

If you are adding a new requested feature, your intention is so clear that writing a test that expects the requested feature is very easy, and by looping between Red, Green, and refactor technique of TDD you can come out with the best solution. Again after changing the code run all the tests to check the validity fo your code.

Do not underestimate tests and be sure that the time you spend writing tests will save your life in the future.

The clean layered architecture

The architecture consists of something like onion layers, the innermost layer is the domain layer, the middle layer is the service layer and the outer layer consists of three parts: the user interface UI, data_source and infrastructure. In coding, each of the parts of the architecture is represented using a folder. So the topmost folders are:

Arrows in the picture above indicate code dependencies which can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circle. In particular, the name of something declared in an outer circle must not be mentioned by the code in the inner circle.

1- Domain (or Domain Model):

It is the core of your app’s business logic, It contains all the logic pertaining to the business domain. By the way, if you want to switch gears again and take your programming skills to the next level, don’t waste more time and consider Domain-Driven Design (DDD). In the context of DDD, experts in the problem domain work with the development team to get out with what is called the analysis model. The implementation of the analysis model is the domain model and this is what this folder should contain. If you are working on an app for yourself, you are the domain expert and the developer at the same time, so you have to think with your self to get out with mental models of the business using a common language that anyone of the same business can understand. This mental model free from any technical details is the analysis model. The domain model is the traduction of the analysis model in terms of code using the common language of the business (ubiquitous language).

The domain folder contains at least four subfolders.

entities folder: Entities are mutable objects uniquely identified by their IDs. In most cases, entities are the in-memory representation of the data that was retrieved from the persistence store (data_source). An entity can have methods it must encapsulate all the domain logic it controls. Among the logic that should an entity have is the validation logic, which will be executed just before persisting the entity state to ensure that has the right state.

entities must be validated just before persistance

value objects folder: Value objects are immutable objects which have value equality and self-validation but no IDs. Value objects come to the rescue if you have many primitive fields in your entities. In this case, it may be possible to move some of them with the associated behavior and validation to a new value object classes. Validation is performed at the time of construction so that value objects are either instantiated in the right state or an error is thrown.

Value objects are such a good concept that you do keep refactoring your code to get more of them.

value objects must be validated the time of constraction

NB: This is by no means a comprehensive tutorial on TDD or DDD. For more details on both concepts see the following reference:

1- Test-Driven development: link

2- Domain-Driven Design: link

exception folder: This folder should contain all exception classes that entities and value objects are expected to throw.

common folder: contains common helper utilities shared inside the domain (constants, enumerations, util classes …).

This is not an exhaustive list of folders, you can add any number of folders that will help you organize your domain logic.

2- Service layer:

The second layer is named service for it holds the application service layer. The application service layer is the client of the domain layer. It hides (or in technical terms “abstracts”) the details of the domain and exposes a programming interface (API) to represent the business use cases and behavior of the application. it is all about coordination and orchestration through delegation to domain and external services. The service layer must inversely depend on external services (Infrastructure and data_source) and this is done by defining a set of interfaces that should any external service implement. The set of interfaces defined is placed in the interfaces folder.

The service folder is made up of at least three subfolders.

interfaces folder: contains interfaces that should any external service implement.

exception folder: This folder should contain all exception classes that the services classes and external service are expected to throw. External services in the infrastructure and data_source layer should not throw their own defined exceptions, but their errors should be caught and custom exceptions defined in this folder should be thrown instead.

common folder: contains common helper utilities shared inside the service layer (constants, enumerations, util classes …).

3- Infrastructure:

Applications may rely on external resources, i.e; getting persistent data, fetching web, make a call or send a message or email, using 3rd-party libraries. In this case, the code using API of these third-party libraries is contained in the infrastructure folder (infrastructure layer). The service layer must communicate with the infrastructure layer through interfaces and infrastructure must implement interfaces defined in the service layer. This decoupling is mandatory to ensure our app does not depend on any 3rd-party library.

It is here the right place to put all the logic that deals with communication with platform-specific services such as sending emails, checking network connection, using GPS service, camera …

4- data_source:

Data_source is just a detail like the infrastructure, but it is such an important detail that gives it the merit of having its own folder. data_source is responsible for orchestrating the fetching and the persistence of data from one or more sources (API calls to a server, a local database, or both). It is here where the row data is converted to entities and value_object.

5- UI folder:

The UI is concerned only with the visual aspects of the application. In flutter, UI views have a top-level widget that contains other widgets, which contain other widgets and so on until you get to the leaf widget.

The UI folder contains at least four subfolders.

pages folder: Since a UI is a collection of pages that the user can navigate between them, the first subfolder of the UI folder is the pages directory. Typically, you dedicate a folder for each page even if it has one file. The advantage of dedicating one folder for a page is to increase readability.

widgets: It contains small reusable widgets that should be independent of the application. They can be used in other applications with a simple copy and paste and with little change if necessary. For example, this folder may contain your best-designed FAB, your super attractive CircularProgressBar, your awsome list tile … etc. By the time you constitute your custom widget library and you know where to find them if you need them in other apps.

Even if you are using widgets from external libraries, it is more convenient to adapt their interface and create your own and use yours throughout the user interface (this is a practical case of the adapter model). Your advantage is to dissociate your user interface from the concrete implementation of external libraries, and in the future, if you change your mind and decide to use another library, you will change one place: here inside the widgets folder.

exception folder: Here you handle exceptions thrown from the domain and service layer.

common folder: contains common helper utilities used in the UI layer (constant, enumeration, style, util classes …).

A big enterprise app may have many areas of interest. By area of interest, I mean an ensemble of related tasks and use cases that share the same knowledge to achieve common goals. In this case, each area of interest can have its own architecture as described above. In domain-driven design, these areas of interest are called bounded context.