Dogs We Love™ 🎉 is an app that shows a lists of dogs.
The goal of the project is to showcase my knowledge on Flutter and it's technologies. My architectural decisions followed simple principles: Simplicity, testability, modularity, scalability and maintainability to name a few.
I named simplicity first, because that is my modus operandi, that's how I usually work, that's when I feel comfortable. But don't be fooled, the project is simple yet robust. It lives on a rock-solid foundation suitable for new features, improvements and life-long development.
Clean Architecture, SOLID Principles and Domain-Driven Design are at the core of this app's architecture. First I divided the app into 3 layers with distinct set of responsibilities. Data, Domain and Presentation.
This layer is the foundation of the whole application. I started here to match my code structure to
the business domain. In this case, delivering dog objects to the user. Notice that the Domain
layer is
independent from any other layers. Actually if we exclude Equatable, it only depends on the Dart
language itself.
This approach allows the domain to be independent from the Data
and Presentation
layer. Ideally
decisions made on what database to use or how to display the list of dogs to the user should not
result in
any code change to the Domain
layer.
This layer manages data access and exposes said data using Domain
's Repositories. This layer is
responsible to retrieve data from the internet and/or local cache (e.g.
retrofit, Floor
, SharedPreferences).
We call data source any library, service or client used to retrieve data. In this app we have two data sources.
- Remote: Implemented using retrofit, it handles http calls for us and automatically casts data into our DTOs.
- Local: Implemented using floor, it caches remote data into a local database.
This layer is closest to what the user sees on the screen. The presentation layer is made easy by the bloc library. Even though we use the bloc library, I decided to use cubits since they are simple yet robust, following the principles mentioned above.
This project takes advantage of many popular libraries and tools in the Flutter ecosystem. Here is a list of the relevant libraries used in the project.
- bloc: A predictable state management library that helps implement the BLoC (Business Logic Component) design pattern.
- flutter_bloc: Easy to implement the BLoC (Business Logic Component) design pattern.
- google_fonts: Fonts from fonts.google.com in your Flutter app.
- retrofit: An dio client generator using source_gen and inspired by Chopper and Retrofit.
- get_it: A simple Service Locator for Dart and Flutter.
- equatable: Value based equality without needing to explicitly override == and hashCode.
- mocktail: A Dart mock library which simplifies mocking with null safety support and no manual mocks or code generation.
To calculate the coverage you should have lcov
in your computer. If that is not the case, you can
install it by running the following command.
brew install lcov
Once installed, you can run the following command at the root of the project. This will run all
tests, output a lcov.info
file and finally, the script will auto-magically open a report in your
browser.
sh coverage.sh
Daniel Llamas, Android Developer
soyllamas.com
Made with ❤️ from Daniel.