如何确定组合房间可用性 DDD

How to determine combined room availability 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() { }
}

几周前我被要求处理组合房间(两个较小的房间可以合并成一个更大的组合房间)。在这种情况下,只有 sub-rooms 和它本身可用的组合房间才可用。换句话说,我需要检查多个时间表以确定可用性,不幸的是我当前的抽象级别不允许这样做(一个时间表,一个房间)。

我找到的唯一方法是检索资源及其 children(=子房间),然后创建一个包含 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

    (...)
}

我觉得这个解决方案不是很优雅。对我来说,更好的解决方案是检索时间表并将它们组合起来以确定实际可用性。我尝试了几种解决方案,但我 ended-up 编写了意大利面条代码。

关于我如何 re-design 我的聚合正确处理这个新概念,你有什么想法吗?

谢谢, 塞布

据推测,核心问题是您的模型中缺少领域概念。

我的猜测是您缺少描述可用库存的产品目录表示。在该目录中,您将有一个房间 #101 的条目和一个房间 #102 的条目。如果这些房间可以打包在一起,那么您也将有一个打包条目[#101 和#102]。

所以包的可用性很容易 - 获取包中元素的时间表的交集。由于您知道包裹的内容,因此很容易找到您需要协调的时间表。

请注意,您可以更新目录 - 添加或删除套餐 - 不会以任何方式影响预订。

当然,您必须弄清楚您将如何处理多个房间的实际预订。有几种可能性;最简单的,也是我想您的用户最熟悉的一种,是允许计划聚合接受重叠预订,设置重复预订标志以跟踪需要采取补偿措施的情况。

另一种选择是使用 saga 模式预订套餐;如果无法预订整个套餐,则进行预订编排的流程知道取消房间预订。

您可以通过将所有计划移动到一个集合中来简化事情;将一致性边界提高到更大的范围(可能 属性,而不是单个房间);这牺牲了更大房间的自主权和规模。

几乎所有包含资源调度的域模型都将日作为单独的聚合概念。没有它,你将永远聚集在一起。不同的上下文可以保留不同的日期表示:

  • 预订可以有房型和空房情况
  • 定价可以有房型和价格
  • 接待处可以分配房间、到达和离开