Статьи

EventsZilla: прохождение моделирования RavenDB (часть 1)

Мне нужно было простое приложение для публикации событий. Мне также хотелось создать еще одно приложение-образец RavenDB и пост RavenDB. Вот как EventsZilla ожила. 

EventsZilla (полные источники здесь: https://github.com/synhershko/eventszilla ) предназначена для того, чтобы быть простым веб-приложением для объявления событий вместе с расписанием, которое также способно просматривать прошлые и будущие события. Люди должны иметь возможность зарегистрироваться на событие, не регистрируясь на веб-сайте, а также просматривать слайды и другой контент, когда он станет доступен после события.

Этот пост пишется во время разработки, описывая каждый этап и соображения, ведущие к следующему. Таким образом, код, на который я ссылаюсь, не обязательно работает, хотя и должен. После публикации этого поста я, вероятно, внесу некоторые исправления и поправки в код.

Начальное моделирование

Когда мы говорим о приложении для публикации событий, на что мы смотрим? Основными элементами являются событие со средствами регистрации и список сеансов для каждого события. Каждое событие должно иметь окно регистрации, место его проведения, а также заголовок и описание.

Для каждого сеанса в событии мы хотим иметь докладчика (возможно, более одного), заголовок, краткое изложение (или реферат) и время, в которое каждый сеанс начинается и заканчивается. Следует отметить, что время начала и окончания события будет получено непосредственно из первой и последней сессий события. А пока мы называем сессию «слот расписания».

В отличие от реляционной модели, с RavenDB мы можем сделать эскиз целиком как один класс и просто использовать его. Однако есть одно исключение — на данном этапе мы уже знаем, что места проведения и докладчики могут показываться несколько раз в разных мероприятиях (возможно, даже один и тот же докладчик в нескольких сеансах в одном и том же событии), поэтому мы не хотим хранить их непосредственно под событие, а скорее ссылку на них, сохраняя только их идентификаторы. Их можно эффективно найти с помощью функции «Включает».

Мы заканчиваем с этим классом События:

public class Event
{
    public Event()
    {
        Schedule = new List<ScheduleSlot>();
    }
 
    public int Id { get; set; }
    public string Title { get; set; }
    public string Slug { get; set; }
    public string Description { get; set; } // markdown content
 
    public string VenueId { get; set; }
 
    public DateTimeOffset CreatedAt { get; set; }
 
    public DateTimeOffset RegistrationOpens { get; set; }
    public DateTimeOffset RegistrationCloses { get; set; }
    public int AvailableSeats { get; set; }
 
    public class ScheduleSlot
    {
        public List<string> PresenterIds { get; set; } // list of person IDs
        public string Title { get; set; }
        public string Brief { get; set; } // markdown
        public DateTimeOffset StartingAt { get; set; }
        public DateTimeOffset EndingAt { get; set; }
    }
    public List<ScheduleSlot> Schedule { get; set; }
 
    public DateTimeOffset StartsAt
    {
        get
        {
            var firstSession = Schedule.OrderBy(x => x.StartingAt).FirstOrDefault();
            return firstSession == null ? DateTimeOffset.MinValue : firstSession.StartingAt;
        }
    }
 
    public DateTimeOffset EndsAt
    {
        get
        {
            var lastSession = Schedule.OrderByDescending(x => x.EndingAt).FirstOrDefault();
            return lastSession == null ? DateTimeOffset.MaxValue : lastSession.EndingAt;
        }
    }
}

Поскольку расписание событий не имеет смысла вне рамок события, его лучше сохранить там же. Это также означает, что весь график будет загружен вместе с событием при каждой операции загрузки или запроса, частью которой будет данное событие. На данном этапе мы в порядке с этим.

Свойства StartsAt и EndsAt события сохраняются таким образом, чтобы снять некоторое давление с индексов, которые мы собираемся создать, поэтому бизнес-логика будет находиться в реальных типах доменов, а не в индексах в максимально возможной степени.

Классы Venue и Presenter довольно тривиальны, поэтому здесь не будут показаны.

Фактический код для этой фазы находится в коммите github .

Регистрация событий

Регистрация на событие является довольно распространенной операцией в нашей системе, и на переполненном веб-сайте можно сделать несколько регистраций на одно и то же событие одновременно.

Как и расписание событий, регистрация событий вообще не имеет смысла вне рамок самого события, по крайней мере до тех пор, пока мы не пытаемся отслеживать участников (а мы этого не делаем). В отличие от графика, список участников будет меняться довольно часто, и часто одновременно. По этой причине сохранение этого списка в самом объекте Event не имеет смысла, так как это потребует от нас задуматься о разрешении конфликтов, когда два или более человека пытаются зарегистрироваться на одно и то же событие в одно и то же время.

Еще одна причина, по которой не нужно сохранять регистрации внутри самого события, заключается в том, что мы не заботимся об этом, когда загружаем событие, и этот список может вырасти довольно большим для определенных событий. Мы хотим убедиться, что объект Event содержит только те данные, к которым мы будем часто обращаться в этом контексте; список владельцев регистраций — это не тот тип данных.

Чтобы разделить список владельцев регистраций, хотя мы позаботились о том, чтобы нам не пришлось беспокоиться о возможных конфликтах, я создал простой класс EventRegistration, который будет хранить все эти данные. Мы сохраняем это именно таким образом, и всякий раз, когда нам нужно узнать количество людей, зарегистрировавшихся на событие, мы запрашиваем в БД количество регистраций для этого события. Этот запрос использует простой статический индекс, который мы определили заранее.

public class EventRegistration
{
    public string EventId { get; set; }
    public string RegistrantEmail { get; set; }
    public string RegistrantName { get; set; }
    public DateTimeOffset RegisteredAt { get; set; }
}

Фактический код для этой фазы находится в этом коммите .

Доступные места

Тем не менее, в контексте регистраций, важно отметить, что по замыслу мы не обязательно блокируем регистрацию для события, во второй момент, когда оно заполнено. Причина этого заключается в том, что мы получаем число людей, зарегистрировавшихся на событие, с помощью индексационного запроса, и хотя процесс индексирования RavenDB довольно быстрый, на загруженных веб-сайтах возможно, что этот запрос вернет число, которое не полностью на свидание.

В EventsZilla это дизайнерское решение, которое мы приняли, а не думать как компьютер. Ваш компьютер знает, что нужно соблюдать жесткие ограничения, но в жизни мы вряд ли действительно так поступаем. Итак, если ваше мероприятие рассчитано на 100 мест, вы не сможете выжать еще 10-15? и вы действительно уверены, что все первые 100 владельцев регистраций действительно появятся?

По этой причине нас не волнует, является ли количество, которое мы получили из нашего запроса, не фактическим значением в момент времени, когда мы выпустили запрос. Это довольно распространенная практика с возможной последовательностью — мы не стараемся соблюдать жесткие ограничения, которые на самом деле не существуют, и случаются удивительные вещи.

RavenDB может сказать нам, что счет, который мы получили, устарел. Ожидание нестандартных результатов возможно, но на самом деле это не рекомендуется, поскольку это значительно замедлит работу вашей системы. В некоторых случаях это также может привести к бесконечному ожиданию. Так что не делай этого.

Если нам действительно необходимо соблюдать жесткие ограничения, мы можем добавить свойство RegistrantsCount в наш класс Event и увеличивать его при каждой регистрации. Требуется немного больше работы, чтобы удостовериться, что одновременные записи обнаружены, и одна задержка, поэтому не происходит неправильных подсчетов, но это гарантирует, что мы можем знать точное количество в любое время, так как мы могли бы тогда получить это событие, используя операцию Load , который является кислотой.

Следующий в очереди

Как только время позволит, я буду исследовать наши возможности для времен, когда мы хотим включить различные треки в событии и лучше поддерживать события только с одним сеансом. Кроме того, в моем списке задач есть возможность для каждого слота расписания иметь такие материалы, как слайды, видео, примеры кода и т. Д. Оставайтесь с нами.

Источник: http://www.code972.com/blog/2011/11/eventszilla-ravendb-modeling-walkthrough/