如何在 DDD 中管理两个耦合聚合?
How to manage two coupled aggregates in DDD?
我正在开发电动汽车充电站管理系统,它连接了几个Charging Station
,我陷入了僵局。在这个域中,我为 Charging Station
提出了一个聚合,其中包括 Charging Station
的内部状态(是否连接,其连接器的内部状态)。
它有一个 UnlockConnector
方法,为了准确地尊重它的名称(而不是贫血),它将请求发送到相应的 Charging Station
,因为它是我域的核心知道 Charging Station
的连接性,并向物理 Charging Station
:
发送请求
type Connector struct {
Status string
}
type ChargingStation struct {
Connected bool
Connectors []Connector
URL string
...
}
func (station *ChargingStation) UnlockConnector(connectorID int, stationClient service.StationClient) error {
if !station.Connected {
return errors.New("charging station is not connected")
}
connector := station.Connectors[connectorID]
if connector.Status != "Available" {
return errors.New("connector is not available to be unlocked")
}
err := stationClient.SendUnlockConnectorRequest(station.URL, connectorID)
if err != nil {
return errors.New("charging station rejected the request")
}
station.On(event.StationConnectorUnlocked{
ConnectorID: connectorID,
Timestamp: time.Now(),
})
return nil
}
我想出了另一个聚合,它表示 Charging Session
,用户与 Charging Station
的 Connector
之间的交互。 Charging Session
的创建完全与 Connector
的状态相结合,即如果 Connector
已被用户解锁,则创建会话,如果 Connector
的能量流已经停止,Charging Session
已经结束
无论它们如何耦合,Charging Session
实体似乎不属于 Charging Station
聚合,因为它没有回答主要问题:删除站时,它的 Charging Session
是否也应该被删除?)另外,当我支付本次会话中消耗的能量时,它与站点聚合上下文没有任何关系。
我想创建一个 SessionCreator
域服务,以确保当 Charging Station
的 Connector
解锁时,也会创建 Charging Session
:
type SessionCreator interface {
CreateSession(station *station.Station, connectorID int, sessionID int) error
}
type sessionCreator struct {
stationClient StationClient
}
type (svc sessionCreator) CreateSession(station *station.Station, connectorID int, userID string, sessionID int) error {
err := station.UnlockConnector(connectorID, svc.stationClient)
if err != nil {
return err
}
session.Create(sessionID, station.ID, connectorID, userID)
return nil
}
然而它只是感觉有点奇怪,并且不能完全满足其他不变量(当连接器的能量流停止时,它必须结束会话),我虽然也制作了一个事件监听器来监听StationConnectorUnlocked
事件,但我只是不知道哪个是理想的方式。
根据我对问题的理解,我认为您的聚合 Charging Station
和 Charging Session
是正确的。你说他们都是耦合的,但在我看来 Charging Station
不需要知道 Session 的存在,而且 Session 也不需要知道 Station 内部的任何信息,所以对我的耦合度很低。
关于你的问题,当Station发生事情时如何创建和修改Session,根据你的措辞,我觉得解决方案很清楚:
if the Connector has been unlocked by a user, a session was created, if the Connector's energy flow has stopped, the Charging Session has ended.
突出显示的词是业务事件。你可以这样画:
- ConnectorUnlocked --> ChargingSessionCreated
- ConnectorEnergyFlowStopped --> ChargingSessionEnded
我的实现方式是使用 pub/sub 系统,因此聚合会在状态更改和订阅者触发用例后发布事件,这可能会对聚合执行操作,这可能会结束发布一个事件。
这种方法的分布式特性使得跟踪整个系统上发生的事情变得有点困难,因为解锁连接器和创建会话不会在一个地方发生。它们可能会发生在系统的两个不同部分。优点是,随着时间的推移,利益相关者可以想出连接器解锁时需要发生的许多事情,而您只需要继续向该现有事件添加订阅者。
我正在开发电动汽车充电站管理系统,它连接了几个Charging Station
,我陷入了僵局。在这个域中,我为 Charging Station
提出了一个聚合,其中包括 Charging Station
的内部状态(是否连接,其连接器的内部状态)。
它有一个 UnlockConnector
方法,为了准确地尊重它的名称(而不是贫血),它将请求发送到相应的 Charging Station
,因为它是我域的核心知道 Charging Station
的连接性,并向物理 Charging Station
:
type Connector struct {
Status string
}
type ChargingStation struct {
Connected bool
Connectors []Connector
URL string
...
}
func (station *ChargingStation) UnlockConnector(connectorID int, stationClient service.StationClient) error {
if !station.Connected {
return errors.New("charging station is not connected")
}
connector := station.Connectors[connectorID]
if connector.Status != "Available" {
return errors.New("connector is not available to be unlocked")
}
err := stationClient.SendUnlockConnectorRequest(station.URL, connectorID)
if err != nil {
return errors.New("charging station rejected the request")
}
station.On(event.StationConnectorUnlocked{
ConnectorID: connectorID,
Timestamp: time.Now(),
})
return nil
}
我想出了另一个聚合,它表示 Charging Session
,用户与 Charging Station
的 Connector
之间的交互。 Charging Session
的创建完全与 Connector
的状态相结合,即如果 Connector
已被用户解锁,则创建会话,如果 Connector
的能量流已经停止,Charging Session
已经结束
无论它们如何耦合,Charging Session
实体似乎不属于 Charging Station
聚合,因为它没有回答主要问题:删除站时,它的 Charging Session
是否也应该被删除?)另外,当我支付本次会话中消耗的能量时,它与站点聚合上下文没有任何关系。
我想创建一个 SessionCreator
域服务,以确保当 Charging Station
的 Connector
解锁时,也会创建 Charging Session
:
type SessionCreator interface {
CreateSession(station *station.Station, connectorID int, sessionID int) error
}
type sessionCreator struct {
stationClient StationClient
}
type (svc sessionCreator) CreateSession(station *station.Station, connectorID int, userID string, sessionID int) error {
err := station.UnlockConnector(connectorID, svc.stationClient)
if err != nil {
return err
}
session.Create(sessionID, station.ID, connectorID, userID)
return nil
}
然而它只是感觉有点奇怪,并且不能完全满足其他不变量(当连接器的能量流停止时,它必须结束会话),我虽然也制作了一个事件监听器来监听StationConnectorUnlocked
事件,但我只是不知道哪个是理想的方式。
根据我对问题的理解,我认为您的聚合 Charging Station
和 Charging Session
是正确的。你说他们都是耦合的,但在我看来 Charging Station
不需要知道 Session 的存在,而且 Session 也不需要知道 Station 内部的任何信息,所以对我的耦合度很低。
关于你的问题,当Station发生事情时如何创建和修改Session,根据你的措辞,我觉得解决方案很清楚:
if the Connector has been unlocked by a user, a session was created, if the Connector's energy flow has stopped, the Charging Session has ended.
突出显示的词是业务事件。你可以这样画:
- ConnectorUnlocked --> ChargingSessionCreated
- ConnectorEnergyFlowStopped --> ChargingSessionEnded
我的实现方式是使用 pub/sub 系统,因此聚合会在状态更改和订阅者触发用例后发布事件,这可能会对聚合执行操作,这可能会结束发布一个事件。
这种方法的分布式特性使得跟踪整个系统上发生的事情变得有点困难,因为解锁连接器和创建会话不会在一个地方发生。它们可能会发生在系统的两个不同部分。优点是,随着时间的推移,利益相关者可以想出连接器解锁时需要发生的许多事情,而您只需要继续向该现有事件添加订阅者。