Actually, RavenDB is a perfect fit for that. To properly do that, ask yourself what are the main entities in your model? each one of those will be a document type in RavenDB.
So in your scenario, you'd have Event
and User
. Then, an Event
can have a list of User
IDs which you can then easily index and query on. There are more ways to do that, and I actually discussed this in my blog some time in the past with some further considerations that might come up.
The only non-trivial bit is probably the index for answering queries like "all events user has not entered", but that's quite easily done as well:
public class Events_Registrations : AbstractIndexCreationTask<Event>
{
public Events_Registrations()
{
Map = events => from e in events
select new { EventId = e.Id, UserId = e.Registrations.SelectMany(x => x.UserId) });
}
}
Once you have such an index in place, you can do a query like below to get all events a specified user has no registrations to:
var events = RavenSession.Advanced.LuceneQuery<Event, Events_Registrations>()
.Where("EventId:*")
.AndAlso()
.Not
.WhereEquals("UserId", userId).ToList();
Then handling things like expiring events etc is very easily done. Definitely don't denormalize event data in the User object, it will make your life living hell.
There's some data missing though - for example how many registrations are allow per event. If there are too many you may want to break it out of the actual Event
object, or to revert to Booking objects as you mention. I discuss this in length in my book RavenDB in Action (shameless plug, I know, but its just too long to actually discuss here).