1

I have recently started working on a project where we have a workflow engine which has flexibility to add dynamic states and corresponding actions for each state and all these are stored in database.

For example:

Workflow Name: Make breakfast
Workflow ID : 143


Workflow States
state 1 : Turn on stove
state Id 1121
workflowid 143
state 2 : make breakfast
state Id 1122
workflowid 143

and then there are actions that user can perform when in each state

Action Name: Approve
Action Id: 0098
belongstostate: 1121

Action Name: Reject
Action Id: 0099
belongstostate: 1121
....

Now the code works by using amazing if else statements and mapping everything to enums which is a mess.

I was thinking of applying strategy pattern to fix the issue but then we cannot as everything is dynamic here. we can have n number of workflows,states and actions.

what is the best way to tackle this issue?


Edit
The major problem is that we have to manually add the id of corresponding states,actions in side the enum which means that it will change with each environment too. And we have to use if state == enumofstate then do this else do that

What I am looking for, may be, is something like "state machine pattern" but dynamic, is there anything like that?

digi
  • 111
  • 2
  • 2
    I'm confused, why would there be if statements? If it is 100% dynamic, everything you need to know concerning order, approval, and execution should be saved to the database to some extent. Also, if you're saving states to the database, enums should not be used, because by definition it's dynamic, not static! – Neil Mar 29 '18 at 08:09
  • Possible duplicate of [Best approach - convert multiple conditional if -else in a more handy design](https://softwareengineering.stackexchange.com/questions/366803/best-approach-convert-multiple-conditional-if-else-in-a-more-handy-design) – gnat Mar 29 '18 at 08:21
  • @neil By dynamic i meant that it can be modified from the db and each time its modified we have to go edit the enum manually to accommodate the change. – digi Mar 29 '18 at 08:41
  • @digi This is a big no no. The reason why you'd save it to the database is to process information dynamically. If you add dependency to a hard-coded aspect of your program, you've gained no advantage. The state string, *not* the state enum should be the distinguisher in your program. – Neil Mar 29 '18 at 09:02
  • @Neil thanks I know that's bad but I dont know how to correct it. Let's suppose I use string to distinguish different states, I will have to use magic strings (another wrong thing) for such a comparison `if (state =="make breakfast"` send email to a person. This is the issue that I want to solve. – digi Mar 29 '18 at 10:11
  • You're right, that would be equally bad. But this is what I mean by having all the information you require on the database. If "make breakfast" involves sending an e-mail, then on the database somewhere should be associated the task "send e-mail" to "make breakfast". It's the essential difference between hard coding and writing dynamic code. Program *either* uses enums *or* depends on database for behavior. Not both. – Neil Mar 29 '18 at 10:50

1 Answers1

1

Well. You can fix your immediate problem by changing your Ids to strings and populating them with the Enum name

This means you can read the database without having the code open to translate the numbers.

Longer term, @Neil is right, you should either move everything to code or everything to the database. But there might be an intermediate stage where you process the database with 'workers'

Say you want to send an email when the 'make breakfast' action has been taken. You can have a program which looks through the db for the 'MakeBreakFast' action Id, send emails and mark that action done.

Now when you add a new action 'MakeLunch' nothing happens, until you write a new worker which looks for the 'MakeLunch' actions. Because it checks to see if they have been done before actioning you can run it and it will pick up on missed ones.

You can add a simple unknown action check, which loops through all actions looking for ones which have no corresponding enum and raises an alert.

This pattern means that you don't have to change existing code when you add new actions. You just make a new worker.

In effect you have made some processes fully in the database. Anywhere I put a MakeBreakfast action it will work with no code changes.

Maybe you can join 'MakeBreakfast' and 'MakeLunch' into a single 'SendEmail' action with a parameter 'Lunch' or 'BreakFast'?

Hopefully you will gradually work through all the actions until they are generic enough that new processes can be defined from the pool of existing actions.

Ewan
  • 70,664
  • 5
  • 76
  • 161