Domain driven design 如何管理DDD中的两个耦合聚合?

Domain driven design 如何管理DDD中的两个耦合聚合?,domain-driven-design,dddd,Domain Driven Design,Dddd,我正在开发电动汽车充电站管理系统,该系统连接到多个充电站,我陷入了僵局。在这个领域中,我为充电站提供了一个聚合,它包括充电站的内部状态是否连接,其连接器的内部状态 它有一个UnlockConnector方法,为了准确地尊重其名称,而不是贫血,它会将请求发送到相应的充电站,因为了解充电站的连接性和向物理充电站发送请求是我所在领域的核心: 类型连接器结构{ 状态字符串 } 类型ChargingStation结构{ 连通布尔 连接器[]连接器 URL字符串 ... } func station*Cha

我正在开发电动汽车充电站管理系统,该系统连接到多个充电站,我陷入了僵局。在这个领域中,我为充电站提供了一个聚合,它包括充电站的内部状态是否连接,其连接器的内部状态

它有一个UnlockConnector方法,为了准确地尊重其名称,而不是贫血,它会将请求发送到相应的充电站,因为了解充电站的连接性和向物理充电站发送请求是我所在领域的核心:

类型连接器结构{ 状态字符串 } 类型ChargingStation结构{ 连通布尔 连接器[]连接器 URL字符串 ... } func station*Charging station解锁ConnectorConnectorId int,stationClient服务。stationClient错误{ 如果!车站已接通{ 返回错误。新充电站未连接 } 连接器:=站。连接器[connectorID] if connector.Status!=可用{ 返回错误。无法解锁Newconnector } 错误:=stationClient.SendUnlockConnectorRequeststation.URL,connectorID 如果错误!=零{ 返回错误。新充电站拒绝了该请求 } station.OneEvent.StationConnector已解锁{ ConnectorID:ConnectorID, 时间戳:时间,现在, } 归零 } 我还提出了另一个聚合,它表示充电会话,用户和充电站连接器之间的交互。充电会话的创建与连接器的状态完全耦合,即,如果用户已解锁连接器,则创建会话;如果连接器的能量流已停止,则充电会话已结束

无论两者如何耦合,充电会话实体似乎不属于充电站聚合,因为它不回答主要问题:当一个站被删除时,其充电会话是否也应该被删除?此外,我还认为,当我为本次会议所消耗的能源付费时,它与站点聚合上下文没有任何关系

我想创建一个SessionCreator域服务,确保充电站的连接器解锁时,也可以创建充电会话:

类型SessionCreator接口{ CreateSessionstation*station.station,connectorID int,sessionID int错误 } 类型sessionCreator结构{ stationClient stationClient } 键入svc sessionCreator CreateSessionstation*station.station、connectorID int、用户ID字符串、sessionID int error{ 错误:=station.UnlockConnectorconnectorID,svc.stationClient 如果错误!=零{ 返回错误 } session.CreatesessionID、station.ID、connectorID、userID 归零 }
然而,它只是感觉有点奇怪,并不能完全满足其他不变量。当连接器的能量流停止时,它必须结束会话,我认为这就像制作一个事件侦听器来侦听StationConnectorUnlocked事件一样,但我不知道哪种方式是理想的。

从这个问题中我可以理解,我认为您的充电站和充电会话是正确的。你说它们都是耦合的,但在我看来,充电站不需要知道会话的存在,会话也不需要知道任何关于站点内部的信息,所以对我来说耦合度很低

关于您关于在电台发生事件时如何创建和修改会话的问题,根据您的措辞,解决方案对我来说似乎很清楚:

如果用户已解锁连接器,则会创建会话;如果连接器的能量流已停止,则充电会话已结束

突出显示的单词是商业事件。你可以这样画:

连接器解锁->计费会话已创建 连接器能量流停止->充电会话停止 我实现这一点的方法是使用发布/订阅系统,因此在状态更改后聚合发布事件,订阅者触发用例,这可能会对聚合执行操作,最终可能会发布事件


这种方法的分布式特性使得跟踪整个系统上发生的事情更加困难,因为解锁连接器和创建会话不是在一个地方发生的。它们可能发生在系统的两个不同部分。其优点是,随着时间的推移,当连接器解锁时,涉众可以想出许多需要发生的事情,而您只需不断向现有事件添加订阅者

您是否在充电站内部运行此型号?或者模型运行在某个中心位置,监听来自充电站的消息?我只在这个中心位置运行模型,它监听来自所有充电站的消息。这是不可能的
e要更改充电站本身的代码,因为它们已经内置了供应商的代码。我开发了一个进程管理器,用于侦听连接器事件,并向会话聚合CreateSession、FinishSession发出命令。在CreateSession命令中,一切工作都很完美,就像我简单地执行CreateSessionid:newUUID、stationId:event.stationId等操作一样。。。。但是,在FinishSession中,我无法加载会话聚合以完成它,因为ConnectorEnergyFlowStopped事件中不包含会话ID。我已设法将内存中的内部状态添加到process manager,以便它将stationID、connectorID与sessionID相关联。但是,在内存中,只要process manager崩溃,我就失去了stationID、connectorID sessionID之间的关联,并且我无法再正确响应ConnectorEnergyFlowStopped事件。你知道我该怎么处理吗?我应该坚持流程管理器状态吗?如果我在Process Manager事件中坚持使用它,那会很尴尬,因为它与无处不在的语言没有很好的关联。我不明白为什么您需要一个Process Manager。我认为在聚合中处理事件和执行操作的行为是干净体系结构中的一个用例,它所做的唯一事情是:var agg=uow.GetAggregateBySomeId;综合执行操作;uow.Commit。您可以使用UnitOfWork或Repository,但您将专门为该聚合定义一个接口。因此,在您的情况下,可能需要GetSessionById和GetSessionByConnectorId。这应该允许您处理会话上的所有异步和同步操作。我个人不喜欢活动外包,我总是看到太多的问题,但没有什么价值,所以我真的不能帮你,对不起。存储聚合的最简单方法是使用NoSQL文档,因为您无论如何都需要将它们作为单个单元进行读写。为所有课程补水听起来并不正确,因为最终会遇到可伸缩性问题。也许你必须把这个问题作为一个单独的问题发布,有ES知识的人可以帮助你。当然,没问题。谢谢你在其他方面的帮助: