Skip to content

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

  • fileName
  • blobPath
  • contentType
  • size
  • userId
  • visitId
  • poolId
  • threadId
  • messageId
  • uploadedBy
  • createdAt
  • description

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

  • file
  • uploadedBy (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, and messageId in the Photo document.

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 url for 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

  1. validate parent entity;
  2. validate filename;
  3. compute blob path;
  4. calculate file size;
  5. optionally remove previous single-photo record;
  6. upload blob content;
  7. insert Photo metadata;
  8. return PhotoResponse.

Read flow

  1. validate related entity if required by the service;
  2. load photo metadata;
  3. map to PhotoResponse with generated URL.

Delete flow

  1. load photo metadata;
  2. attempt blob deletion;
  3. 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:

  1. create message;
  2. 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.