Events 应用期间引发的CQRS事件

Events 应用期间引发的CQRS事件,events,domain-driven-design,cqrs,event-sourcing,state-pattern,Events,Domain Driven Design,Cqrs,Event Sourcing,State Pattern,我在使用CQR建模和实现事件出席系统时遇到了一个问题。我的问题是子实体可以引发事件,但我不确定如何以及何时处理它 基本上,一个活动可以有参与者,他们以待定状态开始,可以接受或拒绝参加该活动。但是,他们可以更改出席人数,当这种情况发生时,我希望引发一个事件,以便事件处理程序可以处理(例如通知事件组织者) 我已经使用状态模式来管理与会者的状态,它取决于是否应该引发事件的当前状态。目前,此事件不会更改事件的状态。然而,在我看来,这个事件应该是事件流的一部分 我的问题是,在应用其中一个AttenderR

我在使用CQR建模和实现事件出席系统时遇到了一个问题。我的问题是子实体可以引发事件,但我不确定如何以及何时处理它

基本上,一个活动可以有参与者,他们以待定状态开始,可以接受或拒绝参加该活动。但是,他们可以更改出席人数,当这种情况发生时,我希望引发一个事件,以便事件处理程序可以处理(例如通知事件组织者)

我已经使用状态模式来管理与会者的状态,它取决于是否应该引发事件的当前状态。目前,此事件不会更改事件的状态。然而,在我看来,这个事件应该是事件流的一部分

我的问题是,在应用其中一个AttenderResponsed事件之前,我不知道是否会引发该事件,该事件会在当前状态下调用该方法。如果我在应用过程中引发事件,则在重新水化AR时会出现问题。我可以在应用过程中将此信息添加到事件中,并获得状态返回信息,但随后,事件变得易变

我的想法是,可能状态模式不适合作为事件生成的地方,或者状态模式不适合这里。我可以扩展state,让它有一个方法来确定某个状态更改是否会抛出一个事件,但这看起来很笨拙

最后,我的AR没有任何对eventBus的引用,因此我不能只是将事件扔到总线上,而不将其作为AR事件流的一部分。我认为AR对事件总线的引用开始违反SRP,但也许我在这方面错了

我已经加入了简化的代码来帮助我进行描述。有谁有一些有用的建议吗?谢谢,菲尔

