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.
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()
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?