1

Я пытаюсь реализовать простую архитектуру DDD/CQRS без использования событий-источников.Где я должен поместить логику для запроса дополнительных данных в потоке команд CQRS

В настоящее время мне нужно написать код для добавления уведомления к объекту документа (документ может иметь несколько уведомлений).

Я уже создал команду NotificationAddCommand, ICommandService и IRepository.

Прежде чем вставлять новое уведомление через IRepository, я должен запросить текущий user_id из db, используя свойство NotificationAddCommand.User_name.

Я не знаю, как сделать это правильно, потому что я могу

  1. Использование IQuery от чтения потока.
  2. Передайте имя пользователя домену и разрешите user_id в репозитории.

Код:

public class DocumentsCommandService : ICommandService<NotificationAddCommand> 
{ 
    private readonly IRepository<Notification, long> _notificationsRepository; 

    public DocumentsCommandService(
     IRepository<Notification, long> notifsRepo) 
    { 
     _notificationsRepository = notifsRepo; 
    } 

    public void Handle(NotificationAddCommand command) 
    { 
     // command.user_id = Resolve(command.user_name) ?? 
     // command.source_secret_id = Resolve(command.source_id, command.source_type) ?? 
      foreach (var receiverId in command.Receivers) 
      { 
       var notificationEntity = _notificationsRepository.Get(0); 
       notificationEntity.TargetId = receiverId; 
       notificationEntity.Body = command.Text; 
       _notificationsRepository.Add(notificationEntity); 
      }    
    } 
} 

Что делать, если мне нужно более сложную логику, прежде чем вставить? Можно ли использовать IQuery или создавать дополнительные службы?

+0

Обработчик команд должен быть введен другим специализированным сервисом. Кстати, ваш код очень странный. Я уверен, что он не отражает вообще язык экспертов домена, когда они описывают вариант использования «добавить уведомление». Во всяком случае, даже «добавление» уведомления не имеет смысла ... уведомление позволяет вам узнать о чем-то, что произошло, например о событии. Вы пытаетесь переписать историю? – plalx

+0

Уведомление похоже на сообщение о чем-то, оно содержит свойства notificationBody и notificationTargetUserId –

ответ

0

В проекте, в котором я работаю, у меня есть аналогичные проблемы. Я вижу 3 варианта решения этой проблемы

1) Что я сделал, так это сделать UserCommandRepository, у которого есть опция запроса. Затем вы будете вводить этот репозиторий в свою службу.

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

2) Другой способ обращения с ним - это заставить пользователя просто поднять команду с помощью user_id. Тогда вы можете позволить ему сделать запрос.

3) Третий вариант - спросите себя, почему вам нужен user_id. Если при запросе данных вы также можете обрабатывать данные, вы можете также обрабатывать данные (или при распространении вашего writeDB на ваш readDB)

1

Идея повторного использования вашего IQuery несколько поражает цель CQRS в том смысле, что ваша читающая сторона должна быть оптимизирована для вытягивания данных для целей отображения/запроса - это означает, что ее можно денормализовать, распределить и т. д. любым способом, который вы сочтете необходимым без, ограниченным - или имеющим последствия для - командной стороны (ключевым примером является то, что он может быть не сразу последовательным, в то время как ваша командная сторона, очевидно, должна быть для целей целостности/действительности).

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

public DocumentsCommandService(IRepository<Notification, long> notifsRepo, 
           IUserIdResolver userIdResolver) 

public interface IUserIdResolver 
{ 
    string ByName(string username); 
} 

С IUserIdResolver реализован в зависимости от обстоятельств.

Конечно, если и это, и сторона запроса используют ту же реализацию доступа к данным низкого уровня (например,сразу-согласованный репозиторий), это прекрасно - важно то, что ваша архитектура такова, что если вам нужно поменять местами, где ваша сторона чтения получает свои данные для целей, например, облегчая медленный автономный процесс, ваши стороны чтения и записи достаточно разделены, что вы можете поменять местами, из которых вы читаете, без необходимости распутывать записи из записей.

В конечном счете, самое главное, чтобы знать, почему вы делаете архитектурные решения, которые вы делаете в вашем сценарии - тогда вам будет гораздо легче сделать такого рода решения, так или иначе.

+0

У меня есть ваша точка зрения и согласны с тем, что стороны чтения и записи должны быть разделены. –

+0

@Batavia Как я понимаю, IRepository не должен содержать общие методы запросов, но только получить/обновить/вставить/удалить агрегированные корневые методы. Таким образом, возможно, любая исключительная логика должна быть помещена в соответствующие небольшие службы (IUserIdResolver и т. Д.) –

+0

Вы правы в том, что не имеете общих методов запросов, но именно поэтому вы создаете очень специфический репозиторий, содержащий очень специфический метод запроса, который не отображается на ваш общий интерфейс, но только в вашей реализации – Batavia