Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Pre ScalaMatsuri 2020

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

Pre ScalaMatsuri 2020

Avatar for Akihiro Okuno

Akihiro Okuno

August 30, 2020
Tweet

More Decks by Akihiro Okuno

Other Decks in Programming

Transcript

  1. About Me • Akihiro Okuno • Twitter, Github: @choplin •

    Database Enthusiast • VLDB2020 Tokyo will start tomorrow! • CTO at Splink Inc. • Medical AI startup company • We’re hiring!
  2. Scala.js Introduction • Scala to JS Compiler + Ecosystem •

    Brief History • First release in Nov 2013 (0.1.0 • Production-ready in Feb 2015 (0.6.0) • Reached 1.0.0! in Feb 2020 • Growing Ecosystem • Build tools • UI • Client-server communication • Functional programming • etc. addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.1.1") plugins.sbt lazy val root = (project in file(".")) .settings( scalaVersion := "2.13.3", name := "scalajs-tutorial", libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "1.0.0", scalaJSUseMainModuleInitializer := true ) .enablePlugins(ScalaJSPlugin) build.sbt import org.scalajs.dom.document object Hello { def main(args: Array[String]) = { val e = document.getElementById("app") e.textContent = "Hello Scalajs!" } } Main.scala $ sbt fastOptJS build => Yay!
  3. Architecture component • Scala JS architecture component • User Interface

    • Logic • Communication • Choices available to each component Client User Interface Logic Communication Client Communication Endpoint Whatever you need Server
  4. User Interface • User Interface • Manages intereactions with users,

    i.e. DOM rendering UI states, event flows etc. • Properties • Safe and clean UI logics • Requires sbt as a main build system • Main component on the client side! • Choices • Dom Builder: scalajs-dom, scalatags • RX/FRP: Binding.scala, OutWatch, airframe-rx- html • React Wrapper: scalajs-react, slinky • Flux: Diode Client User Interface Server
  5. Logic • Logic • All other stuffs to build an

    application • Properties • Less intrusive • Self-contained in many cases • Best starting point for Scala.js Client Logic Server
  6. Communication Client • Communication Client • Manages sending and receiving

    information between servers via HTTP • Properties • Interface matters! • Explicitly defined interface helps us a lot • Strictly-typed interface with Scala.js • Isomorphic architecture • Choices • RPC approach • IDL approach • Endpoint approach • Vanila REST Client Communication Client Server
  7. Communication Client – RPC approach • RPC approach • Define

    interfaces in internal DSL (e.g. Traits and annotations), and generate client/server codes automatically • Pros • Straightforward and intuitive for Scalaist • Less efforts • Cons • Implicit HTTP request/response • Not Human-friendly • Hard to incorporate HTTP ecosystem (monitoring, load-balancing, authz/authn, etc.) • Example • autowire, OutWatch, airframe-rpc, etc. case class Person(id: Int, name: String) @RPC trait MyService { def hello(person: Person): String } class MyServiceImpl extends MyService { override def hello(person: Person): String = s"Hello ${person.name} (id=${person.id})!" } val client = new ServiceSyncClient(Http.client.newSyncClient( "localhost:8080")) client.myService.hello(Person(id=1, name="leo")) Interface Server Client
  8. Communication Client – IDL approach • IDL approach • Define

    interfaces in external IDL, and compile them with the provided tools • Pros • High interoperability • Existing community and ecosystem • Cons • Weaker type safety • No types, or at least, conversions are required • Sometimes Scala support is not matured • Need to learn IDL • Examples • OpenAPI, GraphQL, gRPC, ScalaPB, mu-scala, etc syntax = "proto3"; package com.example.protos; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; } .proto val channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext(true).buil d val blockingStub = GreeterGrpc.blockingStub(channel) val request = HelloRequest(name = "World") val reply: HelloReply = blockingStub.sayHello(request) private class GreeterImpl extends GreeterGrpc.Greeter { override def sayHello(req: HelloRequest) = { Future.successful(HelloReply(message = "Hello " + req.name)) } } Server Client
  9. Communication Client – Endpoint approach • Endpoint approach • Define

    REST endpoints as values in Scala and retrieve client/server automatically. • Pros • No need to learn other language • Strictly typed in Scala • Definition Programmable • Interoperable with other languages • You can exploit standard HTTP REST • Cons • REST design must be considered • Some boilerplates is required (boring, but not complex) • Understanding of the library design is necessary • Examples • tapir, endpoints4s • My recommendation case class Person(id: Int, name: String) trait GreeterEndpoints extends algebra.Endpoints with algebra.JsonEntitiesFromSchemas with generic.JsonSchemas { val sayHello: Endpoint[Person, Counter] = endpoint(get(path / "hello"), ok(jsonResponse[String])) implicit lazy val personSchema: JsonSchema[Person] = genericJsonSchema } Endpoint object GreeterClient extends GreeterEndpoints with endpoints4s.xhr.thenable.Endpoints with endpoints4s.xhr.JsonEntitiesFromSchemas val helloResult: js.Thenable[String] = GreeterClient.sayHello(Person(1, "foo")) class GreetRoutes[F[_]: Sync] extends endpoints4s.http4s.server.Endpoints[F] with endpoints4s.http4s.server.JsonEntitiesFromSchemas with GreetEndpoints { val routes: HttpRoutes[F] = HttpRoutes.of( sayHello.implementedBy(person => s"Hello ${person.name} (id=${person.id})!") ) } Server Client
  10. Example 1: Scala.js in Logic • Mainly JS, Scala.js only

    as Logic • Pros • Less intrusive • Self-contained in many cases • Best starting point for Scala.js • Cons • Applicability is limited • Logic component tends to be a small part of whole client codes. • Most of client codes are still in JS/AltJS • Understanding of Scala.js’s JS interoperability is necessary Client Server
  11. Example 2: All Scala • Scala.js for all components •

    + a bit of JS optionally • Pros • Complete Scala experience! • ADT, pattern match, FP, etc. • Simple build system • Complete within sbt • sbt-scalajs, sbt-crosscompile, sbt-scalajs-bundler etc. • Cons • Hard to adopt the existing frontend ecosystem • There is no DefinitelyTypes for Scala.js! • Writing facades is always required (complex and boring) • (IMO) Psychological barrier to adopt JS • Requires determination! Client Server
  12. Example 3: Scala.js in Communication • Mainly JS, Scala.js only

    as Communication Client • Pros • Explicit and strictly-typed client-server interface • Easily exploitable the existing frontend ecosystem • High Interoperability (not the case for RPC) • Cons • Most of client codes are still in JS/AltJS • Build definition tends to be complex • Both Sbt and JS bundler must be combined • Wrappers of Scala.js is sometimes required • boring, but not complex • Wrappers may be auto-generated • My recommendation Client Server
  13. Summary • Scala.js is production-ready • You should consider how

    to incorporate Scala.js in your product • User Interface • Logic • Communication • Example architecture • Scala.js in Logic • All Scala • Scala.js in Communication • Enjoy Scala.js! Client User Interface Logic Communication Client Communication Endpoint Whatever you need Server