聚合可以使用聚合查找服务还是业务逻辑应该在域服务中?

May an aggregate use an aggregate lookup service or should the business logik be in a domain service?

我正在使用带有事件源的 CQRS 开发 DDD 软件。现在我想弄清楚我应该把我的业务逻辑放在哪里。

我有一个引用 triggerId(触发器聚合根)的条件聚合根。我还有一个 conditionGroup 聚合根,其中包含一个 conditionId 列表。我有一个看门狗聚合根,它包含一个 conditionGroupId。

当调用触发器聚合的Create 方法时,域发出一个triggerReceived 事件。 triggerReceived 事件具有 triggerId 和一个值 属性,应由链接到条件聚合中的 triggerId 的条件进行检查。

我在域端有一个订阅者监听这个事件。我的计划是检索订阅者中的所有看门狗并调用方法 watchdog.ShouldBark(triggerId, value)。看门狗然后需要查找 ConditionGroup 聚合(它的 ConditionGroupId 为 属性)并调用 ConditionGroup.DoesGroupMatch(triggerId, value)。 ConditionGroup 必须查找所有 Condition 聚合(它有一个 conditionId 列表)并调用 Condition.DoesConditionMatch(triggerId, value) 方法。

因此每个聚合都必须查找其他聚合以访问一个业务逻辑方法,该方法进行一些检查(未完成更新)。

或者在域中有一些 watchdogService 并且 watchdog 服务执行所有聚合查找并具有业务逻辑是否更好?

所以我的问题是:聚合是否可以通过使用接口 (IAggregateStore) 进行业务逻辑检查来查找其他聚合(没有更新,因为每个事件只应更新一个聚合)?

您的描述相当不完整,因此这个答案也可能不完整。 根据您的描述 -

I have a subscriber on the domain side which listens to this event. My plan is to retrieve all watchdogs in the subscriber and call a method watchdog.ShouldBark(triggerId, value).

我在你的域设计中闻到了一些非常糟糕的东西。 ShouldBark 方法的目的是什么。它必须更改您的 WatchDog 域的状态。如果是这样的话,你走错了方向。

让我们先获取您的业务领域。 触发器,条件,条件组,看门狗.

I have a condition aggregate root which references a triggerId (trigger aggregate root).

所以,你在触发器聚合中的代码应该是这样的(你可能已经发现我的一些代码是伪代码)-

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;
    }
}

基于此 -

I also have a conditionGroup aggregate root which holds a list of conditionIds.

我们还可以声明,每个条件都应该有一个名为 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;
    }
}

最后,基于此——

And I have a watchdog aggregate root which holds a 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);
    }
}

您可能已经发现这是一个事件和订阅者的网络,但请记住,事件源系统应该始终是最终一致的。通过这种方式,您可以记录 TriggerConditionConditionGroup看门狗.