DEV Community

Cover image for Modelling a Music Catalogue Without Regretting It Later
James Sanderson
James Sanderson

Posted on

Modelling a Music Catalogue Without Regretting It Later

Music streaming app on a phone next to headphones

If you are building anything that plays licensed music, the schema you choose in week two determines whether accurate royalty attribution is even possible. Not difficult — possible. You cannot report a split you have nowhere to store.

This is a walkthrough of what breaks in the obvious model and what to do instead.

The model everyone starts with

tracks(id, title, artist_name, album_name, duration, audio_url, isrc)
Enter fullscreen mode Exit fullscreen mode

It is clean, it demos beautifully, and it fails on contact with a real catalogue. Here is the sequence, which happens in roughly this order every time.

Week 3. A track has three writers, one performer and a producer, all with different rights and different splits. There is one artist_name column. You add artists as a many-to-many and feel clever.

Week 5. The same recording arrives from a second distributor with slightly different metadata and a different local identifier. You now have two rows for one recording, and any play count you report is split across both.

Week 7. A compilation arrives. The recording already exists on its original album. Your model conflates recording with release, so you either duplicate the recording or lose the compilation context. Both are wrong.

Week 9. Explicit and clean versions. Different audio, same composition, sometimes the same ISRC in practice because someone was careless upstream.

Week 12. Territory restrictions differ per release, per recording and per agreement. There is no column for it, and geo-enforcement is now a client-side check somebody can bypass.

Month 6. A rights split is corrected and backdated. You have stored aggregates. You cannot recompute.

The model that survives

Separate the concepts that the industry separates. The vocabulary exists for a reason.

  • Work — the underlying composition. Writers and publishers attach here.
  • Recording — a specific performance of a work. Performers, producers and the master rights holder attach here. This is what actually gets streamed.
  • Release — a package containing recordings: album, single, compilation. Territory and availability windows attach here, not to the recording.
  • Contributor — a party with a role (writer, performer, producer, publisher, label) and a time-bounded rights share on a work or a recording.
  • Identifier — an external id (isrc, iswc, distributor-local) attached to a work or recording, with the source recorded, many-to-one.

Three properties fall out of this that you cannot get from the flat model:

  1. Deduplication becomes possible. Two deliveries of the same recording resolve to one recording entity with two identifier rows, rather than two competing rows.
  2. Attribution becomes expressible. A split is a relationship with a validity window, not a column that gets overwritten.
  3. Territory lives at the right level. Availability is a property of a release in a market, which is how the agreements are actually written.

Studio mixing console

Splits must be time-bounded

This is the single most commonly missed detail, and it is what makes retroactive corrections survivable.

Do not store a contributor share as a current value. Store it as a record with valid_from and valid_to. When a split is corrected and backdated, you insert a new period rather than overwriting anything, and the historical calculation for any past month remains reproducible from the data as it applied then.

If you overwrite, you have silently destroyed your ability to explain a past statement. That conversation with a rights holder does not go well.

Events, not aggregates

The corollary on the reporting side: plays are immutable events, and every aggregate is a derived artefact that can be rebuilt from them.

play_events(id, recording_id, user_id, started_at, ms_played,
            qualified, client, country, session_id, ...)
Enter fullscreen mode Exit fullscreen mode

Store enough to apply whatever definition of a qualifying stream your agreements specify — including one you might renegotiate later — and enough for fraud detection to distinguish genuine listening from synthetic patterns. You cannot reconstruct signal you never captured.

Then every monthly statement is a function over events plus the rights model as it stood, which means:

  • A backdated split correction is a recomputation, not an archaeology project
  • Fraud identified after payout can be excluded and the period restated
  • A disputed statement can be reproduced line by line

Teams that store aggregates and drop raw events save a little storage and lose the ability to answer the only questions that ever really matter.

Practical notes

  • Do not trust ISRCs to be unique. They are supposed to be. In real catalogues they are not. Treat them as one identifier among several, with a source attached.
  • Model unknown contributors explicitly. Deliveries arrive with missing credits constantly. A null is not the same as "we know there is a writer we cannot identify", and only one of those can be resolved later.
  • Keep qualified as a derived, recomputable flag, not something written at ingest. The definition changes when agreements change.
  • Version the rights model itself. Not just the splits — the rules for applying them.

Why this is worth the upfront cost

Remodelling a catalogue after launch means migrating every playlist, every recommendation index and every historical royalty record simultaneously, while continuing to report accurately throughout. It is one of the more painful migrations available in consumer software, and it is entirely avoidable by spending a fortnight on the model before writing the player.

When you evaluate a build partner for this kind of product, ask to see their catalogue model and ask what happens when two distributors deliver the same recording with conflicting credits. The answer tells you whether they have ingested a real catalogue or a test fixture.

Full guide, including build budgets, discovery cost economics and the questions to ask on a shortlisting call: Music Streaming App Development Company. Related: how we approach custom software development for systems like this.

Frequently Asked Questions

Is a relational database the right choice for a music catalogue?

Generally yes for the catalogue and rights model, where relationships and constraints matter and correctness beats flexibility. Play events are usually better served by an append-only store or event log, with aggregates materialised into whatever your reporting layer queries.

How do I dedupe recordings arriving from multiple distributors?

Match on identifiers first, then fall back to fuzzy matching on title, contributors and duration, with a human review queue for anything below a confidence threshold. Store every source identifier rather than picking a winner, so a bad merge is reversible.

Should territory restrictions live on the recording or the release?

On the release, in almost all cases, because that is how agreements are written. A recording can be available in one market on one release and unavailable there on another.

What is the minimum I should capture in a play event?

Recording, user, start time, duration played, client, country, session, and enough context to apply a qualifying-stream rule you might change later. Fraud detection needs pattern signal too, so do not sample aggressively.

How do I handle a split correction backdated a year?

Insert a new time-bounded split period rather than updating the existing one, then recompute affected statements from play events. This only works if you never overwrote history and never discarded the events.

Do I need this level of rigour for a small niche platform?

The catalogue and rights model, yes, because the cost of getting it wrong scales with how long you run rather than how large you are. Discovery sophistication and platform tooling can absolutely wait.

Top comments (0)