Fork me on GitHub
#architecture
<
2018-02-22
>
manderson19:02:21

@drewverlee I'm a bit late to the conversation here, but what you described above sounds similar to an event driven FSM I wrote a while back. We used it in one of our applications for a time that had a complex business logic workflow and the FSM abstraction made it easier to comprehend/represent. We ended up scrapping that part of the application for other reasons, but the code remains in one of our OSS projects: https://github.com/LiaisonTechnologies/proletariat/blob/master/src/proletariat/fsm.clj. It has a good bit of documentation. Might at least generate some ideas...

drewverlee21:02:51

@manderson very interesting i’ll take a look. The description seems close to what i imagine. I’m curious why i dont see things modeled this way more often. I want a more declarative way to model the possible states an application can be in.

manderson22:02:56

Yep, that's what lead me to that approach and it actually made for really easy to visualize code (and made me wonder if I was missing something because it isn't more prevalent). One problem is that it's hard to test if your events have side effects (eg: calls to a DB), so I added a multi-mock namespace in the same repo above for mocking multi-methods. It's not ideal but served its purpose.

drewverlee14:02:43

> One problem is that it’s hard to test if your events have side effects (eg: calls to a DB), so I added a multi-mock namespace in the same repo above for mocking multi-methods. This is interesting but i’m not sure i understand. I was thinking about having the state flow processor put events on a event log. In cases where processing these events meant triggering a side effect I would declear just record the intent to fire the site effect. Here display board would print a screen. I’m not actual going to trigger that action when testing. [{:action :display-board*}] Here get-user-input* would potentially read input from the command line (or wait for input from a client/browser). In testing, I would mock the side-effect (clojure spec has facilities for this) part to just generate data mostly valid but some invalid. [{:action :get-player-input*}] Does that make sense? how does that compare with what you were suggesting?

manderson17:02:12

In my case, I was using the fsm/auto-event multi-method to determine what processing step to take next. In our system, we were receiving a document off a queue and then making some calls to different downstream systems based on some business logic. We would then handle the response and invoke a "success" path or "exception" path (by returning the next step in the FSM as a response). In testing, I needed a way to test the different scenarios, so I used multi-mock to mock the outputs of the fsm/auto-event multi-methods. Looking back, I probably should have organized the system a bit differently so that I could have replaced the side-effect calls more easily, but that was what I came up with at the time (this was very early days of spec).

manderson17:02:35

I think your approach sounds good. As I said, I should have kept the side effects at the edges and allowed an easy way to mock results for testing

drewverlee20:02:26

Thanks for the response. I’ll have to dig into your library. My system at the moment is self syncronsis in nature (or at least i can make it that way). So i think it varies for your setup in that way. I also realized that because i dont have multiple subscribers to my events (the state log) i dont really need to record that the actions were completed. When i’m done i’m going to turn this adventure into a mini blog post mainly to facilitate feedback 🙂. Its just a simple tic tac toe game, bu i like trying to solve the ugly parts of small problems because i figure if i cant do it on the small things i have no hope when things get bigger.

ivan.kanak_clojurians22:02:58

luna-lang is all around that concept

drewverlee14:02:49

I’m going to give Luna Lang a try. I dont think i would want to write my programs complete in (what im calling) a state flow pattern. Just the high level parts and probably only in cases where things are very stateful and can loop back on themselves conceptually (like game-over -> reset-game -> etc…)