如何在达到日期时仅创建一次事件?

发布于 2025-02-09 08:36:46 字数 332 浏览 1 评论 0原文

假设我将来可以安排一件事情(例如表演)。

例如,我想在预定日期之前发出“ 10天”活动。 或者 我想在预定日期之后发出“ 3天”活动(演出发生了)。

我已经存储在DB的预定日期中。

从建筑的角度来看,最好的方法是什么?

我考虑对我的数据库进行定期投票以获取匹配日期,但恐怕要面对以下几个问题:

  • 如何避免到达日期后产生多个事件(现在() + 10天>计划日期)
  • 如何避免丢失的事件(假设我每分钟使用CRON并将范围限制为一分钟,我可能会错过事件)。

我正在寻找企业模式,而且我很确定它存在于以有效方式管理此用例的策略。

Let's say I can schedule a thing in the future (a show for example) in the future.

I want for example to emit an event "10 days" before the scheduled date.
or
I want to emit an event "3 days" after the scheduled date (the show happened).

I've stored in my DB the scheduled date.

What would be the best approach, from an architectural point of view, to do this?

I've think about doing regular polling of my DB to fetch matching date but i'm afraid to face several issues such as:

  • How to avoid generating multiple events once the date is reached (NOW() + 10 days > scheduled date)
  • How to avoid missing events (let's say I use a cron every minute and constraint the range to a minute, I may miss events).

I'm looking for enterprise pattern and I'm pretty sure it exists kind of strategies to manage this use case in an efficient way.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

毅然前行 2025-02-16 08:36:46

在我看来,事件的排放是一个领域的关注。满足技术要求(例如域之间的数据传播)不仅仅是基础架构的要求。因此,我同意,业务逻辑不是您应该将其委派给RabbitMQ的东西。

鉴于此,我将构建将事件处理为域逻辑。

show

public class Show : EntityBase
{
    public DateTime ScheduledDate { get; set; }
    public string Name { get; set; }
    public bool PreShowNotificationSent { get; set; }
    public bool PostShowNotificationSent { get; set; }

    public void NotifyPre()
    {
        if (PreShowNotificationSent)
        {
            throw InvalidOperationException("Notification already sent");
        }
 
        base.AddDomainEvent(PreShowNotificationSentEvent(this));

        PreShowNotificationSent = true;
    }

    // repeat for NotifyPost()
}

然后在启动时初始化背景服务,该服务将根据您的首选时间表(例如每1分钟)运行。

该服务将每1分钟启动一次命令处理程序(sendproshownotifications)。

该命令处理程序将从您的存储库中检索一个“显示器”实体(该实体是处理通知的汇总)。存储库将为所有符合通知标准的表演示例和子对象检索,并为适当的“ Preshownotificationent”或“ shownotificationent”具有“错误”。

显示器

public class ShowNotifier
{
    List<Show> ShowsNeedingPreNotification { get; set; }
    List<Show> ShowsNeedingPostNotification { get; set; }

    public void Notify()
    {
        foreach (Show show in ShowsNeedingPreNotification)
        {
            show.NotifyPre();
        }

        foreach (Show show in ShowsNeedingPostNotification)
        {
            show.NotifyPost();
        }
    }
}

示例限量存储库

public async Task<ShowNotifier> GetWithPendingAsync()
{
    List<Show> pre = await _db.Shows.Where(s => 
        s.ScheduledDate is-within-10-days
        && s.PreShowNotificationSent == false).ToListAsync();

    List<Show> post = // similar to above for post notifications

    return new ShowNotifier()
    {
        ShowsNeedingPreNotification = pre;
        ShowsNeetingPostNotification = post;
    }
}

命令处理程序

ShowNotifier notifier = await _showNotifierRepository.GetWithPendingAsync();

notifier.Notify();

域事件处理程序(用于preshownotificationenteventEvent)

// Publish the integration event on the bus
await EventBus.PublishAsync(new ShowComingUpIntegrationEvent(
    domainEvent.Show.Name,
    domainEvent.Show.ScheduledDate));

使用上述方法将保持核心逻辑您的域名,同时确保每场演出仅发送一个事件。

根据您的样式,您可以将显示器从域实体中提起,并使其成为一种域服务,它使用显示存储库在调用NotifyPre或NotifyPost之前检索显示。

It seems to me that the emission of events is a domain concern. It is not simply an infrastructure requirement to satisfy a technical requirement (e.g. propagation of data between domains). Therefore, I would agree that the business logic is not something you should delegate to RabbitMQ.

Given this, I would build the event handling into domain logic.

Show

public class Show : EntityBase
{
    public DateTime ScheduledDate { get; set; }
    public string Name { get; set; }
    public bool PreShowNotificationSent { get; set; }
    public bool PostShowNotificationSent { get; set; }

    public void NotifyPre()
    {
        if (PreShowNotificationSent)
        {
            throw InvalidOperationException("Notification already sent");
        }
 
        base.AddDomainEvent(PreShowNotificationSentEvent(this));

        PreShowNotificationSent = true;
    }

    // repeat for NotifyPost()
}

Then initialise a background service at startup that will run based on your preferred schedule (e.g. every 1 minute).

That service will initiate a command handler (SendPreShowNotifications) every 1 minute.

That command handler will retrieve a "ShowNotifier" entity from your repository (this entity is the aggregate for handling notifications). The repository will retrieve a ShowNotifier and child objects for all Shows that meet the notification criteria and have 'false' for the appropriate 'PreShowNotificationSent' or 'PostShowNotificationSent'.

ShowNotifier

public class ShowNotifier
{
    List<Show> ShowsNeedingPreNotification { get; set; }
    List<Show> ShowsNeedingPostNotification { get; set; }

    public void Notify()
    {
        foreach (Show show in ShowsNeedingPreNotification)
        {
            show.NotifyPre();
        }

        foreach (Show show in ShowsNeedingPostNotification)
        {
            show.NotifyPost();
        }
    }
}

ShowNotifier Repository

public async Task<ShowNotifier> GetWithPendingAsync()
{
    List<Show> pre = await _db.Shows.Where(s => 
        s.ScheduledDate is-within-10-days
        && s.PreShowNotificationSent == false).ToListAsync();

    List<Show> post = // similar to above for post notifications

    return new ShowNotifier()
    {
        ShowsNeedingPreNotification = pre;
        ShowsNeetingPostNotification = post;
    }
}

Command Handler

ShowNotifier notifier = await _showNotifierRepository.GetWithPendingAsync();

notifier.Notify();

Domain Event Handler (for PreShowNotificationSentEvent)

// Publish the integration event on the bus
await EventBus.PublishAsync(new ShowComingUpIntegrationEvent(
    domainEvent.Show.Name,
    domainEvent.Show.ScheduledDate));

Using the above approach will keep the core logic in your domain, whilst ensuring that only one event is sent for each Show.

Depending on your style, you could lift ShowNotifier out of your domain entities and make it a Domain Service that uses Show repositories to retrieve the Shows before calling NotifyPre or NotifyPost.

尐籹人 2025-02-16 08:36:46

您还可以在服务总线上发布延迟消息(事件)。如果您不使用消息总线系统,则可以应用玉米员系统,并在发布事件(如果已发布事件)中进行跟踪。

You can also publish delayed messages(events) on the service bus. If you don't use a message bus system you can apply a cornjob system and keep track in the aggregate if events have been published.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文