Closed 4seer closed 4 years ago
As I see from the first implementation of getting products (FindProductsByFilterUseCaseImpl) it is just a wrapper above the repository that handles errors and throws exceptions when required. Bloc doesn't handle these errors yet. Maybe it is better to try to use error states in bloc to see all the advantages of your approach? And with GetIt - I can't agree to add another lib for the same purpose as the one that already exists in the project. What's wrong with bloc providers? The only difference is using context. What's wrong with context? We use bloc, we create blocs with context. Maybe you just want to rebuild the whole project without bloc approach? :)
You are write the use cases are just wrappers. We are using them in order to separate the business logic of the app from the UI and make each small part that is wrapped by use case a testable piece of code. If you can propose a better vairant for use case separating with Bloc please provide. I do not see a better variant now. As I described above bloc providers rely on context. The business logic should not depend on context if it does not need to simply because Bloc providers require it. Each piece of code should be simple enough to be tested and serve one purpose and rely on other parts of the app as less as possible.
I am not sure if it's relevant here but I came across this article, I thought I should share. It's about 'WidgetView' pattern to separate business logic & UI.
I am not sure if it's relevant here but I came across this article, I thought I should share. It's about 'WidgetView' pattern to separate business logic & UI.
There are a few problems we are facing right now:
Not all Flutter developers use Bloc. Some prefer redux, others something else. So using as general architecture component limits the number of developers that can produce themes for e-commerce app.
WooCommerce is one of many platforms that we can integrate. That means that we need to separate data access level completely from the UI. Businesss logic layer should be separate too.
That is why we are aiming to use Clean Architecture described in this article. Test Driven Development is another concept that makes sence to set up as a wirtten rule for development. Right now most of the implementations available there use functional development Either/Left/Right concept to handle errors instead of exceptions.
My propositions is to completely separate all three layers (Data Access - data in clean architecture, UI and Repositories to access data (remote or local). All three layers should be independent to make each task more simple and testable and to attract more developers to concrete tasks implemetation and futher customization.
The biggest concern I have right now is about get_it package to implement dependency injection. But I did not find anything more suitable in our case. If you have other propositions please provide them before Tuesday so we can figure out a solution for our app and more quicker on its implementation. Thank you.
I am not sure if it's relevant here but I came across this article, I thought I should share. It's about 'WidgetView' pattern to separate business logic & UI.
The concept described in this article is nothing new but MVVM pattern. And it is strange to change patterns in the middle of the development process. If we already said that we will use BLOC and partially implemented it, why all of you still proposing other architecture approaches?
I am not sure if it's relevant here but I came across this article, I thought I should share. It's about 'WidgetView' pattern to separate business logic & UI.
The concept described in this article is nothing new but MVVM pattern. And it is strange to change patterns in the middle of the development process. If we already said that we will use BLOC and partially implemented it, why all of you still proposing other architecture approaches?
Take a look at my comments above. We need to make UI as simple as possible to design new themes in the future and so many other developers with less experience will be able to do that. Therefore the clean architecture principles with use cases "wrapping" are doing exactly that.
Hello, thought I might share some information.
Use Cases are not just "wrappers" , they are the classes that contain the business logic. They should have no dependency to Flutter nor the concrete Repository Implementation. All they know is one or more Repository interfaces. A use case could for example fetch a list of inbox messages from an InboxRepository and then take from each message the author ID and call the UserProfileRepository to load the matching user profiles. Such UseCase could be called LoadMessagesAndProfilesUseCase
. Each UseCase should only have 1 method therefore you can also think of UseCases as commands
Clean Architecture is a nice concept, but it does not replace an UI pattern like MVP, MVVM or BLOC. State management of the UI is a task of the UI architecture and should be done within a presenter, controller or likewise. In mobile development you usually combine clean architecture with an UI architecture, in your case it would be BLOC + Clean Architecture.
I would keep the widgets and its presenter class (or the equivalent in BLOC) stupid and have the state-holder class (the presenter e.g.) call the use case.
E.g. Presenter -> UseCase -> RepositoryInterface <-- RepositoryImpl (Injected through DI) --> Cache / NetworkDataSource
The cache is allowed to have dependencies to flutter specific persistence components like SQLite, the NetworkDataSource would be responsible for doing the right http calls (you can introduce a common http connector class here) and map to/from json or whatever object notation scheme you decide to use.
Also make sure that your UI and your Domain + Data Layers do not share the same model. A change in the REST API will obviously lead to changes in your data model, your UI should be untouched by these changes. In theory each layer should have an own model, so that you'd end up with a UI, Domain and Data model. A common practice is to have naming rules for the layer models, so a Message data model would be called MessageDTO
(DTO = DataTransferObject), the domain Model would be just Message
and a view model could be MessageViewModel
. It's up to you if you want to introduce the domain model, as it usually ends up in more boiler plate code, but through experience, it usually is worth it to have a domain model when working in a complex app with fast changing requirements and a big team.
The advantage of clean architecture is that you can build a big and complex system in a simple way. You can scale horizontally very easily, have clean defined dependencies / boundaries. If done right, you can later just extract your whole domain layer and parts of the data layer and build a web app with only having to focus on building the UI
Hello, thought I might share some information. Use Cases are not just "wrappers" , they are the classes that contain the business logic. They should have no dependency to Flutter nor the concrete Repository Implementation. All they know is one or more Repository interfaces. A use case could for example fetch a list of inbox messages from an InboxRepository and then take from each message the author ID and call the UserProfileRepository to load the matching user profiles. Clean Architecture is a nice concept, but it does not replace an UI pattern like MVP, MVVM or BLOC. State management of the UI should be done within a presenter, controller or likewise. In mobile development you usually combine clean architecture with an UI pattern, in your case it would be BLOC + Clean Architecture. I would keep the widgets and its presenter class (or the equivalent in BLOC) stupid and have the state-holder class (the presenter e.g.) call the use case.
E.g. UseCase -> Repository <-- RepositoryImpl (Injected through DI) --> Cache / NetworkDataSource
Also make sure that your UI and your Domain + Data Layers do not share the same model. A change in the REST API will obviously lead to changes in your data model, your UI should be untouched by these changes. In theory each layer should have an own model, so that you'd end up with a UI, Domain and Data model. It's up to you if you want to introduce the domain model, as it usually ends up in more boiler plate code, but through experience, it usually is worth it to have a domain model when working in a complex app with fast changing requirements and a big team.
Thank you for an extensive explanation. We are implementing clean code architecture and are keeping each part as separate as possible. I agree with you on the fact that patterns like Bloc/MVVM can be used for UI controller/presenter.
The use cases are not just "wrappers" and should contain business logic. The separation of them from the UI will allow other developers create new themes with less time because we should definetly document use cases after implementation and extend them in the future.
I want to add that in our case models and entities will not be the same because they will contain fields and methods relevant to layer they belong to. I think the best way is when models extend entitites. That looks achievable because the common entities for e-commerce like products/orders are alignable between various systems like WooCommer/Shopify and our UI data structure because they share common ordering process worflow.
Hello, thought I might share some information. Use Cases are not just "wrappers" , they are the classes that contain the business logic. They should have no dependency to Flutter nor the concrete Repository Implementation. All they know is one or more Repository interfaces. A use case could for example fetch a list of inbox messages from an InboxRepository and then take from each message the author ID and call the UserProfileRepository to load the matching user profiles. Clean Architecture is a nice concept, but it does not replace an UI pattern like MVP, MVVM or BLOC. State management of the UI should be done within a presenter, controller or likewise. In mobile development you usually combine clean architecture with an UI pattern, in your case it would be BLOC + Clean Architecture. I would keep the widgets and its presenter class (or the equivalent in BLOC) stupid and have the state-holder class (the presenter e.g.) call the use case. E.g. UseCase -> Repository <-- RepositoryImpl (Injected through DI) --> Cache / NetworkDataSource Also make sure that your UI and your Domain + Data Layers do not share the same model. A change in the REST API will obviously lead to changes in your data model, your UI should be untouched by these changes. In theory each layer should have an own model, so that you'd end up with a UI, Domain and Data model. It's up to you if you want to introduce the domain model, as it usually ends up in more boiler plate code, but through experience, it usually is worth it to have a domain model when working in a complex app with fast changing requirements and a big team.
Thank you for an extensive explanation. We are implementing clean code architecture and are keeping each part as separate as possible. I agree with you on the fact that patterns like Bloc/MVVM can be used for UI controller/presenter.
The use cases are not just "wrappers" and should contain business logic. The separation of them from the UI will allow other developers create new themes with less time because we should definetly document use cases after implementation and extend them in the future.
I want to add that in our case models and entities will not be the same because they will contain fields and methods relevant to layer they belong to. I think the best way is when models extend entitites. That looks achievable because the common entities for e-commerce like products/orders are alignable between various systems like WooCommer/Shopify and our UI data structure because they share common ordering process worflow.
Why would you want models to extend entities? Entities are usually considered as being the domain model, they describe the truth (all relevant information) about specific domain. Inheritance between models of different layers would cause a coupling and this is not a good approach for any reasons, one is that this violates the rules of clean architecture by mixing up layers and another one is that one model would heavily depend on the implementation of the other one. Clean Architectures has many benefits but also some disadvantages, one is boiler plate code induced by all those layers and the layer-specific models. In the beginning you will probably struggle with this but in the long term, when the app grew to certain complexity, you will be thankful
Why would you want models to extend entities? Entities are usually considered as being the domain model, they describe the truth (all relevant information) about specific domain. Inheritance between models of different layers would cause a coupling and this is not a good approach for any reasons, one is that this violates the rules of clean architecture by mixing up layers and another one is that one model would heavily depend on the implementation of the other one. Clean Architectures has many benefits but also some disadvantages, one is boiler plate code induced by all those layers and the layer-specific models. In the beginning you will probably struggle with this but in the long term, when the app grew to certain complexity, you will be thankful
Lets take an example. We have class Product
class Product { int id; String title; }
Our RemoteRepository is returning JSON that we convert in our model related only to WooCommerce for instance into data structure.
class ProductWoocommerce extends Product{ fromJson(String json){ //convert from JSON } }
On the other side we have model in UI that is used to display product and contains view related methods that are used everywhere in UI.
class ProductModel extends Product{ convertPriceToString(){ //a method for example } }
Do you see any problems with that from clean architecture point of view?
They should be indepent classes with mappers converting them between layers. But in our case all the data structures are very common and it makes sence to keep them related.
I think @MrIceman speaks about the fact that UI does not require the same data as it is stored in the model. Let me describe: we have a model class Product with title and description. In the product list in UI, we don't need a description. We don't show it. So the classes which present data to UI can be simplified or extended in some cases. And it is a good idea to do this because each backend can have a different model structure.
I think @MrIceman speaks about the fact that UI does not require the same data as it is stored in the model. Let me describe: we have a model class Product with title and description. In the product list in UI, we don't need a description. We don't show it. So the classes which present data to UI can be simplified or extended in some cases. And it is a good idea to do this because each backend can have a different model structure.
Models may have different fields and they can be completely different because they serve different purpose in different layers. Entities should contain data and methods that is common for all parts of the app. Models are used in UI, for instance, and can have methods related only to convert price into string or something like that, for example. Product entity in our case can have a method that gives discount price by calculating it from price and discountPercent (that part is possible but not mandatory).
They should be indepent classes with mappers converting them between layers. But in our case all the data structures are very common and it makes sence to keep them related.
This is maybe valid now, but this can change very quickly once you start developing the backend and the API will underlay changes. If you do it that way you might benefit from it for now, but you restrict yourself and will probably face a lot trouble when API changes happen. @Yahii sums it up well, your UI model does not need necessarily need the information of a domain / data model. In clean architecture your entities should not contain any logic, the logic should be implemented by the use cases, which use the entities / domain models. Entities are domain models. They are created by the data provided from the data layer and are used as source of information for the UI model.
Thanks for you input guys. Lets see where we can get from here.
We are separating UI and business logic. The App Layer structure is like that:
Since we are separating repositories and UI by use case level we have to avoid using MultiRepositoryProvider because use cases are outside of current context. If you see any better solutions to separate logic and UI leave a comment here.
Dependency injection is implemented using get_it flutter package.