Photoflow Service
Overview
The Photoflow Service is the core service of the PhotoFlow platform, responsible for managing albums, photos, feedback, and the workflow state machine.
Responsibilities
Album lifecycle management (CRUD operations)
Album state machine execution
Photo processing and synchronization
Client feedback collection
Photo voting and selection
Real-time notifications via SignalR
Project Structure
Photoflow/
├── Photoflow.API/
│ ├── Controllers/
│ │ ├── AlbumController.cs
│ │ ├── AlbumTagController.cs
│ │ ├── PhotoController.cs
│ │ ├── PhotoCommentController.cs
│ │ ├── PhotoLikeController.cs
│ │ ├── FeedbackController.cs
│ │ └── PublicController.cs
│ └── Program.cs
├── Photoflow.Application/
│ ├── UseCases/
│ │ ├── Albums/
│ │ ├── AlbumTags/
│ │ ├── Photos/
│ │ └── Feedback/
│ ├── Events/
│ │ ├── Sync/
│ │ └── Handlers/
│ └── Services/
├── Photoflow.Domain/
│ ├── Entities/
│ │ ├── AlbumEntity.cs
│ │ ├── AlbumTagEntity.cs
│ │ ├── PhotoProcEntity.cs
│ │ ├── PhotoComment.cs
│ │ ├── PhotoLikeEntity.cs
│ │ └── PublicUrlEntity.cs
│ ├── StateMachine/
│ │ └── AlbumStateMachineDefinition.cs
│ ├── Common/
│ │ └── Enums/
│ │ ├── States/AlbumState.cs
│ │ ├── Triggers/AlbumTrigger.cs
│ │ ├── AlbumTagType.cs
│ │ └── EnumAlbumTagStatus.cs
│ └── Repositories/
└── Photoflow.Infrastructure/
├── Persistence/
│ ├── PhotoflowDbContext.cs
│ └── Repositories/
└── Services/
Domain Entities
AlbumEntity
The root aggregate for album management:
public sealed class AlbumEntity : BaseEntity<Guid>, IMultiTenantEntity, IAggregateRoot
{
public string Name { get; private set; }
public AlbumStatus Status { get; set; }
public AlbumState AlbumState { get; set; }
public Guid OrgId { get; set; }
public Guid ProviderId { get; private set; }
// Relationships
public ICollection<AlbumTagEntity> AlbumTags { get; set; }
public ICollection<PhotoProcEntity> PhotoProcs { get; set; }
// Computed properties
public int TotalPhotos => GetPhotosFromCurrentStateAlbumTag()?.Count ?? 0;
public int SelectedPhotos => GetPhotosFromCurrentStateAlbumTag()?.Count(p => p.Voted) ?? 0;
}
AlbumTagEntity
Represents a stage in the album workflow:
public class AlbumTagEntity : BaseEntity<Guid>
{
public Guid AlbumId { get; private set; }
public AlbumTagType Type { get; private set; }
public EnumAlbumTagStatus Status { get; private set; }
public AlbumTagLink? Link { get; private set; }
public Guid ProviderId { get; private set; }
public Guid? FolderId { get; private set; }
}
PhotoProcEntity
Individual photo within an album tag:
public class PhotoProcEntity : BaseEntity<Guid>
{
public Guid AlbumTagId { get; private set; }
public string FileName { get; private set; }
public string ExternalId { get; private set; }
public PhotoStatus Status { get; private set; }
public bool Voted { get; private set; }
public int SortOrder { get; private set; }
}
Use Cases
Album Use Cases
Use Case | Description |
|---|
CreateAlbumUseCase
| Create a new album |
UpdateAlbumUseCase
| Update album details |
DeleteAlbumUseCase
| Soft delete an album |
GetAlbumDetailUseCase
| Get album with all related data |
GetAlbumsUseCase
| List albums with filtering |
TriggerAlbumStateUseCase
| Execute state machine trigger |
GetAlbumTransitionsUseCase
| Get available transitions |
Photo Use Cases
Use Case | Description |
|---|
GetPhotosUseCase
| List photos in an album tag |
VotePhotoUseCase
| Mark photo as voted/selected |
GetPhotoDetailUseCase
| Get photo details |
Feedback Use Cases
Use Case | Description |
|---|
CreateFeedbackUseCase
| Add feedback comment to photo |
GetFeedbackCountUseCase
| Get count of photos with feedback |
Event Handlers
Sync Events
Event | Handler | Action |
|---|
FullSyncStorageEvent
| SyncPhotosHandler
| Sync photos from Google Drive |
StorageFolderSyncProgressEvent
| StorageFolderSyncProgressEventHandler
| Update sync progress |
StorageFolderSyncCompletedEvent
| StorageFolderSyncCompletedEventHandler
| Complete photo sync |
Domain Events
Event | Handler | Action |
|---|
AlbumStateMachineTriggeredEvent
| AlbumStateMachineTriggeredEventHandler
| Update album state |
PhotoVotedEvent
| PhotoVotedEventHandler
| Handle photo voting |
AlbumTagCreatedEvent
| AlbumTagCreatedEventHandler
| Send SignalR notification |
AlbumTagStatusChangedEvent
| AlbumTagStatusChangedEventHandler
| Send SignalR notification |
API Controllers
AlbumController
# List albums
GET /api/v1/albums
# Get album detail
GET /api/v1/albums/{id}
# Create album
POST /api/v1/albums
# Update album
PUT /api/v1/albums/{id}
# Delete album
DELETE /api/v1/albums/{id}
# Get available transitions
GET /api/v1/albums/{id}/transitions
# Trigger state change
PUT /api/v1/albums/{id}/state
AlbumTagController
# List album tags
GET /api/v1/albums/{albumId}/tags
# Get album tag detail
GET /api/v1/albums/{albumId}/tags/{tagId}
# Update album tag URL
PUT /api/v1/albums/{albumId}/tags/{tagId}/url
PhotoController
# List photos in album tag
GET /api/v1/albums/{albumId}/tags/{tagId}/photos
# Get photo detail
GET /api/v1/albums/{albumId}/photos/{photoId}
# Vote photo
POST /api/v1/albums/{albumId}/photos/{photoId}/vote
PublicController
Anonymous endpoints for client access:
# Get public album by URL
GET /api/v1/public/albums/{publicUrl}
# Get public photos
GET /api/v1/public/albums/{publicUrl}/photos
# Submit feedback
POST /api/v1/public/albums/{publicUrl}/feedback
Database Schema
Tables
Table | Description |
|---|
Albums
| Album master data |
AlbumTags
| Album workflow stages |
PhotoProcs
| Photo records |
PhotoComments
| Photo feedback comments |
PhotoLikes
| Photo likes/votes |
PublicUrls
| Public access URLs |
FailureRecords
| Rollback failure tracking |
Key Relationships
Albums (1) ─── (N) AlbumTags
Albums (1) ─── (N) PhotoProcs
AlbumTags (1) ─── (N) PhotoProcs
PhotoProcs (1) ─── (N) PhotoComments
PhotoProcs (1) ─── (N) PhotoLikes
AlbumTags (1) ─── (0..1) PublicUrls
Configuration
appsettings.json
{
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Database=pfl-dev;..."
},
"Photoflow": {
"SyncBatchSize": 50,
"MaxRetryAttempts": 3,
"RetryDelaySeconds": 5
}
}
Last modified: 25 February 2026