I regularly have the situation that I have some complex logic that I break into steps. For example an import of an Excel file where I need to apply some logic or a complex calculation that I break into smaller calculations.
Another situation is shown below. This is one big User Story: As a user I want to show the graphs. So I break this into steps:
- Get a scenario domain model from database based on a given id.
- Load data from external weather API: based on the location property of the scneario loaded by step 1 (Location has lang and lat properties);
- Save the weather data from step 2 into the database. We also need the scenario of step 1, so I can store the weather data to the specific location.
- Perform calculations based on the data I get from step 2 and 1. This is very complex calculator. Scientists have made many calculations and I have translated them into C#. The outcome of all those calculations are based on hourly weather data.
- Based on the outcome of step 4 and 1 aggregate values by month. So we go from hourly calculations to monthly calculations.;
- Save the data genereted in step 4 and 5. This data is stored because the calculation is step is heavy and when the data is wrong we can use the stored data to debug and find out why the data is wrong.
- Generate graph data based on the outcome of step 6. The graphs (10 of them) are based on the monthy values, which are calculated in step 5;
- Save the genereted graph data with data from step 1. I Store the genereted outcome so when we need to show the same graphs next time, I don't have to calculate everything again.
As you can see, each step requires the outcome of a previous step, but every step needs the data from step 1.
What I usually do in these situations is to create a Context
object. This object contains properties for each step. So each step, sets it's property on the context. And off course this context is passed to every step. So in the case above I have a context
class with 5 properties (the saving steps don't set anything on the context).
Next, every step implements this interface:
public interface IStep
{
void Execute(Context context);
}
Then in the root class I manually compose a List with all these steps and finally I loop over this list so every step gets executed.
I don't feel very happy with this and I was wondering if this can be done smarter. It doesn't feel SOLID:
- chances are that values on the context are null;
- for every step I have to modify the context class;
- context class can become very large;
- not solid at all...
So how would you handle such a scenario?