We have to make a bunch of HTTP calls from Java/Spring-Boot application which will be mix of in-sequence and parallel.

Level 1 : We make 3 parallel calls to Services 1 , 2 and 3

Level 2: After service 1 response is received (which is a list), we make parallel calls to service 4 and 5 for each item in list

Level 3 : After service 5 response is received, depending on its response, we make parallel calls to Service 6 for few of items received from service 1.

Which design pattern should be used? Will the Command design pattern would be a good use case here?

I was thinking, may be we create a DataProvider having 2 properties isParallel and httpService. And then I ccreate objects of DataProvider and set these 2 attributes for each of the HTTP call we want to make. Does it make sense?

  • 2
    Does this answer your question? Choosing the right Design Pattern
    – gnat
    Commented May 11, 2021 at 4:22
  • Thanks @gnat for pointing me to this question. Though, it doesn't exactly answers my question but it points in the right direction.
    – Smile
    Commented May 11, 2021 at 4:26
  • 1
    It sounds like a pipeline to me.
    – Laiv
    Commented May 11, 2021 at 7:46

2 Answers 2


What you're maybe looking for is simply "async programming" or non-blocking programming. The default tool to use in Java for that is CompletableFuture, unless you are using a different tool explicitly (like reactive libraries, or akka, etc.)

Technically that boils down to:

  1. Converting all network calls to return CompletableFuture, for those that do not already do that. Like calls that use callbacks, or return some 3rd party objects for futures.
  2. Then composing these calls in the appropriate manner.


// Level 1: 3 services in parallel
CompletableFuture.allOf(service1(), service2(), service3());

// Level 2: Make parallel calls to 4 and 5
public CompletableFuture<?> service1() {
   // After calling service 1, call 4 and 5 in parallel with results
   return callService1().thenCompose(items ->
      CompletableFuture.allOf(service4(items), service5(items)));

// Level 3: Call service 6 conditionally
public CompletableFuture<?> service5(List<Item> items) {
   return callService5(items).thenCompose(response -> {
      if (response.isSomething()) {
         return callService6(items);
      } else {
         return CompletableFuture.completedFuture(null); // No-op

That's about it. Of course this is not a real design, it's just the rough technical steps involved, but seeing how it's done might help to imagine a design for it.

  • Thanks for the answer. I will definitely try this out and will try to post the implementation in few days for others' benefit.
    – Smile
    Commented May 11, 2021 at 11:29

Classing example of orchestration flow. Orchestration flow most of the time utilizes Command pattern very well along with Aggregator. But like in the link @gnat provided, Patterns are prescribed solution to a specific software design problem. Identifying problem is most important in choice of design pattern.

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.