Developer guide
Recommendation¶
src/Recommendation/ generates music recommendations using multiple algorithms and stores them for retrieval. It does not own the entities it recommends (artists, genres) — those live in Catalog. This context is purely about computing, persisting, and serving recommendation scores.
Domain Models¶
Aggregate Root: Recommendation¶
| Property |
Description |
score |
Numeric relevance score from the source algorithm |
sourceType |
Type of entity that generated the recommendation |
sourceId |
ID of the source entity |
targetUser |
User who receives the recommendation |
recommendationType |
Which algorithm produced this recommendation |
Value Objects¶
| Type |
Kind |
Description |
RecommendationType |
Enum |
Classifies the originating algorithm (collaborative, content-based, genre, database relation) |
Domain Services¶
Each domain service implements one recommendation algorithm:
| Service |
Algorithm |
Description |
CollaborativeFilteringCalculator |
User-based collaborative filtering |
Finds users with similar taste and recommends what they liked |
ContentSimilarityCalculator |
Content-based similarity |
Matches entities with overlapping attributes |
DatabaseRelationCalculator |
Relationship-based |
Uses existing database relations (e.g., same artist, same genre) |
GenreSimilarityCalculator |
Genre overlap scoring |
Scores entities by shared genre membership |
Commands & Handlers¶
| Command |
Handler |
Purpose |
SaveRecommendationCommand |
SaveRecommendationHandler |
Persist a single computed recommendation |
GenerateRecommendationsCommand |
GenerateRecommendationsHandler |
Run recommendation generation (dispatched by the console command, the admin action and the scheduler) |
DeleteRecommendationCommand |
DeleteRecommendationHandler |
Remove one recommendation by UUID |
DeleteRecommendationsBySourceCommand |
DeleteRecommendationsBySourceHandler |
Remove all recommendations originating from a source entity |
Scheduled Generation¶
GenerateRecommendationsCommand implements the Scheduler's SchedulableCommandInterface, with the scheduler parameters mode (full or incremental) and automatic. The initial migration seeds an active scheduled job, Generate recommendations, that dispatches it daily at 04:00 UTC with {"mode":"incremental","automatic":true}.
The handler reads the system setting recommendations.auto_generate (off by default, contributed by RecommendationSettingDefinitions) when a command with automatic: true arrives. While the setting is off, the handler logs at info level and returns skipped: recommendations.auto_generate is off, which the scheduler records as the job's last result. Commands without automatic, from app:recommendation:generate, the admin action or a schedule an administrator creates, always generate. An unknown mode throws instead of falling back to incremental.
The command has no transport route, so the handler runs in the dispatching process: the scheduler worker for scheduled runs. There the CPU process pool is not running, and the handler generates synchronously. An incremental run loads the songs updated in the last seven days; a full run loads every song. Both load every user's listening history, so a full run on a large library can exhaust the scheduler worker's memory or time, which is why the seeded job is incremental.
Queries & Query Handlers¶
| Query |
Purpose |
GetRecommendationQuery |
Fetch a single recommendation by UUID |
GetRecommendationsForUserQuery |
All recommendations for the authenticated user |
GetRecommendationsBySourceQuery |
Recommendations generated by a specific source entity |
GetTargetingRecommendationsQuery |
Recommendations targeting a specific entity type and ID |
Ports¶
| Port |
Purpose |
RecommendationInsightsPortInterface |
Read access to recommendation insights |
RecommendationJobPortInterface |
Recommendation job lifecycle operations |
API Endpoints¶
All endpoints live under RecommendationController with the /api/recommendations prefix.
| Method |
Path |
Purpose |
| GET |
/api/recommendations |
List recommendations for the current user (cursor-paginated) |
| GET |
/api/recommendations/source/{sourceType}/{sourceId} |
Recommendations by source entity |
| GET |
/api/recommendations/targeting/{targetType}/{targetId} |
Recommendations targeting a specific entity |
| POST |
/api/recommendations |
Create a recommendation |
| DELETE |
/api/recommendations/{uuid} |
Delete a single recommendation |
| DELETE |
/api/recommendations/source/{sourceType}/{sourceId} |
Delete all recommendations for a source |
Cross-Context Relationships¶
| Direction |
Context |
Details |
| Depends on |
Shared |
Uuid, PublicId |
| Depends on |
Catalog |
Artist and genre references (read-only) |
| Depends on |
Scheduler |
GenerateRecommendationsCommand is schedulable through the Scheduler Schedulable Contract Deptrac layer |
| Depends on |
Shared |
SystemSettingsPortInterface for recommendations.auto_generate |
| Depended on by |
— |
No contexts depend on Recommendation directly |
Infrastructure¶
| Component |
Type |
Purpose |
RecommendationEntity |
Doctrine entity |
ORM entity mapped to the recommendations table |
RecommendationRepository |
Doctrine repository |
Implements the domain repository interface |