Spring Data Redis - mapping object relationships?
There's nothing really wrong with your modelling approach. If you feel confident with the modelling you are good to go.

Data modelling in Redis depends on the functional and non-function requirements you need to meet. In Redis and your case this boils down to whether read performance is more important than write performance, and whether performance is an issue at all.

In my opinion (and after having developed quite a few commercial-grade products based on Redis), Redis has not been made with object relationships in mind. At its core it is just a very fast, efficient and lightweight key-value based database, i.e. you lookup a value by its (complex) key. That's it. Simple, plain and wonderful.

With this said, I would strongly recommend you to keep data models as simple as possible, when working with Redis. In classic database scheme design your are encouraged to perform database normalizations. However, Redis is not a RDMBS and thus is used most efficiently, when object relations are kept at a minimum (or even better avoided altogether, if possible).

The @Reference annotation actually is just a convenience to developers, taking care of automatically loading referenced objects from Redis database. On a side note, please keep in mind that referenced objects need to be updated/saved separately from the referencing object (related). Resolution of references in Spring Data Redis just mean additional queries under the hood and thus mean less read performance.

For maximum read performance, I would suggest the following changes to your model. Please note that this not necessarily the best model for your purpose, as many constraints of your project remain in the dark. Just consider it as an alternative to your "classic" model you may want to think about.

@RedisHash("messages")
public class Message {
    @Id String messageId;
    @Indexed String threadId;
    @Reference User user;
    String message;
    Date date;
    List&lt;Score&gt; userScores;
}

@RedisHash("users")
public class User {
    @Id String userId;
    String grade;
    List&lt;Score&gt; scores;
}

public class Score {
    String scoreId
    String messageId;
    Long score;
    User userId;
}


This approach basically eliminates the object relationships between Message and Score and User and Score by embedding Score information into the Message and User hash objects as standalone data. So you gain substantial read performance at the cost of data redundancy.

When querying a message (e.g. for message rendering) this means Redis Data just needs one lookup to fetch the full data from database (because message scores are already part of the stored value). The same is valid for querying a user for charting his/her scores. However, the drawback with this approach is, that you need to write/update the Score twice whenever a new score is added to a message by a user: once for the update to the user object and once for the update to the message object. However, using @Reference you still had to write the score object once, then write the updated references to User and Message, respectively.

Another (and probably more important) drawback is the increased memory usage due to the data duplication in User and Message objects. As Redis holds all its data in RAM at the time of this writing (albeit in a very efficient manner), this can become an issue if you deal with a huge amount of users and messages.

The proposed model should work particularly well in a strongly asymmetric producer-consumer model, where you have a lot more consumers (people just reading messages) than producers (users contributing messages and scores to messages). YMMV depending on the actual project style and setup.