Библиотека государственной машины без гражданства - соответствующий способ структурирования?

Как люди структурируют свой код при использовании библиотеки С# stateless?

https://github.com/nblumhardt/stateless

Я особенно заинтересован в том, как это связано с инъецированными зависимостями, а правильный подход к ответственности и правильному расслоению.

Моя текущая структура включает в себя следующее:

public class AccountWf
{
    private readonly AspNetUser aspNetUser;

    private enum State { Unverified, VerificationRequestSent, Verfied, Registered }
    private enum Trigger { VerificationRequest, VerificationComplete, RegistrationComplete }

    private readonly StateMachine<State, Trigger> machine;

    public AccountWf(AspNetUser aspNetUser, AccountWfService userAccountWfService)
    {
        this.aspNetUser = aspNetUser;

        if (aspNetUser.WorkflowState == null)
        {
            aspNetUser.WorkflowState = State.Unverified.ToString();
        }

        machine = new StateMachine<State, Trigger>(
        () => (State)Enum.Parse(typeof(State), aspNetUser.WorkflowState),
        s => aspNetUser.WorkflowState = s.ToString()
        );

        machine.Configure(State.Unverified)
        .Permit(Trigger.VerificationRequest, State.VerificationRequestSent);

        machine.Configure(State.VerificationRequestSent)
        .OnEntry(() => userAccountWfService.SendVerificationRequest(aspNetUser))
        .PermitReentry(Trigger.VerificationRequest)
        .Permit(Trigger.VerificationComplete, State.Verfied);

        machine.Configure(State.Verfied)
        .Permit(Trigger.RegistrationComplete, State.Registered);

    }

    public void VerificationRequest()
    {
        machine.Fire(Trigger.VerificationRequest);
    }

    public void VerificationComplete()
    {
        machine.Fire(Trigger.VerificationComplete);
    }

    public void RegistrationComplete()
    {
        machine.Fire(Trigger.RegistrationComplete);
    }

}

Должны ли мы реализовывать все процессы (вызов служб) внутри крюка OnEntry или реализовывать процессы снаружи после проверки состояния, что это разрешено? Мне интересно, как это сделать при управлении транзакциями.

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

Ответ 1

Перед рассмотрением самой структуры пара замечает:

  • OnEntry действия выполняются только , если триггер успешно запущен.

  • Триггеры, которые не разрешены в текущем состоянии, будут бросать InvalidOperationException. Рассмотрите переопределение OnUnhandledTrigger, если вы не ожидаете исключения (я обнаружил, что ведение журнала необработанных триггеров - хороший подход к поиску недостатков в логике).

Мое правило для структурирования OnEntry/OnExit заключается в том, что любое создание и логика будут размещены OnEntry, и всякая необходимая очистка выполняется OnExit.

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

Учитывая это, способ, которым ваш государственный аппарат в настоящее время структурирован, отлично.

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