Sprint2 - fedemelo/unitrade-wiki GitHub Wiki
This page gathers all deliverables expected for Sprint 2.
- Sprint 2: Deliverable
The following table contains all the business questions that were implemented in this sprint, with their respective type, rationale, and the view in which they were implemented. Notice the questions are a subset of those presented in the Sprint 1 Deliverable.
№ | Business question | Type | Rationale | View in which it was implemented |
---|---|---|---|---|
1 | Which days and times see the highest user engagement on the app? | Type 4 | Understanding peak activity times can help optimize notifications, promotions, and customer support on UniTrade. | Analytics Dashboard |
2 | What are the most selected interests when users log in for the first time? | Type * (2 and 4) | This question helps personalize the app experience by recommending relevant products and services, increasing engagement. It could also be of interest to third parties which are the interests of the app's audience. | Login View / Analytics Dashboard |
3 | What is the most selected group by users on the home view? | Type 2 | Identifying popular categories can enhance user navigation by prioritizing high-interest items in the UI. | Home Page /Analytics Dashboard |
4 | What is the distribution of app crashes by device type and OS version? | Type 1 | Monitoring app stability helps prioritize fixes and optimize performance, reducing user frustration and churn. | Analytics Dashboard |
5 | What proportion of users don’t upload a photo vs. upload a photo from the gallery vs. upload a photo from the camera? | Type 3 | Evaluating user behavior when listing items can inform feature improvements or removals, and optimize both the listing process for the user and the storage use. | Analytics Dashboard |
6 | What proportion of users sell and what proportion of users rent for each category? | Type 3 | Understanding the split between sellers and renters helps UniTrade tailor its marketplace to user needs. | Analytics Dashboard |
The analytics pipeline is designed to clearly define each stage that contributes to the visualization of key business questions. To achieve this, the pipeline is structured into the following six distinct steps:
The initial stage of the pipeline involves collecting data from two primary sources:
- Users Data: Data collected from the user interactions and profiles.
- Listings Data: Data on items or listings when they're uploaded.
Once data is ingested, it's organized and formatted to JSON according to the structure of Firebase, which is the central platform used for managing data storage. This stage ensures that all incoming data conforms to the schema used by Firebase services, preparing it for storage.
The pipeline utilizes two core Firebase services for data storage:
- Cloud Firestore (NoSQL): This database stores users and listings data in a NoSQL structure.
- Firebase Storage: This service is used for storing larger objects like images of the products that are associated with the data.
Data computation is where specific queries are made using the Firebase REST API. These queries extract relevant data from Cloud Firestore, based on the business questions and the analytical requirements. This stage involves extracting, filtering, and aggregating data to derive insights and prepare it for presentation.
The final stage of the pipeline is the presentation layer. Here, the processed data is visualized using Power BI.
Business Question (BQ) | Transformation/Queries | Implemented Visualization (Type and Details) |
---|---|---|
Which days and times see the highest user engagement on the app? | Query user engagement logs by timestamp. Aggregate and group by day of the week and hour. | Horizontal Grouped Bar Graph: Y-axis as days of the week and hours, where each group of bars represents a day and each bar represents an hour; X-axis as number of logins. |
What are the most selected interests when users log in for the first time? | Query the 'interests' field when a user logs in for the first time. Aggregate the count of selected interests. | Vertical Bar Graph: X-axis representing interests, Y-axis representing the number of users selecting each interest, ordered by count. |
What is the most selected group by users on the home view? | Query the 'group' selections made by users when they interact with the home view and aggregate to find the most selected categories. | Horizontal Bar Graph: Each bar representing a category, with bar length based on the percentage of users selecting that category. |
What is the distribution of app crashes by device type and OS version? | Query app crash logs by device type and OS version. Aggregate and group by each category to find the distribution. | Vertical Bar Graph: X-axis as OS versions, Y-axis as crash counts, with bars representing device types. |
What proportion of users don’t upload a photo vs. upload a photo from the gallery vs. upload a photo from the camera? | Query photo upload behavior during the listing process and categorize as ‘no photo,’ ‘photo from gallery,’ or ‘photo from camera’. Calculate proportions. | Pie Chart: Each section representing a photo behavior category, with percentage values. |
What proportion of users sell and what proportion of users rent for each category? | Query user actions in the marketplace (sell or rent) and aggregate by product category to calculate proportions. | Vertical Grouped Bar Graph: X-axis as categories, Y-axis as percentage, with bars divided into "Sell" and "Lease" actions. |
A complete architectural design of the system is provided in the form of numerous diagrams, most of them adhering to the UML standard. The diagramas comprehensively display the architectural styles, patterns and tactics that were used, as well as the design patterns leveraged. Each diagram has an explanation that details the rationale behind the design choices.
The chosen diagrams are:
- Technology Stack Overview
- High-Level Architecture Diagram
- UML Component Diagram
- UML Class Diagrams (Swift)
- Widget/Component Diagrams (Flutter)
The mobile app project leverages a diverse set of technologies to ensure a high-performance, cross-platform experience, supported by robust backend services and third-party integrations.
-
Frontend:
- Flutter (Android-specific): Flutter is used for developing the Android version of the app, leveraging its widget-based architecture to create a reactive and dynamic UI. The language used in Flutter is Dart, which allows for cross-platform development and future expansion or modification.
- Swift (iOS-specific): For the iOS version, Swift is utilized to provide native performance and platform-specific features, ensuring the app delivers an optimal experience for iOS users. The language used for iOS development is Swift.
-
Backend Services:
-
Firebase: Core services from Firebase power key app functionalities, including:
- Firebase Authentication: Provides secure user authentication, allowing users to register and log in via email and password.
- Firebase Storage: Handles the storage of images and other files uploaded by users for product listings.
- Firebase Realtime Database: Supports real-time data syncing, storing user data, product details, and interactions, enabling dynamic updates without delays.
- RESTful API (FastAPI): In addition to Firebase, a custom backend is implemented using FastAPI, a modern and highly efficient Python web framework. This RESTful API provides additional functionality, such as determining product categories when users upload items. FastAPI ensures fast, scalable services that handle tasks complementary to Firebase, improving the app’s overall flexibility and performance.
-
Firebase: Core services from Firebase power key app functionalities, including:
-
Analytics:
- Custom Analytics Pipeline: The analytics pipeline tracks key user metrics, crashes, and engagement data, providing valuable insights that help answer business questions. These insights drive data-informed improvements, feature optimizations, and enhance decision-making processes for future development.
The previous diagram represents a high-level architecture diagram that will be implemented for this sprint in both applications in they use the MVVM (Model-View-ViewModel) architecture pattern. The components are divided into 3 main sections: View, ViewModel and Model. The view section is the user interface where the user interacts with the app. It captures UI events (e.g., button clicks, data inputs) from the user and sends these to the ViewModel. It also listens for Property change events that come from the ViewModel to update the UI. The next layer, the ViewModel, acts as an intermediary between the view and the model, it processes the UI events from the and interacts with the model. When the information from the model changes, it sends an event to the view to update the UI accordingly. Finally, the model layer represents the business and logic of the application and notifies the ViewModel of changes. This layer is also the one responsible for interacting with the FireBase service. For this sprint we have implemented 3 Firebase services which are: Authenticacion, Storage and Realtime Database. Authenticacion simply allows the app to register and sign In users using email and password. Storage will handle images corresponding to products that the users can upload. And realtime database will store structured data corresponding to user information such as name, favorite categories and what products they have bought, sold and published.
The diagram above is a UML components diagram for both applications. Recall a component is an independent and self-contained unit of a system that performs a specific function. In this architecture, the Flutter Mobile Application and Swift Mobile Application serve as the client-side components, each communicating with Firebase services and a Category Assignment API. Firebase components include the Firestore Database for storing structured data, Firebase Storage for managing product images, and Firebase Authentication for securing user access. The Category Assignment API is the other component, responsible for assigning categories to products via its endpoint, providing essential classification functionality to both client applications.
The class diagram above shows the relationship between the classes that implement the Product Upload feature. Given that the views designed for uploading a product for sale and those for uploading a product for rent share many similarities, writing a separate view for each would result in unnecessary code duplication. Therefore, the Strategy pattern was considered appropriate.
The class diagram above shows the implementation of the Strategy design pattern. The Strategy pattern allows a class, typically called the context, to choose its behavior at runtime by selecting a strategy. In this case, the UploadProductViewModel
class (which manages the UploadProductView
class) is the context, and it can choose between two strategies: SaleProductStrategy
and RentProductStrategy
. The ProductUploadStrategy
protocol defines the interface for the strategies, which are implemented by the concrete classes. The context class has a property of type ProductUploadStrategy
, which is set at runtime to one of the concrete strategies. This is determined by the view where the user decides whether to sell or rent a product, which is ChooseUploadTypeView
.
In this way, the context class can modify its behavior, and this change is delegated to the strategy class. The strategy can be changed without modifying the context class. Using this pattern provides two main benefits:
- If we ever want to add another option that is neither renting nor selling (e.g., donating to the university, or some other option), adding the new strategy will be straightforward. It is very clear how to add it and the code is completely independent of the other strategies.
- If we ever want to change the way putting a product for sale or rent works, such as adding or removing fields, it is clear where the changes should be made, and they will not affect any other code (neither the main context class nor the other strategies).
The previous diagram represents the singleton pattern implemented for the FirebaseAuth instance. This pattern offers several advantages, particularly allowing consistent access to the same instance throughout all the application. By allowing only a single instance of a class to be created, the singleton pattern prevents the unnecessary overhead of creating multiple instances that could lead to resource exhaustion or data inconsistency. For example, in the case of FirebaseAuthManager, the singleton pattern ensures that all parts of the application reference the same instance of the FirebaseAuth service, thereby centralizing authentication logic and promoting efficient resource management. This leads to a more streamlined architecture, where changes or updates to authentication logic only need to occur in one place, simplifying maintenance and reducing the potential for errors.
Moreover, the singleton pattern enhances the ease of access to critical functionalities across different components of an application. In the context of the UniTrade application, having a single instance of FirebaseAuthManager allows any part of the app, including the login view and view model, to easily access authentication methods without the need to instantiate multiple objects. This centralized access not only promotes code reuse and consistency but also encourages best practices in software design. By managing global state effectively and reducing the complexity of dependency management, the singleton pattern contributes to cleaner, more maintainable code, which is essential for long-term project success.
The previous diagram represents the Observer pattern implemented for managing the data flow between a ViewModel
and the SwiftUI views. This pattern provides certain advantages, like ensuring that changes in the data automatically reflect in the user interface without requiring manual synchronization (which gets along very well with th MVVM Architectural Style explained above). By enabling one class to observe changes in another, the Observer pattern promotes consistency across the app and reduces the complexity of managing state manually.
For example, in the case of a ViewModel
conforming to the ObservableObject
protocol, the pattern ensures that all views observing the ViewModel
are automatically updated whenever a property marked with @Published
changes. This centralizes the state management logic, ensuring that updates propagate to all interested views. The views observe the data in the ViewModel
via @StateObject
or @ObservedObject
, and when any data changes, they are automatically re-rendered. The ViewModel acts as the Publisher (ObservableObject), and views like ExplorerView act as Subscribers that automatically update when data changes.
The class diagram shows how the Strategy pattern is used in the Flutter app's product upload feature to improve code reuse and flexibility. The main components are:
-
UploadProductView
- Inherits from
StatelessWidget
, indicating that it is a Flutter UI component. - Contains a
_type
attribute that determines the product type.
- Inherits from
-
UploadProductViewModel (Context)
- Acts as the context in the Strategy pattern.
- Contains a
_strategy
attribute of typeUploadProductStrategy
. - Maintains various form-related attributes such as
_name
,_description
,_price
, etc. - The
submit
method implements the logic to manage product data using the strategy.
-
UploadProductStrategy (Interface)
- Defines an interface for product strategies with methods
saveImage
,saveProduct
, andtoMap
.
- Defines an interface for product strategies with methods
-
Concrete Strategies (SaleStrategy and LeaseStrategy)
- Implements the
UploadProductStrategy
. - Each strategy class contains its own attributes, like
userId
,type
,name
,description
, and specific attributes for its purpose (e.g.,rentalPeriod
forLeaseStrategy
). - Provides specific implementations for the strategy methods.
- Implements the
The Strategy pattern helps avoid code duplication by allowing UploadProductViewModel
to delegate tasks to different strategy classes depending on the product type (sale or lease). The appropriate strategy is chosen at runtime, enabling flexible behavior without modifying the context class. Similar to the iOS Swift implementation, adding new strategies (like donation or exchange) is simple: just create a new class that follows the UploadProductStrategy
interface. This keeps changes isolated to specific strategy classes, so altering product upload logic (e.g., adding fields) doesn't impact other parts of the application.
The class diagram above shows the implementation done in the code of the Singleton pattern in the Flutter application. In this case the Singleton pattern is used to ensure that a class has only one instance while providing a global access point to that instance. This is particularly useful in our application for the different FireBase services which needs to coordinate actions across all the application. Whereas before each component of the application had to create its own FireAuth instance (same for firestore and firebaseStorage), now there is a single instance which all of the files call in order to use the functionalities.
By ensuring a single instance of Firebase services is shared across the entire application, it provides a clean, efficient, and maintainable way to handle Firebase operations. This allows the app to avoid redundancy, maintain consistent states, and ensure proper resource management.
This diagram illustrates the application of the Decorator Pattern for handling product price display in a ProductCardViewModel. The diagram starts with an interface Price, which defines a method getPrice() that returns a Text widget, this interface is implemented by BasePrice, which provides the basic price presentation functionality. The class PriceDecorator acts as an abstract decorator that holds a reference to another Price object (wrapping it) and adds additional behaviors without altering the original price logic. The decorators FormatDecorator and CurrencyDecorator extend PriceDecorator and modify the way the price is formatted or how the currency symbol is handled. The ProductCardViewModel interacts with the Price interface, allowing dynamic decoration of the price presentation depending on the specific requirements (e.g., different formats, currency symbols). This structure enables flexibility and modularity in how the price is displayed, ensuring that different formatting or currency representations can be added or modified independently without changing the core logic. The Decorator Pattern offers different advantages, particularly in increasing flexibility and promoting code reusability. By using this pattern new functionalities or modifications, like formatting or currency display, can be added to an object dynamically without altering its original structure. This keeps the class BasePrice clean and free of unnecessary complexity, adhering to the Single Responsibility Principle.
№ | Feature | View | Description | Characteristics |
---|---|---|---|---|
1 | User Authentication | Login and Create Account | Allows users to register and log in. | 1. User authentication |
2 | Interest-based Personalization | Recommendations | Collects user interests (e.g., study major or hobbies) during onboarding to personalize the experience. | 1. Aids the smart feature functionality (interest-based personalization) |
3 | Product Listings | Home Page | Displays products available to buy or rent on the homepage with filters and categories. | 1. External service integration (Firebase) |
4 | Personalized Recommendations | Home Page | Recommends products based on the user's interests or preferences. | 1. Smart feature (personalized suggestions based on tags) |
5 | Product Upload | Product Upload | Allows users to upload products for sale or rent, with fields for name, description, price, and type. | 1. Uses phone sensors (camera for photos) 2. External service integration (categories API) |
6 | Light/Dark Mode | N/A | Automatically switches between light and dark mode based on the time of day. | 1. Context-aware feature (time of day) |