Как управлять двумя связанными агрегатами в DDD? - PullRequest
0 голосов
/ 30 апреля 2020

Я занимаюсь разработкой системы управления зарядной станцией Electri c, которая подключена к нескольким Charging Station с, и я в тупике. В этом домене я создал агрегат для Charging Station, который включает внутреннее состояние Charging Station (независимо от того, подключено ли оно, внутреннее состояние его разъемов).

Имеется UnlockConnector метод, для которого, чтобы точно уважать свое имя (а не быть anemi c), он отправляет запрос соответствующему 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 s?) Кроме того, я, когда я буду платить за энергию, потребляемую в этом сеансе, это не будет иметь ничего общего с агрегированным контекстом станции.

Я думал о создании доменной службы 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, но я просто не знаю, какой будет идеальным способ.

1 Ответ

1 голос
/ 01 мая 2020

Из того, что я могу понять из проблемы, я думаю, что ваши агрегаты Charging Station и Charging Session верны. Вы говорите, что они оба связаны, но мне кажется, что Charging Station не нужно знать о существовании Сессии, и Сессии на самом деле не нужно ничего знать о внутренностях станции, поэтому связь для меня низко.

Что касается вашего вопроса о том, как создавать и изменять сеанс, когда что-то происходит на станции, мне кажется ясным решение на основе вашей формулировки:

, если Connector был разблокирован пользователем , сеанс был создан , если поток энергии коннектора прекратился , сеанс зарядки завершился .

Выделенные слова: бизнес-события . Вы можете нарисовать это так:

  • ConnectorUnlocked -> ChargingSessionCreated
  • ConnectorEnergyFlowStopped -> ChargingSessionEnded

И способ, которым я бы это реализовал, заключается в система pub / sub, таким образом, агрегирует события publi sh после изменения состояния, и подписчики запускают сценарии использования, которые могут выполнить операцию над агрегатом, что, вероятно, в конечном итоге приведет к публикации события.

Распределенная природа этого подхода делает более трудным следить за тем, что происходит во всей системе, поскольку разблокировка соединителя и создание сеанса не происходят в одном месте. Скорее всего, они произойдут в двух разных частях системы. Преимущество состоит в том, что со временем заинтересованные стороны могут придумать много вещей, которые должны произойти, когда Соединитель разблокирован, и вам нужно будет только добавлять подписчиков на это существующее событие.

...