Best Practice Question : Service A needs to do a request on Service B and Service A needs to find out the result of that request


(franz) #1

Hi,

This is a design question. If let’s say I have a Service A which needs to request Service B to do some processing, and Service A needs to know the result of that processing, what would be the best way to implement this?

Note: My understanding of lagom’s preferred way of communication is via event topics over rest calls. Although both are available to lagom developers, communication via event topics makes the overall architecture more resilient vs communication via REST. If I misunderstood this, kindly correct me as well.

Option 1: Via Event Publication/Subscriptions
ServiceA publishes a ProcessingRequestedEvent to service-a-topic
ServiceB subscribes to service-a-topic
Once ServiceB sees a ProcessingRequestedEvent, it will do the processing, and outputs the result by publishing a ProcessedFinishEvent to service-b-topic.
ServiceA subscribes to service-b-topic, and waits for the ProcessedFinishEvent to find out what happened to its request.

Pro : It’s asynchronous and all communications are via event topics
Con : Introduces a cyclic dependency wherein ServiceA and ServiceB subscribes to each other. Although I think this is a “weak” circular dependency (because it is ServiceA-impl that depends on ServiceB-api, and it is ServiceB-impl that depends on ServiceA-api, it still introduces a conceptual cyclic dependency between two microservices)

Option 2: Via REST Calls
ServiceA invokes ServiceB’s REST API.
Once ServiceB has finished processing, it will respond to ServiceA’s request synchronously.
ServiceA will find out the result of that request via the REST call’s response

Pro : There are no cyclic dependency. Only ServiceA knows about ServiceB (i.e. ServiceB does NOT know about ServiceA)
Con : It’s a synchronous operation

Given that scenario, which is the lagom best practices to approach this? (if there are other options, kindly let me know as well).

Thanks!


(Alan Klikic) #2

@franz using topic based or service call based communication depends on the use case. Each have cons and pros in different use cases.

When i would generally categorize use cases for each communication then I would say topic based communication is for “asynchronus” functionality and service call is for “synchronous”.
Service calls could also be used in “asynchronous” functionality if used inside of readside processors.

In this context “asynchronous” functionality is the one that is fault tolerant and in that case time consuming.
“Synchronous” functionality is the one that requires immediate fault info.

From my expirience I trend to use, as often as possible, topic based communication for internal, service-to-service communication.
Even avoiding service calls for collecting detailed info from aggregate roots but building local views from each topics.
In this way services become decoupled in runtime (mainly unavailability of one does not affect another).
On the other hand I trend to use service calls for “outside” communication (UI, API,…)

I hope this helped :slight_smile:

Br,
Alan


(franz) #3

Thanks @aklikic

I prefer to do an asynchronous communication between my ServiceA and my ServiceB as well. However, while exploring that approach, I noticed that that would introduce a circular dependency between the two.

That is, ServiceA and ServiceB would have to subscribe to each other. That is, ServiceA publishes to service-a-topic and subscribes to service-b-topic, while ServiceB publishes to service-b-topic and subscribes to service-a-topic. That way, requests for processing can be published in service-a-topic, while the result of the processing can be published to service-b-topic.

Is that a typical lagom pattern and is that acceptable? or is there a way to do communication via events without introducing this circular dependency?

Thanks!


(Alan Klikic) #4

@franz i would not say it is tipical Lagom pattern but it is not a tread off.
I do not see any problems with circular dependency in this case.

For example I’m using this pattern for bidirectional streaming between two services. Initially bidirectional stream service call was used and was replaced with upstream and downstream kafka topic. This way bidirectional streaming is more scalable.


(franz) #5

Understood. Thanks @aklikic!