公共类事件:EventSourceDaggerGetRoot
{
#区域字段
私有只读哈希集_attention=new HashSet();
私有Guid\u事件ID;
私有字符串\u标题;
#端区
#区域构造函数
[过时]
私人活动()
{
}
公共事件(LocalDate,字符串标题)
{
HandleEvent(创建的新事件(日期、标题、新GuidCombGenerator().generateWid());
}
公共事件(IEnumerable@events)
{
LoadsFromHistory(@events);
}
#端区
#区域属性和索引器
公共IReadOnlyCollection出席率
{
获取{return _attention.ToArray();}
}
公共Guid事件ID
{
获取{return\u eventID;}
专用设备
{
如果(_eventID==new Guid())_eventID=value;
else抛出新的FieldAccessException(“无法更改实体的ID”);
}
}
public LocalDate日期{get;private set;}
公共覆盖Guid ID
{
获取{return EventID;}
设置{EventID=value;}
}       
公共字符串标题
{
获取{return\u title;}
专用设备
{
它(()=>值).IsNotNullOrWhiteSpace();
_标题=价值;
}
}       
#端区
#区域方法
公共覆盖无效删除()
{
如果(!已删除)
HandleEvent(新事件已删除(事件ID));
}
public void UpdateEvent(LocalDate,字符串标题)
{
HandleEvent(新事件更新(日期、标题、事件ID));
}
public void AddAttendee(Guid成员ID)
{            
Guard.That(()=>\u attention.IsTrue(set=>set.All(attendee=>attendee.MemberID!=MemberID),“与会者已经存在”);
HandleEvent(新添加的与会者(memberID、EventID));
}
public void DeleteAttendee(Guid成员ID)
{
Guard.That(()=>memberID).IsTrue(x=>Attention.Any(a=>attendee.memberID==memberID),“memberID不存在”);
HandleEvent(新与会者已删除(成员ID、事件ID));
}               
内部void RespondIsComing(Guid成员ID)
{
Guard.That(()=>memberID).IsTrue(x=>Attention.Any(a=>attendee.memberID==memberID),“memberID不存在”);
HandleEvent(新与会者响应数据集(memberID、EventID));
}
内部void RespondNotComing(Guid成员ID)
{
Guard.That(()=>memberID).IsTrue(x=>Attention.Any(a=>attendee.memberID==memberID),“memberID不存在”);
HandleEvent(新与会者响应数据命名(memberID,EventID));
}       
#端区
#区域事件处理程序
私有无效应用(EventCreated@event)
{
日期=@event.Date;
Title=@event.Title;
EventID=@event.EventID;
}
私有无效应用(EventDeleted@event)
{
删除=真;
}
私有无效应用(AttendenceAdded@event)
{
_添加(新与会者(@event.MemberID,@event.EventID));
}
私有无效应用(EventUpdated@event)
{
Title=@event.Title;
日期=@event.Date;
}
私有无效应用(AttenderRespondeDascoming@event)
{
var attendee=GetAttendee(@event.AttendeeID);
attendee.Accept();
}
私有无效应用(AttenderRespondeDasNotComming@event)
{
var attendee=GetAttendee(@event.AttendeeID);
attendee.Reject();
}              
私有无效应用(Attendee已删除@事件)
{
_Attention.RemoveWhere(x=>x.AttendeeID==@event.AttendeeID);
}
受保护的覆盖无效ApplyEvent(IAggregateEvent@event)
{
应用((动态)@事件);
}
#端区
}
公开课参加者
{
#区域参与响应枚举
公共枚举参与响应
{
待定,
来,,
不来
}
#端区
#区域字段
私人IATTendenceResponseEstate(U attendState);
私有只读Guid\u事件ID;
私有只读Guid\u成员ID;
#端区
#区域构造函数
公众与会者
public class Event : EventSourcedAggregateRoot<Guid>
{
    private readonly HashSet<AttendeeId> _attendance = new HashSet<Attendee>();
    private EventId _eventID;
    private string _title;

    // generating AR ID should not be a responsibility of the AR
    // All my IDs are generated by the client or the place where commands are created
    // One thing about building CQRS systems is the you must trust the client. This is important. Google it.
    public Event(EventId id, LocalDate date, string title, List<AttendeeId> attendees/* Can you create an event without attendees? */)
    {
        HandleEvent(new EventCreated(date, title, attendees, id));
    }

    This override reminds me of an active record pattern.
    //public override void Delete()
    public void Cancel()
    {
        if (!Deleted)
            HandleEvent(new EventDeleted(EventID));
    }

    // May be you could split it to two events. The other one could be RescheduleEvent
    // and all attendees will be notified. But changing the title could be just a typo.
    public void UpdateEvent(LocalDate date, string title)
    {
        HandleEvent(new EventUpdated(date, title, EventID));
    }

    public void AddAttendee(AttendeeId memberID)
    {            
        Guard.That(() => _attendance).IsTrue(set => set.All(attendee => attendee.MemberID != memberID), "Attendee already exists");
        HandleEvent(new AttendeeAdded(memberID, EventID));
    }
    public void DeleteAttendee(AttendeeId memberID)
    {
        Guard.That(() => memberID).IsTrue(x => Attendance.Any(a => attendee.MemberID == memberID), "MemberID does not exist");
        HandleEvent(new AttendeeDeleted(memberID, EventID));
    }               
    internal void RespondIsComing(AttendeeId memberID)
    {
        Guard.That(() => memberID).IsTrue(x => Attendance.Any(a => attendee.MemberID == memberID), "MemberID does not exist");
        HandleEvent(new AttendeeRespondedAsComing(memberID, EventID));
    }
    internal void RespondNotComing(AttendeeId memberID)
    {
        Guard.That(() => memberID).IsTrue(x => Attendance.Any(a => attendee.MemberID == memberID), "MemberID does not exist");
        HandleEvent(new AttendeeRespondedAsNotComing(memberID, EventID));
    }       

    private void Apply(EventCreated @event)
    {
        Date = @event.Date;
        Title = @event.Title;
        EventID = @event.EventID;          
    }
    private void Apply(EventDeleted @event)
    {
        Deleted = true;
    }
    private void Apply(AttendeeAdded @event)
    {
        _attendance.Add(new Attendee(@event.MemberID, @event.EventID));
    }
    private void Apply(EventUpdated @event)
    {
        Title = @event.Title;
        Date = @event.Date;           
    }
    private void Apply(AttendeeRespondedAsComing @event)
    {
        var attendee = GetAttendee(@event.AttendeeID); // What this method does?
        //attendee.Accept();
    }
    private void Apply(AttendeeRespondedAsNotComing @event)
    {
        var attendee = GetAttendee(@event.AttendeeID);// What this method does?
        //attendee.Reject();
    }              
    private void Apply(AttendeeDeleted @event)
    {
        _attendance.RemoveWhere(x => x.AttendeeID == @event.AttendeeID);
    }
    protected override void ApplyEvent(IAggregateEvent @event)
    {
        Apply((dynamic) @event);
    }      
}