I am working on refactoring a project code that is wired up in a tangled way. It was started with decent dependency injection and over time with all custom requirements it looks it got tangled up. This is the outline of the service functionality
1. Get the request from the user
2. Infer some business logic based on the user and request information - Determine user access privileges, get data from external services to help with building the query, etc
3. build a complex query to send to backend
4. execute the query by sending it to backend and get the results
5. Any postprocessing that needs to be done on the results (do some last mile filtering of results, decorate it with more data, etc)
6. Build a response using the results we got from line 5 and return it back
I have two slight variants in mind now and i would like to get some suggestions on which option is better.
Option 1
Break down some objects by following single responsibility principle and then wire up the objects in a simple way
RequestObjectBuilder
UserAccessInformation
extractAccessInformation
BusinessLogic1Extractor
extract business logic by calling external services
BusinessLogic2Extractor
extract business logic by calling external services
QueryBuilder - use all dependencies and input and build the query (Query Building would be broken down into pieces and dependencies would be injected where needed)
UserAccessInformation
BusinessLogic1Extractor
BusinessLogic2Extractor
String buildQuery(RequestInput request)
ResponseBuilder
PostProcessingBusinessLogic1
buildResponse()
Option 2
Break down some objects by following single responsibility principle and chain them together using chain of responsibility pattern
PreprocessorChain - order of preprocessor matters a lot when one is dependent on some fields populated in context by other
void run(List<Preprocessor> processes)
Preprocessor - each preprocessor will read input and context and update the context back
void preprocess(RequestContext context, RequestInput request)
QueryBuilder - read the context and input and build the query
String buildQuery((RequestContext context, RequestInput request)
PostProcessorChain
void postprocess(ResponseContext context, ResponseOutput response)
ResponseBuilder
String buildResponse(ResponseContext context, ResponseOutput response)
I feel that option 2 would give me flexibility but I dont like the idea of passing around a context object and use that as both input and output. THe problem with option 1 is, it doenst abstract out preprocessors and postprocessors very well. Should i go for a hybrid approach ? Like any other software we are constantly adding and removing logic, what is a good design that will fit this use case?