用Scala来搞Lagom!【第二部分:创建最小服务】
我是Gaku。
开始用Scala做Lagom!【第1部分:环境设置】
之前完成了环境设置,所以这次要创建一个新的服务。
提供的Hello World中包含了很多内容,理解起来有些困难,所以我将进一步简化代码,只创建一个最基本的能工作的服务(只能通过Get方法获取一个字符串)。
我們這次創建的程式碼
这是在这里。
https://github.com/gaku3601/study-lagom/tree/step2
创建api和impl文件夹。
添加模块
将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`)
进行
首先,我已经对我理解的部分做了评论。对于我还没有理解的部分,我写了一些模糊的评论,如果您可以忽略的话我会很感激。(这只是我自己的备忘录。如果我理解了,我会进行更新的。)
API的实现
所以,以这样的方式进行实施。
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
所以,我的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和聚合根的编写等等都还存在很多疑问。所以我打算在有空的时候悄悄解决这些问题。