shrink0r/workflux

How would you add state observers?

MrHash opened this issue · 15 comments

I'm guessing you could extend the execution context and add state change observers (e.g. for persistence) but is this an intended feature of the library?

You correctly spotted out the execution context as the entity, that needs to be persisted in order to support suspend/resume like features.
Workflux itself does not know about persistence concerns.
As the ExecutionContextInterface is exposed to the StateMachineInterface by a given StatefulSubjectInterface, most likely implementing both the StatefulSubjectInterface and ExecutionContextInterface will be required in order to integrate.
With integrating I mean that the $subject passed to the $state_machine->execute($subject, $event) call, will be a custom implementation within an existing model.

Atm I am using the lib to manage workflows within a bigger application.
In order to integrate I added the interface implementations mentioned above to the domain model.
In case of the StatefulSubjectInterface I was able to add the interface to an existing model and for the ExecutionContextInterface I introduced a new class (inheriting from a class within the app domain, so I couldn't extend the workflux execution context).
The actual persistence is done by persisting the $subject after the state machine execution.

If you would like to attach observers, spontaneously I can think of two approaches:

  • add a custom state implementation and override the onEntry and onExit methods, to trigger the notifications
  • if you decide to implement a custom ExecutionContextInterface the the onStateEntry and onStateExit methods would be good fit I guess
    I hope this helps a bit.
    If you like, drop by the #honeybee channel on freenode(irc) to chat on the subject.

tldr:

  • Workflux does not support persistence.
  • You can integrate by implementing the StatefulSubjectInterface and ExecutionContextInterface, where your StatefulSubjectInterface implementation is probably what you want to persist.

Thanks for the comprehensive reply. My feeling is that the best place to define state observers is in an implementation of the StateInterface as you suggested as your first solution. This way specific states can trigger custom application notifications. The ExecutionContext approach may not be granular enough in my opinion.

Do you think support for state observers is something the package should provide? Persistence is just one example of an obvious use case. Others may be an api callback or email send on final state for example.

Yes, I think workflux could benefit from incorporating some kind of notification/event/observer system.
Are you thinking of something specific like Symfony/EventDispatcher?
What would your requirements be, if workflux were to introduce such a feature?

I had no specific implementation idea right now although the Symfony event dispatcher could work. I would imagine support for these events would be most useful:

  • OnInitial()
  • OnActive()
  • OnStatePreEntry()
  • OnStatePostEntry()
  • OnStatePreExit()
  • OnStatePostExit()
  • OnFinal()

An ObservableState class could be defined to support observer registration, or it could be offered as standard into the core State implementation. I would be happy to have a play around and give a more detailed idea when i prototype my particular use cases.

+1 for the ObservableState
I'll create a feature ticket for it and will see if I can get this done tonight.
While looking at the desired events, what you probably want is an ObservableStateMachine (#26) ?

So I ended up with an EventEmittingStateMachine prototype :)
@MrHash Is this heading in the right direction from your POV?

Yeh i guess that make sense because the configuration remains simpler. The only question then is about how to registering event listeners. Would that be possible through the XML?

Hmm, I don't really know how to support registering events by xml, because your limited to static methods only then, right?
That wouldn't provide much benefit from my POV. 👀
As the xml builder is the one, that creates the state machine (when building from xml), you could extend the xml builder and pass it in an (existing) event emitter instance that is known by the other components, that want to be notified about state machine events.
The xml builder would then use the given event emitter to initialize the state machines it builds.
https://github.com/shrink0r/workflux/blob/observable-statemachine-26/src/StateMachine/EventEmittingStateMachine.php#L70
Does that sound legit?

P.S.:
If there was a symfony bundle for workflux, then you could wire this stuff inside the services.xml I guess.
Maybe some day somebody will write one. :)

Looks really neat! Ideally it would be to nice to write multiple generic event listener classes (eg. PersistenceEventListener and LoggingEventListener) which have a number of methods which are registered dynamically as on event handlers. I'm not sure how this can be done right now without having a deeper play with the code. I guess it would be some kind of Chain of Responsibility pattern.

Yeah, getting some feedback from someone else who has also fiddled with the code would be awesome.
I am not sure if events suite the persistence concern well though.
If you already have models that are persisted, then the putting the execution context there could be a good idea, as mentioned in the earlier comments ♻️
I'm excited to hear if you could integrate without too much trouble. :)
I guess you are using doctrine to persist your domain objects?

Yup ok i'll have a play and see what i come up with. I am using the Doctrine/Search package with ElasticSearch. In my use case tho i would probably like to use Redis in parallel to store states for service scalability so there's a few listeners that would work in parallel and keep things in step.

Have been having some trouble installing this because i am using https://github.com/PHP-FFMpeg/PHP-FFMpeg and https://github.com/alchemy-fr/BinaryDriver both of which have a dependency on evenement 1.0. I forked and updated both libs and updated dependency to 2.0 and tests pass fine for both packages, but still i get package resolution problems when using your branch and php-ffmpeg together :(

Can you post the error please?

Just managed to hack the composer to get it loaded so i'll take a look at my use case now..

👯