我正在使用带有事件源的CQRS开发DDD软件。现在,我试图找出应该将业务逻辑放在哪里。
我有一个条件汇总根,它引用了triggerId(触发汇总根)。我还有一个conditionGroup聚合根,其中包含conditionId的列表。而且我有一个看门狗聚合根,其中包含一个conditionGroupId。
当调用触发器聚合Create方法时,域将发出triggerReceived事件。 triggerReceived事件具有triggerId和一个value属性,应该由与该条件聚集中的triggerId链接的条件检查该条件。
我在域一侧有一个订阅者,它在监听此事件。我的计划是检索订户中的所有监视程序,并调用方法watchdog.ShouldBark(triggerId,value)。然后,看门狗需要查找ConditionGroup聚合(它具有ConditionGroupId作为属性)并调用ConditionGroup.DoesGroupMatch(triggerId,value)。 ConditionGroup必须查找所有Condition聚合(它具有conditionId列表),并调用Condition.DoesConditionMatch(triggerId,value)方法。
因此,每个聚合必须查找其他聚合以访问进行某些检查(不进行任何更新)的业务逻辑方法。
还是在域中提供一些watchdogService更好,而watchdog服务执行所有汇总查找并具有业务逻辑?
所以我的问题是:聚合可以通过使用接口(IAggregateStore)进行业务逻辑检查来查找其他聚合(无更新,因为每个事件仅应更新一个聚合)?
答案 0 :(得分:1)
您的描述还很不完整,因此此答案也可能不完整。 根据您的描述-
我在域端有一个订阅者,它在监听此事件。我的计划是检索订户中的所有看门狗,并调用方法watchdog.ShouldBark(triggerId,value)。
在您的域设计中,我会闻到非常不好的气味。 ShouldBark 方法的目的是什么。它必须更改您的WatchDog域的状态。如果真是这样,您的方向就会错误。
让我们首先获取您的业务领域。 触发 , 条件 , 条件组 , WatchDog 。
我有一个条件聚合根,它引用了triggerId(触发聚合根)。
因此,触发器聚合中的代码应如下所示(您可能发现我的某些代码是伪代码)-
public class Trigger : AggregateRoot
{
...
public void CreateTrigger(int value)
{
//You can also pass new guid for trigger from your service
Guid triggerId = Guid.NewGuid();
var @event = new TriggerReceived(triggerId, value);
PublishEvent(@event);
}
}
//this is your event
public class TriggerReceived
{
public readonly int Value;
public readonly Guid TriggerId;
public TriggerReceived(Guid triggerId, int value)
{
Value = value;
TriggerId = triggerId;
}
}
现在,朝正确的方向前进。无需检索所有的WatchDog,请订阅您的 Condition 服务到该事件。
public class ConditionService
{
public void When(TriggerReceived @event)
{
//re-hydrate your condition aggregate root from event store
var condition = getConditionByTriggerId(@event.TriggerId);
//if you want to retrieve condition based on Value you can do that here
//var condition = getConditionByValue(@event.Value);
if(condition is not null)
condition.MatchTrigger();
}
}
然后在您的条件汇总中-
public class Condition : AggregateRoot
{
...
public void MatchTrigger()
{
//your business logic here
...
//we know trigger value matched one condition, so raise the next event
publish(new TriggerConditionMatched(this));
}
}
//this is your TriggerConditionMatched event
public class TriggerConditionMatched
{
public readonly Condition Condition;
public TriggerConditionMatched(Condition condition)
{
Condition = condition;
}
}
基于此-
我还有一个conditionGroup聚合根,其中包含conditionId列表。
我们还可以声明,每个条件都应具有一个名为 ConditionGroupId 的属性。订阅您的 ConditionGroup 服务以收听上述活动-
public class ConditionGroupService
{
public void When(TriggerConditionMatched @event)
{
//re-hydrate your condition group aggregate root from event store
var conditionGroup = getConditionGroup(@event.Condition.GroupId);
if(conditionGroup is not null)
conditionGroup.MatchTriggerToConditionGroup();
}
}
public class ConditionGroup : AggregateRoot
{
...
public void MatchTriggerToConditionGroup()
{
...
//do some check here, your business logic
//raise the next event
publish(new TriggerConditionGroupMatched(this));
}
}
//this is your TriggerConditionGroupMatched event
public class TriggerConditionGroupMatched
{
public readonly ConditionGroup ConditionGroup;
public TriggerConditionGroupMatched(ConditionGroup conditionGroup)
{
ConditionGroup = conditionGroup;
}
}
最后,基于此-
而且我有一个看门狗聚合根,其中包含一个conditionGroupId。
您应该在 ConditionGroup 域中拥有一个watchDogId属性。因此,订阅您的 WatchDog 服务以收听 TriggerConditionGroupMatched 事件-
public class WatchDogService
{
public void When(TriggerConditionGroupMatched @event)
{
//re-hydrate your Watch Dog aggregate root from event store
var watchDog = getWatchDog(@event.ConditionGroup.WatchDogId);
if(watchDog is not null)
watchDog.Bark();
}
}
public class WatchDog : AggregateRoot
{
...
public void Bark()
{
...
this.ShouldBark = true;
//you should raise your next event
publish(nextEvent);
}
}
您可能已经发现这是一个由事件和订阅者组成的网络,但是请记住,事件源系统最终应始终保持一致。这样,您就可以记录发生在触发器或条件或 ConditionGroup 或 WatchDog 上的所有事件。 >