Inter Service Communication with out message broker - unable to Inject if creating component extending LagomServerComponent

scala

(Rajkumar Parthasarathi) #1

I am a beginner of Lagom and using it with Scala. It is an awesome experience for me to create a microservice using it till now.

As I referred the followinghttps://www.lagomframework.com/documentation/1.4.x/scala/ServiceClients.html#Binding-a-service-client and I am trying out for calling inter service with out message broker via remote calls. Which means I wanted to inject ServiceB in to serviceAImpl and do a invoke call on the client.This is for one of the scenario where I dont want the call to be via Event or message broker and should be a direct service to service call.

In Lagom scala I have ServiceA and ServiceB. I have created a ServiceB client in the ServiceAApplication and trying to inject the serviceB in the ServiceAImpl. I get an error during compilation saying the following, Cannot find a value of type : [com.example.ServiceB] override lazy val lagomServer = serverForServiceA

in the abstract class ServiceAApplication

snippet of the ApplicationLoader class where I get this error only when I inject the ServiceB in to the ServiceAImpl constructor.

Snippet of ServiceAApplicationLoader.scala:

trait ServiceAComponents extends LagomServerComponents
  with SlickPersistenceComponents
  with LagomConfigComponent
  with HikariCPComponents
  with LagomKafkaComponents
{
  implicit def executionContext: ExecutionContext
  def environment: Environment

  implicit def materializer: Materializer

  override lazy val lagomServer = serverFor[ServiceA](wire[ServiceAImpl])
  lazy val serviceARepository = wire[ServiceARepository]
  lazy val jsonSerializerRegistry = ServiceASerializerRegistry

  persistentEntityRegistry.register(wire[ServiceAEntity])
  readSide.register(wire[ServiceAEventProcessor])

}

abstract class ServiceAApplication(context: LagomApplicationContext) extends LagomApplication(context)
    with ServiceAComponents
    with AhcWSComponents
    with SlickPersistenceComponents
    with LagomServiceClientComponents
  with LagomConfigComponent
    {

  lazy val serviceB = serviceClient.implement[ServiceB]

}

 `class ServiceAApplicationLoader extends LagomApplicationLoader {
  override def load(context: LagomApplicationContext) =
    new ServiceAApplication(context) with LagomServiceLocatorComponents

  override def loadDevMode(context: LagomApplicationContext) =
    new ServiceAApplication(context) with LagomDevModeComponents

  override def describeService = Some(readDescriptor[ServiceA])
}

`

Snippet of ServiceAImpl.scala:

    class ServiceAImpl (registry: PersistentEntityRegistry, serviceARepository:ServiceARepository ,serviceB: ServiceB )
                          (implicit ec: ExecutionContext) extends ServiceA {

///   Here in one of the method calling serviceB which is injected in constructor.

When I compile I get the error as following : Cannot find a value of type : [com.example.ServiceB] override lazy val lagomServer = serverForServiceA

Note: When I do the application loader in the following way, I dont get the error, But as you will seee below, I dont define a component and hence losing the testability:

I dont have a trait for th ServiceAComponent like above, instead have defined as below.

abstract class ServiceAApplication(context: LagomApplicationContext) extends LagomApplication(context)
    with ServiceAComponents
    with AhcWSComponents
    with SlickPersistenceComponents
    with LagomServiceClientComponents
  with LagomConfigComponent
    {

 override lazy val lagomServer = serverFor[ServiceA](wire[ServiceAImpl])
  lazy val serviceARepository = wire[ServiceARepository]
  lazy val jsonSerializerRegistry = ServiceASerializerRegistry

  persistentEntityRegistry.register(wire[ServiceAEntity])
  readSide.register(wire[ServiceAEventProcessor])

  lazy val serviceB = serviceClient.implement[ServiceB]

}

class ServiceAApplicationLoader extends LagomApplicationLoader {
  override def load(context: LagomApplicationContext) =
    new ServiceAApplication(context) with LagomServiceLocatorComponents

  override def loadDevMode(context: LagomApplicationContext) =
    new ServiceAApplication(context) with LagomDevModeComponents

  override def describeService = Some(readDescriptor[ServiceA])
}

This Application Loader works fine with runAll, but if I have to run with unit test as there is no component trait found, not able to run along with the ServiceB.

snippet of unittest:

class ServiceAImplIntegrationTest extends AsyncWordSpec with Matchers with BeforeAndAfterAll {

  private val server = ServiceTest.startServer(ServiceTest.defaultSetup.withCassandra(true)) { ctx=>
    new ServiceAApplication(ctx) with LocalServiceLocator{
      override def additionalConfiguration: AdditionalConfiguration =
        super.additionalConfiguration ++ ConfigFactory.parseString(
          "cassandra-query-journal.eventual-consistency-delay = 0"
        )

    }
  }

Note: If you see the test case, it does not follow the way it is done in the sample ItemIntegrationTest, instead of starting the server with Component, I have started with just ServiceAApplication and hence the test fail saying service B is not running. How to deal with this.

Questions: 1. Is injecting a serviceB in to ServiceAImpl the right way and call it using invoke method ? (with out having a subscriber) 2. How to test this as integration of serviceA and serviceB ?


(Tim Moore) #2

The problem is that serviceB is not in scope in ServiceAComponents.

One possible solution would be to move lazy val serviceB = serviceClient.implement[ServiceB] into ServiceAComponents (which I think would also require it to extend LagomServiceClientComponents and AhcWSComponents).

Another possibility is declaring an abstract def serviceB: ServiceB in ServiceAComponents and leaving the implementation where it is.


(Tim Moore) #3

Either way, you’ll need to stub ServiceB for your test.

There’s an example of this in the documentation on testing services:
https://www.lagomframework.com/documentation/1.4.x/scala/TestingServices.html#How-to-test-one-service


(Rajkumar Parthasarathi) #4

Thanks Tim, this worked fine when I extende the ServiceAComponents with LagomServiceClientComponents and AhcWSComponents and moving the val serviceB = serviceClient.implement[ServiceB] into ServiceAComponents.