Реализация Saga с постоянным хранилищем в .net. Альтернатива Masstransit Saga?

Я использовал Automatonymous State Machine с MassTransit. Мне понравилось работать с этой машиной состояний / саг, особенно тем, как она была сконфигурирована и настроена, а также тем, что я могу кормить конечный автомат событиями, реализующими контракты, которые будут использоваться в качестве сообщений.

Вот как это может выглядеть:

 //define the statemachine with a State class (ServiceState)
 public class ServiceStateMachine :
    AutomatonymousStateMachine<ServiceState>{

    //define available states
    public State Available { get; set; }
    public State WaitForItem { get; set; }

    //define available events
    public Event<RequestItem> RequestItem { get; set; }

    //configure the state machine and configure the store to use the ServiceState class
    public void ConfigureStateMachineCorrelations(StateMachineSagaRepositoryConfigurator<ServiceState> r)

     //bind events to contracts and conditions
     r.Correlate(RequestItem,
            (state, message) =>
                state.CorrelationId == message.CorrelationId)
    }


    public ServiceStateMachine(IStateMachineActivityFactory activityFactory
    {
         State(() => Available);
         State(() => WaitForItem);

         Event(() => RequestItem);

         //bind states, events, activities, custom actions...             
         During(Available,
            When(RequestItem)
                .Then((state, message) =>
                {
                    state.ServiceId = message.ServiceId; // just an example baby!
                })
                .TransitionTo(WaitForItem)
                .Then(() => _activityFactory.GetActivity<RequestItemActivity, ServiceState>())
    }

Какие существуют альтернативные реализации Saga, которые похожи, но не связаны с архитектурами MQ? Я предполагаю, что на самом деле я ищу конечный автомат или реализацию саги, по крайней мере, с постоянным хранилищем в памяти.


person xoda    schedule 26.06.2014    source источник
comment
Вы можете использовать Automatonymous отдельно от MassTransit. Фактически, интеграция NHibernate также является отдельной библиотекой.   -  person Chris Patterson    schedule 20.07.2014
comment
@ChrisPatterson Не могли бы вы ответить на этот вопрос примером. У меня более или менее тот же вопрос, что и у OP, но мне трудно найти полный пример настойчивости. Спасибо!   -  person Anttu    schedule 24.02.2015


Ответы (1)


Вы можете использовать Automatonymous полностью независимо от MassTransit (или любой другой системы обмена сообщениями). Существуют методы (и функции подъема для выделения конечного автомата или события) для создания событий с данными или без них.

_machine.RaiseEvent(instance, x => x.RequestItem, itemData);

Реализации простого конечного автомата и экземпляра не имеют понятия хранилища состояний (экземпляров), которое зависит от приложения. Например, вы можете ввести словарь в память или использовать сборку NHibernate, которая упрощает сохранение экземпляра состояния в базе данных SQL (сборка в основном включает помощников для сопоставления свойства CurrentState, а также несколько других настраиваемых типы.

Это пример, запрошенный в Интернете, который я недавно написал как модульный тест для последней ветки (mt3) Automatonymous:

class PhoneStateMachine :
    AutomatonymousStateMachine<PrincessModelTelephone>
{
    public PhoneStateMachine()
    {
        InstanceState(x => x.CurrentState);

        State(() => OffHook);
        State(() => Ringing);
        State(() => Connected);
        State(() => OnHold, Connected);
        State(() => PhoneDestroyed);

        Event(() => ServiceEstablished);
        Event(() => CallDialed);
        Event(() => HungUp);
        Event(() => CallConnected);
        Event(() => LeftMessage);
        Event(() => PlacedOnHold);
        Event(() => TakenOffHold);
        Event(() => PhoneHurledAgainstWall);

        Initially(
            When(ServiceEstablished)
                .Then(context => context.Instance.Number = context.Data.Digits)
                .TransitionTo(OffHook));

        During(OffHook,
            When(CallDialed)
                .TransitionTo(Ringing));

        During(Ringing,
            When(HungUp)
                .TransitionTo(OffHook),
            When(CallConnected)
                .TransitionTo(Connected));

        During(Connected,
            When(LeftMessage).TransitionTo(OffHook),
            When(HungUp).TransitionTo(OffHook),
            When(PlacedOnHold).TransitionTo(OnHold));

        During(OnHold,
            When(TakenOffHold).TransitionTo(Connected),
            When(PhoneHurledAgainstWall).TransitionTo(PhoneDestroyed));

        DuringAny(
            When(Connected.Enter)
                .Then(context => StartCallTimer(context.Instance)),
            When(Connected.Leave)
                .Then(context => StopCallTimer(context.Instance)));
    }


    public State OffHook { get; set; }
    public State Ringing { get; set; }
    public State Connected { get; set; }
    public State OnHold { get; set; }
    public State PhoneDestroyed { get; set; }

    public Event<PhoneServiceEstablished> ServiceEstablished { get; set; }
    public Event CallDialed { get; set; }
    public Event HungUp { get; set; }
    public Event CallConnected { get; set; }
    public Event LeftMessage { get; set; }
    public Event PlacedOnHold { get; set; }
    public Event TakenOffHold { get; set; }
    public Event PhoneHurledAgainstWall { get; set; }

    void StopCallTimer(PrincessModelTelephone instance)
    {
        instance.CallTimer.Stop();
    }

    void StartCallTimer(PrincessModelTelephone instance)
    {
        instance.CallTimer.Start();
    }
}

Он создается и вызывается (модель Princess является экземпляром состояния для примера), как показано ниже:

var phone = new PrincessModelTelephone();
await _machine.RaiseEvent(phone, _machine.ServiceEstablished, new PhoneServiceEstablished {Digits = "555-1212"});

await _machine.RaiseEvent(phone, x => x.CallDialed);
await _machine.RaiseEvent(phone, x => x.CallConnected);
await _machine.RaiseEvent(phone, x => x.PlacedOnHold);

await Task.Delay(10);

await _machine.RaiseEvent(phone, x => x.HungUp);

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

person Chris Patterson    schedule 25.02.2015