Clean Architecture With Bloc Pattern

Karan Gore
9 min readMay 22, 2022

--

Hello guys, IT is a whole new world every day we come up with something new and we get so much to learn with each other. Every other project we pick up or company we join we see new things implemented and that's the magic of the IT industry.

INTRODUCTION

A lot of companies are nowadays trying to adapt clean architecture in their flutter project well they are on the correct path as this architecture comes with a lot of flexibility and offers us to keep all our code in a layered perspective.

Any new feature can be easily adapted using clean architecture without harming any other functionality. I’m not going to drag you into much detail but I’ll be sharing some example code sources that you guys can use in your future project.

So it's not compulsory to use the Bloc pattern with clean architecture but it's a culture people follow and it also makes it simple to implement.

What you’ll take with you at the end of this story

Why Clean architecture?

The 3 layers and sub layers

Connections between each layer

Using Bloc pattern

How Does This Help?

Source Code

Why Clean Architecture?

Keep your code clean!!
Remember the days when we used MVC and MVVM architecture and how we use to mess up in some critical cases when it comes up to handling API services. business logic and presentation layers. There used to be some cases when we ended up writing business logic in API services or in the presentations layer, but hey no more! So we should separate code into independent layers and depend on abstractions instead of concrete implementations. So when I say abstractions it means abstract classes are also involved here, nothing to be scared of these classes as they will help us to increase the usability of code and also makes new dev understand the code easily.
Clean Architecture can be used for any project but mostly it is recommended for large-scale projects and projects who have complex functionalities.

The 3 Layers and its sub-layers

The parent Layers are as follows:

Data

Domain

Presentation

The above flow chart will explain how the clean architecture layers interact with each other and how they come back to each component.
I’m not a big fan of having models & entities in the Domain layer, I like to keep it separate from this structure to keep it simple.

1. Presentation:

The name itself implies its role we have all the UI presented here with the addition of State management classes here as we are using Bloc to manage as the state. So Presentation is a directory with Pages & Cubit as subdirectories in it. Pages will contain all the UI-related code and the Cubit will be having the state management classes.

So now hot it works :
Pages -> Cubit -> Usecases
Cubits contain functions/events that will perform a task and in response send a State to the pages to react accordingly.
Cubits are responsible to call the Usecases.

2. Domain

The domain layer is responsible for business logic and it also acts as a mediator between Data and Presentations the manipulations or parameters to be sent are initialized here and are sent to the Data layer.

Repository:
One of the necessary directories in this architecture basically is an abstract class with the functions declaration only and also specifies the Return type and the parameter types to be sent to perform a task. The name of the classes is decided as per the task to be performed for example “GetUserData”.
Let's say we need to call an API that needs a body as parameters to be sent and returns a JSON as a Model. All these types are specified in the Repository.
But hey what if the API returns an Error?”

To solve such problems we use an extraordinary package from Dart called Dartz. This will help us to accept 2 types of return types which in our case can be an error or the Model, “hmm this is making things easy”.
Let me show you a blueprint of what it looks like:

Either is a class of dartz which allows us to return 2 types of classes in our case it is:
1. Failure -> called as Left section of Either
2. UserDataEntity -> called as Right section of Either.
So getUser Function can either return Failure or Map<String, dynamic> but the parameter of the getUser method represents the body we are sending to the API.

Usecase:
Usecases are the classes that represent the Actions to be taken place on a specific button press or page landing. They have the repository initialized in the class and just call the specific repository method, but before that is there any manipulation needed to be done to the parameters sent as the body is done here. Basically, the business logic before sending any parameters to API via Repository is done here. The name of these classes will represent the action be taken place for example AddUserDetails.

Don’t worry we have the source code below which will explain the workflow of this Usecase.
So how it looks like?

Usecase Abstract class

As you can see above I have an Usecase abstract class which I extend every time when I need an Usecase this helps me to decrease the line of codes I write to create a usecase. The call method helps me to define the params and return type dynamically. The AddUseDetails class extends the Usecase which gives us a “call” method which requires a return Type and the Params need to be sent to the Repo.

The AddUseDetailsUsecase is initialized in the Presentation Layer inside Cubit and with help of that object we call the AddUseDetailsUsecase method

So here is out Cubit initialing the GetUserDetails Usecase and invoking the method.
As you can see with respect to the bloc pattern using Cubit we have to emit the states with the data we need to pass to it these states are nothing but classes with final parameters initialized with created. According to the states, the UI reacts.

3. Data

Oh, the main part of the Clean Architecture is where the API calls are made and the errors are thrown if occurred. So what we have here is the implementation of the Domain Repository and services file where the API calls take place. Let me show you the file structure.

Repository (Implementations)
As you guys remember we had a Repository with the abstract class so there should be a place where the methods mentioned there should be defined as it was only declared in the Domin->Repo.
In this class, we extend the class and provide the definition of the methods and with the help of the Data source, we call the API and then return the result to the Usecase.

As you can see above we have extended the GetUserRepo from the Domain layer and defined the methods we had. Also, the Data source Class is been initialized as it contains the API calls and returns expected data or throws and errors. We are using dartz which comes with an option to return 2 types whatsoever is the case. So as you can see in the image above in lines 8 and 10 we have the Right & Left classes which will help us to send the result, in our case the Right is the expected data and the Left is the Failure/Error thrown from the data source. That's pretty straightforward.

Data source
This is the place where the API HTTP calls take place and return us the expected value or throws the error, nothing much to explain on this part as it's made to keep the hardcore calls aside and throw the errors that ever occur.

Uhm, why do we have an abstract class here?
Just to make things simple and understandable let the developer know what kind and how many API calls are here.
So as you can see we have a method called getUser which accepts a String, we are not using the Parameter class here it's not meant to be used in the Data source neither we are using UserDataEntity as you can see we have UserDataModel so anyhow it extends to the UserDataEntity but it's not recommended to use it in the data source.
The data source returns the expected model or throws our user-defined failures which you find in the given source code.

Why do we have data-model & data-entity as 2 different parts?
So Data entities are meant to be used in the Domain and Presentation layer & Data Models should be used in the Data layer. The Domain layer is meant to call the Data sources ( API ) and return to the presentation but what if we have some manipulations that need to be done to the data returned from the Data source/data layer, we should never manipulate and model object but we can an entity object as the model is a blueprint of the JSON returned and the entity is a reflection of Model which can be manipulated in future as per our presentation requirement. If we try to manipulate the Model then we have to do it as a global change but it's not always required so better to change the entity object or manipulate it and pass it to the presentations, all this manipulation is expected to be done in the Domain->Usecase Layer. As the Usecase is initialized in the Presentation->Cubit I like to keep it simple and smart as the Cubit is already having a responsibility to emit the States as its main agenda is to do that, why not we can use Usecase to manipulate the data and send it back as its a bridge for passing and receiving data.

Well guys I won’t stretch this topic more I tried to explain it in a simple way I hope that helped.

Bonus

I’m a template of clean architecture which you can easily use in your projects, the directory called getUser is an example there explaining how to use clean architecture.
Please feel free to clone/download the template and use it in your awesome flutter projects.

--

--