03 - Domain Models
Overview
Wavic's domain model represents a hierarchical music production management system. The core entities form a tree structure: Artists → Projects → Tracks. Entities use a tiered URL identifier system (shortId/slug) for clean URLs — see API-DESIGN.md.
Entity Hierarchy
┌─────────────────────────────────────────────────────────────┐
│ USER (IUser) │
│ - Owns multiple artist spaces │
│ - Has subscription with storage limits │
└─────────────────────┬───────────────────────────────────────┘
│ owns
▼
┌─────────────────────────────────────────────────────────────┐
│ ARTIST SPACE (TArtist) │
│ - Top-level container (artist/band) │
│ - Contains multiple projects │
│ - Has collaborators with roles │
└─────────────────────┬───────────────────────────────────────┘
│ contains
▼
┌─────────────────────────────────────────────────────────────┐
│ PROJECT (TProject) │
│ - Album, EP, Single, etc. │
│ - Contains multiple tracks │
│ - Inherits collaborators from artist │
└─────────────────────┬───────────────────────────────────────┘
│ contains
▼
┌─────────────────────────────────────────────────────────────┐
│ TRACK (TTrack) │
│ - Individual song/recording │
│ - Has audio versions (stems, mixes) │
│ - Has attachments (files, documents) │
│ - Has comments on waveform │
└─────────────────────────────────────────────────────────────┘Core Types
User (IUser)
Represents an authenticated user with subscription information.
typescript
// types/user.ts
export interface IUser {
_id: string;
fullName: string;
username: string;
email: string;
image: string;
isCreatedByInvitation: boolean;
spaceUsed: number;
recentSearches: string[];
subscription: {
name: string; // Plan name (Free, Pro, etc.)
storageMB: number; // Total storage limit
seats: number; // Team seat limit
occupiedStorageMB: number; // Used storage
occupiedSeats: number; // Used seats
isActive: boolean; // Subscription active
periodStartsAt?: Date;
periodEndsAt?: Date;
stripeSubscriptionId?: string;
stripeCustomerId?: string;
stripeProductId?: string;
isFreeTrial?: boolean;
hasBeenFreeTrial?: boolean;
};
}Artist Space (TArtist)
Top-level container representing an artist or band's workspace.
typescript
// types/artist.ts
export type TArtist = {
_id: string;
shortId?: string; // Auto-generated 6-char identifier (nanoid)
slug?: string; // User-customizable URL slug (premium)
slugType?: 'auto' | 'premium';
name: string; // Artist/band name
type: string; // Type identifier
image: string; // Profile image URL
collaborators: TCollaborator[]; // Team members
owner: string; // Owner user ID
sharedLinkRole?: string; // Default role for shared links
lastAccessedAt?: Date; // Last access timestamp
};Project (TProject)
Container for related tracks (album, EP, single).
typescript
// types/artist.ts
export interface IProject<TArtist, TOwner> {
_id: string;
shortId?: string; // Auto-generated 6-char identifier
slug?: string; // User-customizable URL slug
slugType?: 'auto' | 'premium';
name: string; // Project name
artistSpace: TArtist; // Parent artist space
type: string; // Single, EP, Album, etc.
image: string; // Cover image URL
collaborators: TCollaborator[]; // Project-level team
owner: TOwner; // Owner (user or ID)
sharedLinkRole?: string; // Sharing permission
lastAccessedAt?: Date;
}
// Simplified version with string references
export type TProject = {
_id: string;
shortId?: string;
slug?: string;
slugType?: 'auto' | 'premium';
name: string;
artistSpace: TArtist; // Populated artist
type: string;
image: string;
collaborators: TCollaborator[];
owner: string; // Owner user ID
sharedLinkRole?: string;
lastAccessedAt?: Date;
};Track (TTrack)
Individual song with audio versions, metadata, and collaboration features.
typescript
// types/track.ts
export type TTrack = {
id?: string; // Normalized ID
_id?: string; // MongoDB ID
shortId?: string; // Auto-generated 6-char identifier
slug?: string; // User-customizable URL slug
slugType?: 'auto' | 'premium';
name: string; // Track title
isFinalName: boolean; // Name finalized
finalNameUpdatedAt: string;
// Relations
artistSpace: TArtist; // Parent artist
project: TProject; // Parent project
collaborators: TCollaborator[]; // Track collaborators
owner: IUser; // Track owner
// Audio
audio: TFile[]; // Audio versions (mixes, stems)
audioFinal: string | null; // Final version URL
audioUpdatedAt: string;
// Artwork
image: TFile[]; // Cover images
imageFinal: string | null; // Final image URL
imageUpdatedAt: string;
// Metadata
type: string; // Track type
status: string; // Production status
order?: number; // Display order
releaseDate?: Date;
genre?: string;
mainArtists?: string;
label?: string;
ISRC?: string; // International Standard Recording Code
UPC?: string; // Universal Product Code
notes?: string;
// Additional data
credits: any; // Production credits
attachments: any; // Attached files
sharedLinkRole?: string;
lastAccessedAt?: Date;
};File (TFile)
Represents uploaded files (audio, images, attachments).
typescript
// types/track.ts
export type TFile = {
_id?: string;
version?: number; // Version number
s3Url: string; // AWS S3 URL
type: string; // File type
owner?: string; // Uploader user ID
spaceOwner?: string; // Space owner ID
project?: string; // Parent project ID
track?: string; // Parent track ID
artistSpace?: string; // Parent artist ID
name?: string; // Original filename
size?: number; // File size in bytes
contentType?: string; // MIME type
isFinalVersion?: boolean; // Marked as final
comments: any[]; // Comments on this version
createdAt?: string;
updatedAt?: string;
};Collaborator (TCollaborator)
Team member with role-based permissions.
typescript
// types/artist.ts
export type TCollaborator = {
_id: string;
userId: IUser; // User details
role: keyof typeof ROLES; // Permission level
};
// config/constants.ts
export const ROLES = {
Owner: 'owner',
Admin: 'admin',
Editor: 'editor',
Viewer: 'viewer',
} as const;Enums and Constants
Track Status
Production workflow stages:
typescript
// types/status.ts
export enum TrackStatus {
NOT_STARTED = 'Not started',
PRE_PRODUCTION = 'Pre-production',
RECORDING = 'Recording',
EDITING = 'Editing',
MIXING = 'Mixing',
MASTERING = 'Mastering',
COMPLETED = 'Completed',
}Layout Options
UI layout variants:
typescript
// config/enums.ts
export enum LAYOUT_OPTIONS {
HYDROGEN = 'hydrogen',
HELIUM = 'helium',
LITHIUM = 'lithium',
BERYLLIUM = 'beryllium', // Default
BORON = 'boron',
CARBON = 'carbon',
}Project Types
typescript
// Common project types used:
type ProjectType = 'Single' | 'EP' | 'Album' | 'Mixtape' | 'Compilation';Status Options
Track status with visual styling:
typescript
// config/constants.ts
export const TRACK_STATUS_OPTIONS: StatusOption[] = [
{ label: 'Not started', value: 'Not started', bgColor: 'bg-gray-300' },
{
label: 'Pre-production',
value: 'Pre-production',
bgColor: 'bg-yellow-600',
},
{ label: 'Recording', value: 'Recording', bgColor: 'bg-purple-800' },
{ label: 'Editing', value: 'Editing', bgColor: 'bg-blue-700' },
{ label: 'Mixing', value: 'Mixing', bgColor: 'bg-yellow-800' },
{ label: 'Mastering', value: 'Mastering', bgColor: 'bg-pink-500' },
{ label: 'Completed', value: 'Completed', bgColor: 'bg-green-800' },
];Role Permissions
┌─────────────────────────────────────────────────────────────┐
│ ROLE PERMISSIONS │
├─────────────┬─────────┬─────────┬─────────┬────────────────┤
│ Action │ Owner │ Admin │ Editor │ Viewer │
├─────────────┼─────────┼─────────┼─────────┼────────────────┤
│ View │ ✅ │ ✅ │ ✅ │ ✅ │
│ Edit │ ✅ │ ✅ │ ✅ │ ❌ │
│ Upload │ ✅ │ ✅ │ ✅ │ ❌ │
│ Delete │ ✅ │ ✅ │ ❌ │ ❌ │
│ Share │ ✅ │ ✅ │ ❌ │ ❌ │
│ Manage Team │ ✅ │ ✅ │ ❌ │ ❌ │
│ Transfer │ ✅ │ ❌ │ ❌ │ ❌ │
└─────────────┴─────────┴─────────┴─────────┴────────────────┘Waveform Data Types
Used by the audio player and waveform visualization:
typescript
// src/app/shared/wave/data/wave-data.ts
export interface IComment {
_id: string;
text: string;
time: number; // Position in seconds
user: IUser;
status: string;
createdAt: string;
}
export interface IVersion {
label: string; // Display name
value: string; // Version ID
name: string; // File name
}
export interface IWaveFormState {
currentTime: number; // Current playback position
totalTime: number; // Total duration
}
export interface VolumeState {
volume: number; // Current volume (0-100)
lastVolume: number; // Volume before mute
}Search Types
typescript
// types/search-items.ts
export interface SearchItem {
_id: string;
name: string;
type: 'artist' | 'project' | 'track';
image?: string;
artistSpace?: string;
project?: string;
}Notification Types
typescript
// types/notification.interface.ts
export interface INotification {
_id: string;
type: string;
message: string;
read: boolean;
createdAt: string;
data?: any;
}