1

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?

2
  • 1
    Chain of Responsibility introduces complexity; it's a better fit when there already is an object structure that has pathways that form a chain (e.g., a tree of some sort). "problem with option 1 is, it doenst abstract out preprocessors and postprocessors very well" - then come up with some abstractions for those, and structure the rest around that. Commented Aug 7, 2019 at 19:27
  • My main concern with option 1 was "will it allow me to add new preprocessor and postprocessor without touching any existing code. In option 2 I can add a new per or post processor to the chain and easily extend the functionality".
    – srini
    Commented Aug 19, 2019 at 21:06

2 Answers 2

1

Option 1 is good, don't pass the context around, it will tightly bind your UI to the logic.

Think in the following way :-

Your UI components should not know of what is happening when the User enters the Data

UserAccessInformation
extractAccessInformation and then pass all this to a class where Pre Processing  will happen, on the data.

UserDataProcessing
Here check the access privileges, and see if you need to hit the API, if yes, then preprocess the data, and pass this to the CallAPI class.

CallAPI
Here we get the data from above class and call the API and return the result. 
(HOW) This result can only be returned to the UserDataProcessingClass, you will have to make a asynchronous call to the API. 
To get the result, make an interface with abstract method finalResult and pass the data from this class to the finalResult method, and implement this interface in the UserDataProcessing Class. 

Now we got the result in the UserDataProcessing, do Post Processing here. 
The result of this, you can send to UserAccessInformation class where a method to show UI change will be there

Between look in RxJava, It will really help your use-case, where you can transform the data in the post-processing part and then even if you have to call another API, on the data transformed, from 1st API, you can do so easily and quickly

EDIT :- I may be wrong, but we can discuss about it here, and what are you using for Dependency Injection. And have you heard of MircroProfile, seems to be best way for achieve Inversion of Control (IOC) https://www.ibm.com/cloud/blog/migrate-java-microservices-from-spring-to-microprofile-p2

1
  • 1
    This is a web service with rest api which is talking to another querying infrastructure. Let me take a look at your example.
    – srini
    Commented Aug 19, 2019 at 21:08
1

In your flow, I don't see multiple handlers with option for one of them to handle the flow based on condition. It is just a sequence of steps. Generally Chain of Responsibility is used when you have a priority order(not necessarily priority, however there is a sequence) of handlers and each decides whether to handle it there or pass it on. You can definitely chain the modules, but you are not helping in anyway if the sequence of the module is rigid.

Rather go ahead with option 1 with proper abstractions and you should be fine.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.