Как определить наличие комбинированной комнаты DDD

Несколько месяцев назад я обновил наш механизм доступности, чтобы перевести нашу логику с БД на микро-сервис. В то время бизнес-логика была довольно простой:

  • Ресурс (комната для переговоров, стол, пустой офис, оборудование) доступен ТОЛЬКО в определенный период времени, если он еще не зарезервирован (т. Е. Никакое другое бронирование с использованием того же ресурса)

  • Когда ресурс недоступен, должны быть рассчитаны ближайшие доступные периоды времени

Чтобы удовлетворить эти требования, я создал небольшой фрагмент кода ниже:

public class Schedule : IAggregateRoot
{
    public int CityId { get; }
    public int BuildingId { get; }
    public int CentreId { get; }
    public int ResourceId { get; }

    public ICollection<Booking> Bookings { get; }

    public Schedule(int cityId, int buildingId, int centreId, int resourceId, IEnumerable<Booking> bookings)
    {
        CityId = cityId;
        BuildingId = buildingId;
        CentreId = centreId;
        ResourceId = resourceId;
        Bookings = new List<Booking>(bookings);
    }

    public bool IsTimeSlotFree(DateTimeOffset startDate, DateTimeOffset endDate)
        => Bookings.Any(/* Predicate */);

    public IEnumerable<Availability> GetFreeTimeSlots(
        DateTimeOffset startDate,
        DateTimeOffset endDate, 
        TimeSpan duration)
    {
        var nbSlots = Math.Floor((endDate - startDate) / duration);
        for(int i=0; i<nbSlots; i++) {
            /* yield return availability */
        }
    }
}

public class Availability : ValueObject
{
    public DateTimeOffset StartDate { get; set; }
    public DateTimeOffset EndDate { get; set; }
    public int ResourceId { get; set; }
    public bool IsAvailable { get; set; } 
}

public class Resource : Entity
{
    public string Code { get; set; }

    // Required for EF Core
    protected Resource() { }
}

public class Booking : Entity
{
    public DateTimeOffset StartDate { get; set; }
    public DateTimeOffset EndDate { get; set; }
    public string Status { get; set; }
    public int ResourceId { get; set; }

    // Required for EF Core
    protected Booking() { }
}

Несколько недель назад меня попросили работать с объединенными комнатами (две комнаты поменьше можно объединить в одну большую комнату). В этом сценарии объединенная комната доступна ТОЛЬКО ее подкомнатам и доступна сама. Другими словами, мне нужно проверить несколько расписаний, чтобы определить доступность, и, к сожалению, мой текущий уровень абстракции не позволяет этого (одно расписание, одна комната).

Единственный способ, который я нашел, - это извлечь ресурс и его дочерние элементы (= подрумы), а затем создать расписание, содержащее словарь ResourceId и бронирования.

public class Resource : Entity
{
    public string Code { get; set; }

    public Resource Parent { get; set; }
    public ICollection<Resource> Children { get; set; }

    // Required for EF Core
    protected Resource() { }
}

public class Schedule : IAggregateRoot
{
    public int CityId { get; }
    public int BuildingId { get; }
    public int CentreId { get; }
    public int ResourceId { get; }

    public IDictionnary<int, ICollection<Bookings>> Bookings

    (...)
}

Я не считаю это решение действительно элегантным. Для меня лучшим решением было бы получить графики и объединить их, чтобы определить фактическую доступность. Я попробовал несколько решений, но в итоге я написал код для спагетти.

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

Спасибо, Себ

2 ответа

Решение

По-видимому, основная проблема заключается в том, что в вашей модели отсутствуют концепции доменов.

Я предполагаю, что вам не хватает представления каталога товаров, в котором описан доступный инвентарь. В этом каталоге у вас будет запись для комнаты № 101 и запись для комнаты № 102. Если эти комнаты могут быть упакованы вместе, то у вас также будет запись для пакета [#101 и #102].

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

Обратите внимание, что вы можете обновить каталог - добавлять или удалять пакеты - без какого-либо влияния на бронирование.

Вы, конечно, должны выяснить, как вы будете иметь дело с фактическим бронированием нескольких номеров. Есть пара возможностей; самое простое, и, как я думаю, наиболее знакомо вашим пользователям, - разрешить агрегатам расписания принимать перекрывающиеся заказы, устанавливая флаг двойного бронирования для отслеживания случая, когда необходимо предпринять компенсирующие действия.

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

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

Практически любая модель предметной области, которая включает планирование ресурсов, имеет концепцию Дня как отдельного агрегата. Без этого вы будете продолжать агрегировать вечно. Различные контексты могут сохранять разное представление Дня:

  • Бронирование может иметь типы номеров и доступность
  • Цены могут иметь тип номера и цену
  • Прием может иметь номера комнаты, прибытия и отъезда
Другие вопросы по тегам