用Scala来搞Lagom!【第二部分:创建最小服务】

我是Gaku。
开始用Scala做Lagom!【第1部分:环境设置】
之前完成了环境设置,所以这次要创建一个新的服务。
提供的Hello World中包含了很多内容,理解起来有些困难,所以我将进一步简化代码,只创建一个最基本的能工作的服务(只能通过Get方法获取一个字符串)。

我們這次創建的程式碼

这是在这里。
https://github.com/gaku3601/study-lagom/tree/step2

创建api和impl文件夹。

image.png

添加模块

将study-lagom/build.sbt进行如下修正。

organization in ThisBuild := "com.example"
version in ThisBuild := "1.0-SNAPSHOT"

// the Scala version that will be used for cross-compiled libraries
scalaVersion in ThisBuild := "2.13.0"

val macwire = "com.softwaremill.macwire" %% "macros" % "2.3.3" % "provided"
val scalaTest = "org.scalatest" %% "scalatest" % "3.1.1" % Test

// 追記
lazy val `study-lagom` = (project in file("."))
  .aggregate(`my-study-lagom-api`, `my-study-lagom-impl`, `study-lagom-api`, `study-lagom-impl`, `study-lagom-stream-api`, `study-lagom-stream-impl`)

// 追加
lazy val `my-study-lagom-api` = (project in file("my-study-lagom-api"))
  .settings(
    libraryDependencies ++= Seq(
      lagomScaladslApi
    )
  )

// 追加
lazy val `my-study-lagom-impl` = (project in file("my-study-lagom-impl"))
  .enablePlugins(LagomScala)
  .settings(
    libraryDependencies ++= Seq(
      macwire,
    )
  )
  .dependsOn(`my-study-lagom-api`)

lazy val `study-lagom-api` = (project in file("study-lagom-api"))
  .settings(
    libraryDependencies ++= Seq(
      lagomScaladslApi
    )
  )

lazy val `study-lagom-impl` = (project in file("study-lagom-impl"))
  .enablePlugins(LagomScala)
  .settings(
    libraryDependencies ++= Seq(
      lagomScaladslPersistenceCassandra,
      lagomScaladslKafkaBroker,
      lagomScaladslTestKit,
      macwire,
      scalaTest
    )
  )
  .settings(lagomForkedTestSettings)
  .dependsOn(`study-lagom-api`)

lazy val `study-lagom-stream-api` = (project in file("study-lagom-stream-api"))
  .settings(
    libraryDependencies ++= Seq(
      lagomScaladslApi
    )
  )

lazy val `study-lagom-stream-impl` = (project in file("study-lagom-stream-impl"))
  .enablePlugins(LagomScala)
  .settings(
    libraryDependencies ++= Seq(
      lagomScaladslTestKit,
      macwire,
      scalaTest
    )
  )
  .dependsOn(`study-lagom-stream-api`, `study-lagom-api`)

image.png

进行

首先,我已经对我理解的部分做了评论。对于我还没有理解的部分,我写了一些模糊的评论,如果您可以忽略的话我会很感激。(这只是我自己的备忘录。如果我理解了,我会进行更新的。)

API的实现

image.png

所以,以这样的方式进行实施。

package com.example.mystudylagom.api

import akka.NotUsed
import com.lightbend.lagom.scaladsl.api.{Descriptor, Service, ServiceCall}

trait MyStudyLagomService extends Service {

  /**
    * Example: curl http://localhost:9000/api/hello2/Alice
    * id: stringには/api/hello2/:idのidが入ってくる
    * ServiceCall[NotUsed, String]のNotUsed部分にはrequest bodyが、Stringはresponseの肩
    *  今回、request bodyはNotUsedで使わないことを宣言している→つまり取得処理
    *  lagomはServiceCall[NotUsed, String]自動的にGetメソッドと判断する
    */
  def hello(id: String): ServiceCall[NotUsed, String]

  override final def descriptor: Descriptor = {
    import Service._
    named("my-study-lagom")
      .withCalls(
        pathCall("/api/hello2/:id", hello _), // APIルーティングを定義
      )
      .withAutoAcl(true) // これはつけとかないと動かない。
  }
}

创建ServiceImpl

image.png

所以,我的StudyLagomServiceImpl.scala

package com.example.mystudylagom.impl

import akka.NotUsed
import com.example.mystudylagom.api.MyStudyLagomService
import com.lightbend.lagom.scaladsl.api.ServiceCall

import scala.concurrent.Future

class MyStudyLagomServiceImpl() extends MyStudyLagomService {
  // apiの実装の中身。「_ =>」の_にはpostの場合、request bodyが入ってくるみたい。
  override def hello(id: String): ServiceCall[NotUsed, String] = ServiceCall { _ =>
    // なんでもかんでもFutureで返せばOKっぽい?
    Future.successful("test")
  }
}

如果用这个启动,看起来会动,但会出现错误。根据简短调查,似乎需要一个用于进行DI的Loader。
可以按以下方式实施。

package com.example.mystudylagom.impl

import com.example.mystudylagom.api.MyStudyLagomService
import com.lightbend.lagom.scaladsl.api.ServiceLocator
import com.lightbend.lagom.scaladsl.api.ServiceLocator.NoServiceLocator
import com.lightbend.lagom.scaladsl.server._
import com.lightbend.lagom.scaladsl.devmode.LagomDevModeComponents
import play.api.libs.ws.ahc.AhcWSComponents
import com.softwaremill.macwire._

/*
 * これはDIの仕組みを行うために必要?
 * このfileを作らないと動かない
 * とりあえず、いらないものを削ぎ落として、最低限動くようにしたもの
 * akkaとかcassandraの読み込みもここで行うようなので、また別の機会でほそぼそ試す。
 */
class MyStudyLagomLoader extends LagomApplicationLoader {

  // loadとloadDevModeで、おそらく、本番・開発環境で使う依存を管理する感じかな?
  override def load(context: LagomApplicationContext): LagomApplication =
    new MyStudyLagomApplication(context) {
      override def serviceLocator: ServiceLocator = NoServiceLocator
    }

  // このメソッド、消しても動く?と思って消してみたらエラー出た。
  override def loadDevMode(context: LagomApplicationContext): LagomApplication =
    new MyStudyLagomApplication(context) with LagomDevModeComponents
}

abstract class MyStudyLagomApplication(context: LagomApplicationContext)
  extends LagomApplication(context)
    with AhcWSComponents {

  // こんな感じでバインドするみたい
  override lazy val lagomServer: LagomServer = serverFor[MyStudyLagomService](wire[MyStudyLagomServiceImpl])
}

所以,将这个Loader在配置文件中指定出来。

# Loaderの読み込みでこの一行は必要
play.application.loader = com.example.mystudylagom.impl.MyStudyLagomLoader

启动

如果能做到这一点,就试着启动它。使用curl向创建的API发送请求。

gakumbp:study-lagom gaku$ curl http://localhost:9000/api/hello2/Alice
test

有什么东西回来了!然后,当向study-lagom服务的API发送curl请求时

gakumbp:study-lagom gaku$ curl http://localhost:9000/api/hello/Alice
Hello, Alice!

这边也会有一些回应?

结束

总的来说,我对这个问题大概有点明白,但是接下来怎么在服务之间进行协作,或者如何进行持久化的处理,以及如何处理CQRS和聚合根的编写等等都还存在很多疑问。所以我打算在有空的时候悄悄解决这些问题。

广告
将在 10 秒后关闭
bannerAds