Specification — Photo Storage and Media Attachments in Poolia
1. Objective
Reuse a single photo infrastructure across multiple business contexts in Poolia, including:
- user profile photos;
- visit photos;
- pool photos;
- message photos.
The design is based on Azure Blob Storage for binary files and MongoDB for metadata, avoiding binary embedding inside domain documents.
2. Functional Approach
Photos are modeled through a shared Photo document plus context-specific upload flows.
The backend stores:
- file metadata in MongoDB;
- binary content in Azure Blob Storage;
- public or signed read URLs generated by the blob service.
A photo can be linked to several different entity types through optional references.
3. Data Design
3.1. Photo
The photos collection stores the metadata.
Main fields
fileNameblobPathcontentTypesizeuserIdvisitIdpoolIdthreadIdmessageIduploadedBycreatedAtdescription
Multi-context support
A photo can belong to:
- a user profile;
- a visit;
- a pool;
- a message inside a thread.
This allows the same infrastructure to support profile media, operational evidence, and messaging attachments.
3.2. Blob storage paths
The implementation uses context-specific blob paths.
User profile photo
users/{userId}/{timestamp}_{filename}
Visit photo
visits/{visitId}/{timestamp}_{filename}
Pool photo
pools/{poolId}/{timestamp}_{filename}
Message photo
messages/{threadId}/{messageId}/{timestamp}_{filename}
This keeps stored files logically separated and easy to trace.
4. Business Rules
File validation
Uploads fail if the incoming file has no filename.
Entity existence validation
Uploads validate that the related parent entity exists before writing the file.
Single-photo behavior for profiles and pools
The current implementation replaces the previous photo for:
- user profile photo;
- pool photo
That means only one profile photo per user and one main photo per pool are kept by design.
Multi-photo behavior for visits and messages
Visit and message uploads do not replace existing photos. They append new photo records.
Delete behavior
Deleting a photo attempts to delete the blob object first and then removes metadata from MongoDB.
5. REST Endpoints
5.1. Upload user profile photo
POST /photos/users/{user_id}/profile
Body
multipart/form-data
Fields
fileuploadedBy(optional)description(optional)
Behavior
- validates user existence;
- replaces an existing profile photo if present;
- uploads the new file to Azure Blob;
- stores metadata in
photos; - returns a
PhotoResponse.
5.2. Get user profile photo
GET /photos/users/{user_id}/profile
Returns the current user profile photo metadata and URL.
5.3. Upload visit photo
POST /photos/visits/{visit_id}
Behavior
- validates visit existence;
- uploads the file;
- stores metadata linked to the visit;
- returns a
PhotoResponse.
5.4. List visit photos
GET /photos/visits/{visit_id}
Returns all photos linked to the visit.
5.5. Upload pool photo
POST /photos/pools/{pool_id}
Behavior
- validates pool existence;
- replaces an existing pool photo if present;
- uploads the file;
- stores metadata linked to the pool.
5.6. Get pool photo
GET /photos/pools/{pool_id}
Returns the current pool photo.
5.7. Delete photo
DELETE /photos/{photo_id}
Deletes blob content and metadata.
6. Message Photo Reuse
Although message-photo upload is exposed through the messaging router rather than the general photos router, the implementation reuses the same Photo entity and blob infrastructure.
The message-photo flow:
- validates thread existence;
- validates message existence;
- validates message-to-thread relationship;
- uploads to a
messages/...blob path; - stores
poolId,threadId, andmessageIdin thePhotodocument.
This means the media system is shared consistently across both operational and conversational domains.
7. Response Format
Photo endpoints return a PhotoResponse that includes:
- metadata fields;
- related entity ids;
description;- generated
urlfor reading the blob.
Example shape
{
"id": "photo-id",
"fileName": "pool-state.jpg",
"blobPath": "visits/visit-id/1700000000_pool-state.jpg",
"contentType": "image/jpeg",
"size": 245881,
"visitId": "visit-id",
"uploadedBy": "user-id",
"createdAt": "2026-04-03T18:11:00Z",
"description": "After cleaning",
"url": "https://..."
}
8. Backend Logic Summary
Upload flow
- validate parent entity;
- validate filename;
- compute blob path;
- calculate file size;
- optionally remove previous single-photo record;
- upload blob content;
- insert
Photometadata; - return
PhotoResponse.
Read flow
- validate related entity if required by the service;
- load photo metadata;
- map to
PhotoResponsewith generated URL.
Delete flow
- load photo metadata;
- attempt blob deletion;
- remove MongoDB document.
9. Frontend Integration Notes
Profile flow
Frontend should treat profile photo upload as replace-in-place behavior.
Pool flow
Frontend should also treat pool photo as a single main image.
Visit flow
Visit photo upload supports evidence-style accumulation, so clients can allow multiple images.
Message flow
Message photo uploads should follow the messaging two-step flow:
- create message;
- upload one or more photos to that message.
10. UX Considerations
Progressive feedback
Uploads should surface clear states such as:
- uploading file;
- upload complete;
- failed upload.
Replacement-aware UI
For user and pool photos, frontend should present uploads as photo replacement, not as gallery append behavior.
11. Current Scope
The current implementation covers:
- Azure Blob upload and delete;
- metadata persistence in MongoDB;
- user, visit, pool, and message photo support;
- generated read URL responses.
It does not currently cover:
- image transformations;
- thumbnails generated by backend;
- file-type restrictions beyond what upstream clients enforce;
- upload authorization rules at the route layer.
12. Future Recommendations
A future version could add:
- file validation by MIME type and size;
- generated thumbnails;
- photo galleries for pools;
- signed URL expiration policies if needed;
- explicit permission checks around upload and delete operations.
13. Final Summary
Photo handling in Poolia is implemented as a shared media subsystem based on Azure Blob Storage and a reusable Photo metadata document. The design keeps file storage centralized, supports several business contexts, and stays compatible with the rest of the REST-based backend.