Вопрос по architecture, .net, unit-of-work, dependency-injection, c# – Вызов команд из другого метода Handle ()

7

Привет я пользуюсьПростой инжектор Библиотека DI и следит за некоторыми действительно интересными материалами об архитектурной модели, разработанной вокруг шаблона команды:

Meanwhile... on the command side of my architecture Meanwhile... on the query side of my architecture

Контейнер будет управлять временем жизниUnitOfWorkи я использую команды для выполнения определенных функций в базе данных.

У меня вопрос, есть ли у меня команда, напримерAddNewCustomerCommandкоторый, в свою очередь, выполняет другой вызов другой службе (то есть отправляет текстовое сообщение), с точки зрения дизайна это приемлемо или это должно быть сделано на более высоком уровне, и если да, то как лучше это сделать?

Пример кода ниже:

public class AddNewBusinessUnitHandler
    : ICommandHandler<AddBusinessUnitCommand>
{
    private IUnitOfWork uow;
    private ICommandHandler<OtherServiceCommand> otherHandler;

    AddNewBusinessUnitHandler(IUnitOfWork uow, 
        ICommandHandler<OtherServiceCommand> otherHandler)
    {
        this.uow = uow;
        this.otherHandler = otherHandler;
    }

     public void Handle(AddBusinessUnitCommand command)
     {
        var businessUnit = new BusinessUnit()
        {
            Name = command.BusinessUnitName,
            Address = command.BusinessUnitAddress
        };

        var otherCommand = new OtherServiceCommand()
        {
            welcomePostTo = command.BusinessUnitName
        };

        uow.BusinessUnitRepository.Add(businessUnit);

        this.otherHandler.Handle(otherCommand);
     }
}

Ваш Ответ

1   ответ
16

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

Тем не менее, отправка текстовых сообщений, запись в базу данных, выполнение сложных вычислений, связь с веб-службами и все остальное, что вам нужно для управления бизнесом & apos; потребности должны быть выполнены в контексте этой команды (или, возможно, поставлены в очередь, чтобы произойти позже). Не до, не после, поскольку именно эта команда представляет требования в независимом от представления виде.

Это не означает, что обработчик команд сам должен делать все это. Вполне естественно перенести много логики в другие сервисы, от которых зависит обработчик. Так что я могу представить ваш обработчик в зависимости отITextMessageSender интерфейс, например.

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

Однако обратите внимание, что наличие глубокого графика зависимости вложенных обработчиков команд, зависящих друг от друга, может усложнить навигацию по коду, поэтому внимательно посмотрите на это. Может быть, лучше ввестиITextSessageSender вместо использованияICommandHandler<SendTextMessageCommand>, например.

Еще один недостаток, позволяющий обработчикам вкладываться, состоит в том, что это усложняет выполнение инфраструктурных задач. Например, при переносе обработчиков команд с декоратором, который добавляет транзакционное поведение, необходимо убедиться, что вложенные обработчики выполняются в той же транзакции, что и самый внешний обработчик. Я случайно помог мне с этим клиентом. Это не невероятно сложно, но требуется немного времени, чтобы разобраться. То же самое относится и к таким вещам, как обнаружение взаимоблокировок, поскольку они также выполняются на границе транзакции.

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

Итак, как вы препятствуете команде, отправленной из обработчика команд, выполнять эти тупики или декораторы транзакций?
Вам нужно будет либо запретить применение декораторов к вложенным обработчикам (что обычно очень трудно сделать с любым DI-контейнером), либо вы позволите самому декоратору обнаружить, что он уже выполняется в контексте транзакции. Другой вариант - дать вложенным обработчикам собственную абстракцию. Это упрощает применение декораторов только к внешнему обработчику.

Похожие вопросы