-
Notifications
You must be signed in to change notification settings - Fork 1
Design Document
| Date | Version | Details |
|---|---|---|
| 2025/10/05 | v1.0 | Initial Documentation |
| 2025/10/10 | v1.1 | Update API Specification |
| 2025/10/14 | v1.2 | Remove API Specification |
| 2025/10/19 | v2.0 | Separate PoC page, update diagrams |
| 2025/10/19 | v3.0 | Update testing plan & diagrams |
The figure above illustrates overall architecture of MomenTag. MomenTag consists of 3 layers; android frontend, DRF backend and cloud-based resources.
The frontend, built with Android and jetpack compose communicates with the backend using Retrofit as HTTP client, to handle user interactions such as uploading photos, tagging images, and retrieving semantic search results.
On the backend, the application runs on containerized environment within Google Cloud Platform compute engine, using the Django REST Framework to process client requests, manage user data, and coordinate external services. When a text query or an image is uploaded, the backend utilizes a Vision Transformer model from HuggingFace for generating an embedding vector. Work distribution is performed using Celery as task queue, with redis container as broker.
MomenTag uses two kinds of database. Image embeddings and their metadata are saved on Qdrant, a vector database optimized for similarity search, enabling fast and accurate retrieval of contextually related images. Meanwhile, user profiles, authentication data, and metadata are maintained in Cloud SQL, ensuring reliability and scalability. This modular architecture allows efficient communication between components and supports both high-performance vector search and flexible backend management for future expansion.
This is a class diagram of an Android application designed based on the MVVM (Model-View-ViewModel) architecture. It is structured as follows:
-
View
- MainActivity: App's entry point
- AppNavigation: Manages navigation between screens using navigation component
- Screens (HomeScreen, SearchResultScreen, etc.): Composable functions that compose each UI screen. These screens are updated based on ui state changes
-
ViewModel
- LocalViewModel: Manages local data; original user images
- AuthViewModel: Manages authentication state
- SearchViewModel: Manages search results
- ImageDetailViewModel: Manages detailed info of images
- PhotoViewModel: Manages photos to upload
- HomeViewModel: Managees home screen
- AlbumViewModel: Manages tag albums
- AddTagViewModel: Manages tag creation with recommendation
- SelectImageViewModel: Manages image selecting
- StoryViewModel: Manages story(moment) feature
-
Model
- Repository (LocalRepository, SearchRepository, etc.): Abstracts the data source and separates it from the ViewModel
- Data Source: Implements APIService using Retrofit as HTTP client and communicates with the backend
- Data Class (Tag, Photo, etc.): Representation of data
The diagrams above represent the models and APIs of the gallery and search apps, which serve as the core components of the backend. The backend consists of the User, Tags, Captions, RepVec, and Photos databases, along with Photo_Tag and Photo_Caption tables that connect these entities.
The backend is organized into two main applications:
the gallery app, which provides methods to add, delete, and modify data used for searching, and
the search app, which handles the actual search functionality.
The search app supports personalized tag-based search, understanding the semantic meaning of the user-created tags and using them to perform image searches tailored to each user.
The gallery app allows users to upload their photos, thereby adding them to the searchable dataset, or remove photos from search results. Users can also add or remove tags associated with each photo.
When a user uploads a photo, the system generates a caption for it using an LLM, storing descriptive information that helps analyze the photo’s content.
To enhance user convenience, the system can recommend tag names for photos or suggest relevant photos for existing tags.
Finally, to support efficient search, the backend processes tag and caption data to create, update, or delete auxiliary structures such as graphs and representative vectors—the latter serving as embedding vectors that represent each tag.
MomenTag stores the User, Tags, Photo_Tag, Captions, and Photo_Caption tables in MySQL as an RDB, while the Photos and RepVec collections are stored in Qdrant DB.
The User table contains the information (username, password, and email) for each user. id is the primary key. The Tags table stores semantic labels, enabling flexible search and association with photos. The Photo_Tag table serves as a junction entity, allowing many-to-many relationships between photos and tags, which captures the rich, dynamic connections between visual content and its conceptual meanings. The Captions table stores captions for photos, which are generated by an LLM and split word by word. The Photo_Caption table serves as a junction entity, allowing many-to-many relationships between photos and tags, which captures the rich, dynamic connections between visual content and its conceptual meanings.
Additionally, metadata related to each image is stored in the vector DB along with the image's embedding. photo_id is the primary key, and id is a foreign key that indicates who posted the photo. photo_path_id is the photo ID for using the MediaStore URL, which allows for the reconstruction of the URL to access and load the photo via the MediaStore API. The embedding field stores the photo’s semantic representation vector, while created_at, location_lat, and location_lng are metadata for the photo.
Finally, the RepVec collection stores representative vectors for each tag. For each tag, representative vectors are calculated based on the average value of vectors within
Since two different databases are used, MomenTag has to take extra care for maintaining data integrity. However, by combining vector DB and traditional RDB, MomenTag achieves the best of both worlds: efficient semantic retrieval through vector similarity search and reliable relational integrity for user and metadata management.
- Who
- Tests for the existing code are conducted by one coder
- Tests for future code will be conducted by each coder
- When
- At every PR
- Unit Test: JUnit
- Integration Test: JUnit + Compose UI Test
- Architecture: MVVM
- Model: test coverage for all business logic
- ViewModel: test coverage for state and data handling
The Android application follows the MVVM architecture. Therefore the logic is concentrated in the Repository and ViewModel layers. For unit testing, we employ JUnit to validate core logic and synchronization between the local and remote repositories. Mock objects and MockWebServer are used for simulating network responses without depending on actual backend communication.
Since we use Jetpack Compose in our project, and composable functions are declarative without business logic, they are not a direct subject of unit test. Instead, integration tests using JUnit and Compose UI Test are performed to ensure that user interactions trigger the expected ViewModel updates and UI state changes correctly. The Compose UI Test enables simulation of user activities such as pressing the button and navigating through pages.
- Results {photo}
- Unit Test: unittest + DRF APITestCase
- Integration Test: DRF APIClient
For backend testing, we use Python’s unittest framework and Django REST Framework’s APITestCase to perform unit tests on models, serializers, and views. For integration test, we use the DRF APIClient to simulate API requests and responses. These tests simulates interactions between models, serializers, and views.
By combining these tests, we ensure data consistency between our relational database and Qdrant, and verify that both systems work properly under realistic client actions.
- Results
frontend
backend
These are the five user stories for user acceptance testing.
As an user with a large photo collection,
I can search photos using natural language,
so that I can quickly find the images I want.
As a user who has photos with personalized tags,
I can search photos based on my personalized tags,
so that I can easily perform searches related to my personalized tags.
As an user who wants to add tags to my photos,
I can receive recommendations of similar images even if I tag only a few photos,
so that I can easily make tags with many related photos.
As an user enjoying room escape,
I can accept automatically generated tag "room escape" for my new escape room photos,
so that I can add tag to images effortlessly.
As a photo-heavy user,
I can skim through recommended images,
so that I can find forgotten photos and add tags to them.