Sprint 2 - MOVILES-G22-2025/Wiki GitHub Wiki
After designing our user interface for the Senemarket app, we review the entire Design Thinking phase. Next, we begin work on the product backlog, developing features while applying the concepts of architecture, design, and design patterns discussed in class during this Sprint 2.
Business questions (BQs)
Below are the business questions (BQs) implemented in this Sprint along with their respective types and rationale.
Type | Question | Rationale |
---|---|---|
1 | What are the busy hours for the marketplace usage? | This question tracks app growth and marketing effectiveness by analyzing installation data over time. It provides insights into marketing reach, adoption trends and user acquisition without requiring direct user feedback or behavior analysis. |
1 | How many users create an account and log in per week? | This is pure telemetry data collected from system logs, tracking user registrations and logins over time. It helps measure user adoption and engagement trends without requiring direct user feedback. Analyzing this data allows for evaluating marketing efforts and understanding the platform's growth. |
2 | Which filters should be suggested to the user based on their past searches and clicks? | This question enhances the individual user’s experience by providing personalized filter recommendations based on previous interactions. By dynamically suggesting relevant filters, the search process becomes more efficient and user-friendly, improving engagement and satisfaction. |
2 | Has a saved item’s price dropped or has a new listing appeared for a saved search? | This question enhances user engagement and retention by keeping them informed about relevant updates without requiring manual searches. By proactively notifying users when a saved item's price decreases or a new relevant listing appears, the platform increases conversion rates and user satisfaction. |
3 | Which product categories receive the highest user engagement in terms of searches, listings, and transactions? | This helps identify which categories drive the most user activity, guiding improvements to navigation, recommendations and potential expansion of the marketplace. By analyzing user interest, the platform can prioritize high-performing categories and introduce new categories aligned with user demand. |
3 | What percentage of users enable push notifications, and do they interact with them? | This helps assess the effectiveness of push notifications in driving user engagement. By analyzing opt-in rates and interaction levels, the platform can optimize notification strategies to improve retention, reduce churn, and enhance the overall user experience. Understanding user responsiveness also informs decisions on refining notification frequency, content, and timing to maximize impact. |
The second business question (How many users create an account and log in per week?) was changed since the one defined in Sprint 1 was not convenient since we were not going to publish the application in the store.
Analytics pipeline (AP)
The analytics pipeline in the application is designed to collect, store, processing and analyze user activity data to support business questions insights and decision-making.
1. Data source layer
At the application level (Flutter and Kotlin frontend), we track key events by interacting with the Firebase backend. These events are captured automatically and saved to specific Firestore collections.
2. Storage layer
All user events and relevant app data is stored in Google Firebase, which acts as our cloud database. Firestore ensures high availability, real-time data synchronization and automatic scaling.
3. Processing layer
A Python-based ETL (Extract, Transform, Load) script:
- Connects securely to Firebase using Firebase Admin SDK and a service account key
- Extract and queries data from Firestore using date filters
- Aggregates metrics
- Converts it into a Pandas DataFrame
4. Analyze layer
The aggregated data is saved and appended to a structured CSV file. This acts as our intermediate analytics repository and supports historical tracking. At this point, we have processed data ready to view.
5. Presentation layer
The CSV output is imported into Power BI, where we visualize historical data using various charts and filters. This layer provides actionable insights and a centralized view for stakeholders for monitoring user engagement and app usage patterns.
Benefits
- Low overhead and easy-to-maintain pipeline
- Allows historical data tracking via CSV
- Fully integrated with Firebase’s real-time database
- Flexible for future expansion
Architectural design (AD)
The solution architecture is described below, along with the justification for the architectural design decisions:
Flutter
For our application we decided to use an MVVM (Model-View-ViewModel) architecture, it allows to separate the responsibilities between the different layers of the architecture. The model represents the data and business logic, such as the structure of an user or product. In the view, we have the UI where the end user is shown the different functionalities and screens with which they interact. In the ViewModel we have the presentation logic, it allows communication between the data and the UI. This results in a more organized and easy-to-maintain code. At the same time, this architecture allows the application to grow in the future as it has more users, allowing its functionalities to grow as the app does. This approach means that the logic of one of the layers does not have to be directly altered when another layer is modified.
This is the structure of a MVVM Architecture:
This is the structuring of the application code
You can see the organization of directories, the data directory, with subdirectories such as dto, repositories, services takes care of everything related to obtaining and storing data in firebase. On the other hand, within presentation are views, which are the screens or widgets visible to the user, and viewmodels, which handle the presentation logic and serve as a bridge between the data layer and the views. In this way, the views are kept as simple as possible, focused on displaying the information and handling user interaction, while the data layer performs the access or processing operations, and the viewmodels coordinate both to offer a more organized and easy-to-maintain code.
Patterns
-
In our application, we used the Singleton pattern in the authentication module to guarantee a single, global instance of the authentication service (like FirebaseAuth) throughout the app lifecycle. The primary goal of using the Singleton pattern here is to ensure consistency, centralized control, and resource efficiency. By having one instance manage user authentication states and sessions, we prevent multiple conflicting authentication instances, which could cause errors or security issues. Placing this Singleton within the data or service layer also improves maintainability, as changes to authentication logic become easier to handle and reflect across the entire application consistently.
-
In our project, we implemented the Repository pattern to separate data access logic (Firebase, Algolia, etc.) from the business logic and presentation layer (UI). The primary purpose of using this pattern is to achieve cleaner, maintainable, and easily testable code. By defining abstract interfaces (ProductRepository, UserRepository) in the domain layer and providing concrete implementations (ProductRepositoryImpl, etc.) in the data layer, we can easily replace data sources without affecting the app logic. This approach enhances modularity, flexibility, and scalability, allowing us to adapt the application to future data source changes without impacting the user interface or business logic.
-
We implemented the Observer pattern in our app mainly through the MVVM architecture, where ViewModels use Flutter's ChangeNotifier and Provider to manage and broadcast state changes. The Observer pattern ensures our user interface automatically updates when data or state changes occur in the ViewModel, such as product searches, user login states, or adding favorites. This design choice provides clear separation between business logic and UI, making our code easy to test, maintain, and scale. Using the Observer pattern improves user experience by making sure the UI always reflects the most recent data, responding smoothly to user interactions without manual intervention.
In this component diagram, you can see in more detail the implementation of the application's architecture with MVVM and the previously mentioned patterns. First, the view layer is shown, which includes the screens that the Flutter application comprises (login, home, addProduct, and detailProduct). Within the same view, the Observer pattern is applied, grouping all the view components that, as the name suggests, observe the ViewModels. Next, there is the ViewModels layer, which contains a component for each of the previously defined views (login, home, addProduct, detailProduct). Then, you can see that the components connect to the models component within that layer, except for the login, which connects to a singleton (whose functionalities were mentioned earlier) and then connects to Firebase Auth. On the other hand, the Models component that includes the search functionality connects with Argolia for that feature and with the repository that manages the data logic and communicates with FirebaseFirestore.
Kotlin
MVVM whit clean architecture
Clean Architecture is a widely adopted architectural pattern in software engineering, designed to organize code in a way that separates different concerns into distinct layers. It aims to structure software systems with a strong focus on maintainability, flexibility, and independence from external factors like frameworks or databases.
At the core of Clean Architecture is the concept of organizing code into concentric circles, with the innermost circles containing the business logic and domain objects, free from any dependencies on external systems. This separation allows the core logic to remain unaffected by changes in the outer layers, such as user interfaces, infrastructure, or databases. In other words, the business logic stands at the center, and any specific technologies or frameworks are kept on the outer circles, ensuring that the core of the application remains stable and adaptable.
The SeneMarket app’s architecture is designed to be scalable, maintainable, and testable, with a clear separation of concerns. The MVVM pattern ensures that the UI logic is separated from business logic, while Clean Architecture guarantees modularity and ease of extension. The chosen technologies, such as Firebase , enable efficient real-time data handling and location services, providing a seamless user experience.
Patterns
Using design patterns in Kotlin helps us structure our applications more effectively, ensuring code reusability, maintainability, and scalability. Kotlin’s concise syntax and modern features make it easy for us to implement these patterns, leading to cleaner and more efficient applications.
- Singleton for Authentication The Singleton pattern ensures that a class has only one instance throughout the application. This is especially useful for authentication, where maintaining a single instance of the authenticated user’s data prevents unnecessary reloads and ensures data consistency across the system. With Kotlin’s object declaration, we can create a Singleton effortlessly, eliminating the need for extra boilerplate code. By centralizing authentication data, we manage session states more efficiently without having to pass user information between components.
- Repository The Repository pattern allows us to separate data access logic from the rest of the application, making it easier to switch between different data sources, such as local databases and APIs. By implementing this pattern, we improve code organization through a dedicated layer that handles data transactions, reducing dependencies across the application. It also makes our code more testable, as we can use mock repositories to simulate data access in unit tests
- Facade The Facade pattern simplifies how we interact with complex systems by providing a unified interface to a set of underlying classes. This is particularly useful when working with multiple services, APIs, or libraries, as it abstracts intricate implementation details and exposes only the necessary functionality. By using a Facade, we make our applications more modular and maintainable, allowing us to modify internal components without affecting the rest of the system. Thanks to Kotlin’s expressive syntax, implementing a Facade feels intuitive and helps us write cleaner, more organized code.
Pattern | Type | Use for |
---|---|---|
Singleton | Creational pattern | Controls a single instance of a class |
Repository | Structural pattern | Abstracts persistence logic |
Facade | Structural pattern | Provides a simplified interface for a complex system |
Tools/technologies
Kotlin
Jetpack Compose
Jetpack Compose is a modern declarative UI framework for Android, developed by Google. It's based on Kotlin and allows you to build user interfaces more efficiently and concisely compared to the traditional XML-based approach.
Key Features of Jetpack Compose
- Declarative: Instead of directly manipulating views, you define how the UI should look based on the current state of the app.
- Written in Kotlin: Uses Kotlin functions to define interface components.
- Reactivity: Automatically updates when data changes.
- Less Code: Reduces the amount of code required compared to XML and ViewBinding.
- Jetpack Component Integration: Integrates with ViewModel, LiveData, Navigation, and other Android Jetpack components.
- Traditional View Support: Can coexist with the XML-based system.
List of implemented features (IF)
This are the implemented features for both apps, Flutter and Kotlin:
The features implemented in this sprint were:
- User authentication (sign in and sign up) requesting Uniandes email and additional students data.
- Add a product for sale with its respective information using the phone's camera sensor and accessing the photo gallery.
- List of available products with their respective details (name, description, images and seller).
- Personalized filter recommendations by category based on user searches and clicks.
- Efficient product search based on product name and description.
- Personalized notifications when a user's favorite product drops in price.