I have the opinion that each method in a service should only do one small step of a larger task, delegate a result to the successive step/method and terminate. No matter if this next method lives in the same service or not, it is always triggered via inter-service routing (message queue, service discovery + REST call, whatever ...) and thus perhaps handled by a different instance. Never should the method be called directly by default, unless selectively enabled.

I know, network communication is slower by orders of magnitude and complex tasks with a lot of steps will run milliseconds slower, but this is the only way I see that ensures that

  • each step is implemented stateless so that it can - if needed - be replaced in a different technology with no overhead
  • failure handling is least expensive as waiting for a response or doing a successive step in the same process increases the chance to loose intermediate step results if that process dies for whatever reason
  • definite failure rate is reduced as it is easier and more predictable to implement a standardized failure handling centrally than on method level

Do you think I'm overseeing something and what is your opinion?

  • I think it's OK, but only if your boundaries and abstractions are correct. Check out this post. – Zapadlo Jan 22 '18 at 9:26

The downside to using the message queue for communication between smaller and smaller functions is that the 'orchestration' of those messages becomes more and more complex.

What you gain by having smaller units of code to test and debug you lose in the opaqueness of the routing of the various messages.

  • What happens if message X times out or is received twice?
  • Should services A and B both listen for message Y?
  • We have upgraded service C, how many legacy messages are in the pipeline prior to it that need to be processed before we can switch off the old version?
  • Something happens to the message queue server/service, maybe an AWS availability zone goes down? how does this affect our in-progress chains of communication.

At some point this complexity becomes more than the saving you make with the shorter code.

I am generally in favour of Message Queues and Routing. But I've seen it taken to the n'th degree a couple of times and there is definitely a point where it becomes unmanageable, even if it seems super cool and all working when you first develop it.

If anything the error handling is much more complex than a simple in process method call. I'm surprised you pick that out as a positive.

  • Thanks for your answer!I'm thinking method I/O spec to be immutable. When expected input or output changes, it is copied and gets a new name like resizePicture -> resizePicture2 and the old version stays in new releases so that switch is trivial: For each existing instance, start a new one. Notify old instances to stop receiving messages, terminate when buffered work is done. No problem when someone still uses the old version. – Auf Zug Jan 21 '18 at 21:44
  • Regarding 1): The receiving instances are auto-scaled based on how long the queue is, so timeouts shouldn't happen if sufficiently long for high load when auto scaling is blocked by budget. Regarding 4) Each zone has its own queue cluster so when a whole zone goes down the others still operate. – Auf Zug Jan 21 '18 at 21:57
  • For Error Handling my thoughts are: heavyComputation() is called, which calls a couple of other methods in the same process and while the last one is running... DANG. Process dies. All the work is lost. When all of these methods would have been called through a message queue, all the intermediate results were persisted and it would be trivial to re-schedule just the missing step(s). Am I wrong? – Auf Zug Jan 21 '18 at 22:01
  • hmm, I think you are missing some of the subtleties of these problems. so for example when your heavy process dies the intermediate state was in a message that was consumed by the process that crashed. You need some cleverness if you want to pick that message up again. Similarly with the timeout, for all the calling process knows the message is still being processed somewhere. do you send another or wait longer? – Ewan Jan 21 '18 at 22:06
  • 1
    @9000 sure there are ways you can deal with it, but they are are much more complicated than just running the code locally with a try catch – Ewan Jan 22 '18 at 16:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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