Scala Frameworks for Web Application 2016

  • Published on
    16-Apr-2017

  • View
    5.314

  • Download
    4

Embed Size (px)

Transcript

<ul><li><p>Scala Frameworks forWeb Application 2016</p><p>BizReach, IncScala Kansai Summit 2016</p><p>#scala_ks</p></li><li><p>Standard Frameworks for Web Application in Scala</p></li><li><p>Why we've used Play2 and Slick?</p><p> Many users and developers Future prospects Supported by Typesafe</p></li><li><p>Dissatisfaction to Play2</p><p> Unnecessary features View support Assets management Dependency injection</p><p> Poor integration Zipkin https://github.com/levkhomich/akka-tracing Swagger https://github.com/swagger-api/swagger-play</p><p>https://github.com/levkhomich/akka-tracinghttps://github.com/swagger-api/swagger-play</p></li><li><p>Dissatisfaction to Slick</p><p> Lerning curve DBIO is hard for non-functional programmers Asynchronous is not always needed</p><p> Performance Join makes subquery frequently It causes performance issue with MySQL</p></li><li><p>Alternatives?</p></li><li><p>Alternatives</p><p> Web Framework Finagle Akka HTTP Skinny Micro</p><p> Database Framework Quill doobie Scalike JDBC</p></li><li><p>Finagle</p><p> Pluggable asynchronous RPC framework based on Netty</p><p> Client API with Circuit Breaker Zipkin integration Finatra or Finch for HTTP server Swagger 1.x support for Finatra (3rd party)</p><p> https://github.com/xiaodongw/swagger-finatra</p><p>https://github.com/xiaodongw/swagger-finatrahttps://github.com/xiaodongw/swagger-finatra</p></li><li><p>Finagle</p><p>val router = RoutingService.byPathObject[Request] { case Root =&gt; new Service[Request,Response] { def apply(req: Request): Future[Response] = { Future(Response( req.version, Status.Ok, Reader.fromBuf(Utf8("API is Ready.")) )) } } case Root / "hello"/ name =&gt; new Service[Request, Response] { def apply(req: Request): Future[Response] = { Future(Response( req.version, Status.Ok, Reader.fromBuf(Utf8(s"Hello ${name}!")) ))</p><p>} }}</p><p>Maybe Finatra or Finch is better choise for REST API server</p></li><li><p>Akka HTTP</p><p> Actor-based toolkit for interacting web services and clients</p><p> spray like routing DSL Reactive Streams Swagger 2.0 support (3rd party)</p><p> https://github.com/swagger-akka-http/swagger-akka-http</p><p> Play 3.0 will move to Akka HTTP? Experimental in Play 2.5 https://www.playframework.com/documentation/2.5.x/AkkaHttpServer</p><p>https://github.com/swagger-akka-http/swagger-akka-httphttps://github.com/swagger-akka-http/swagger-akka-httphttps://www.playframework.com/documentation/2.5.x/AkkaHttpServerhttps://www.playframework.com/documentation/2.5.x/AkkaHttpServer</p></li><li><p>Akka HTTP</p><p>val route = get { pathEndOrSingleSlash { handleWith((request: HttpRequest) =&gt; "API is ready.") }} ~ path("hello" / ".+".r) { get { case (name) =&gt; complete { HelloResult(message = s"Hello, ${request.name}!") } }}</p></li><li><p>Skiny Micro</p><p> Servlet-based micro framework Scalatra comparible routing DSL Future-wired async operation No Swagger and Zipkin support</p></li><li><p>Skiny Micro</p><p>object Hello extends WebApp with JSONSupport { get("/") {</p><p>"API is ready" } post("/hello/:name") { contentType = "application/json" toJSONString( HelloResult(message = s"Hello, ${params("name")}!") ) }}</p></li><li><p>Results</p><p>Version View DI Routing Circuit Breaker</p><p>Reactive Streams</p><p>Zipkin Swagger</p><p>Play2 2.5.8 Supported Guice Method + DSL</p><p>- 2 3rd party library</p><p>3rd party library</p><p>Finagle 6.38.0 - 1 DSL Supported - Supported 3rd party library</p><p>Akka HTTP 2.4.10-experimantal</p><p>- - DSL - Supported 3rd party library</p><p>3rd party library</p><p>Skinny Micro</p><p>1.1.0 - - DSL - - - -</p><p>1 Finatra is equipped Guice based dependency injection as same as Play22 There is an experimental module in Play 2.5 https://www.playframework.com/documentation/2.5.x/ReactiveStreamsIntegration</p><p>https://www.playframework.com/documentation/2.5.x/ReactiveStreamsIntegration</p></li><li><p>Quill</p><p> Macro-based compile time SQL generation No overhead in runtime Compile time SQL validation is available Some constraints in query building</p><p> Development is very active Many sub modules are available such as async, </p><p>cassandra support and finagle integration Move to scala.meta in the future?</p></li><li><p>Quill</p><p>val account: Option[Account] = ctx.run( quote { (mailAddress: String, includeRemoved: Boolean) =&gt; query[Account].filter { t =&gt; if(includeRemoved){ t.mailAddress == mailAddress } else { t.mailAddress == mailAddress &amp;&amp; t.removed == false } } })(mailAddress, includeRemoved).headOption</p></li><li><p>Quill</p><p>val account: Option[Account] = ctx.run( quote { (mailAddress: String, includeRemoved: Boolean) =&gt; query[Account].filter { t =&gt; if(includeRemoved){ t.mailAddress == mailAddress } else { t.mailAddress == mailAddress &amp;&amp; t.removed == false } } })(mailAddress, includeRemoved).headOption</p><p>Macro (expanded in compile time)</p></li><li><p>Quill</p><p>val account: Option[Account] = ctx.run( quote { (mailAddress: String, includeRemoved: Boolean) =&gt; query[Account].filter { t =&gt; if(includeRemoved){ t.mailAddress == mailAddress } else { t.mailAddress == mailAddress &amp;&amp; t.removed == false } } })(mailAddress, includeRemoved).headOption</p><p>Take variables as argument</p></li><li><p>Quill</p><p>val account: Option[Account] = ctx.run( quote { (mailAddress: String, includeRemoved: Boolean) =&gt; query[Account].filter { t =&gt; if(includeRemoved){ t.mailAddress == mailAddress } else { t.mailAddress == mailAddress &amp;&amp; t.removed == false } } })(mailAddress, includeRemoved).headOption</p><p>Assemble condition dynamically?</p></li><li><p>Quill</p><p>SELECT ...FROM account tWHERE CASE WHEN ? THEN t.mail_address = ?ELSE (t.mail_address = ?) AND (t.removed = false)END</p><p>No, translated to CASE expression</p></li><li><p>doobie</p><p> A pure-functional JDBC layer for Scala It is not an ORM</p><p> Designed for people who are interested in: typed pure functional programming</p><p> IO and monadic effects</p></li><li><p>doobie</p><p>sql"select * from account where uid = $id" .query[Account] .option .transact(xa) .unsafePerformAsync { case -\/(throwable) =&gt; ... case \/-(account) =&gt; ... }</p></li><li><p>doobie</p><p>sql"select * from account where uid = $id" .query[Account] // Query0[Account] .option // ConnectionIO[Option[Account]] .transact(xa) // Task[Option[Account]] .unsafePerformAsync { case -\/(throwable) =&gt; // Throwable case \/-(account) =&gt; // Option[Account] }</p><p>Query0[Account] is all columns query that maps one returned row.Ultimately producing a value of type Option[Account].</p></li><li><p>doobie</p><p>sql"select * from account where uid = $id" .query[Account] // Query0[Account] .option // ConnectionIO[Option[Account]] .transact(xa) // Task[Option[Account]] .unsafePerformAsync { case -\/(throwable) =&gt; // Throwable case \/-(account) =&gt; // Option[Account] }</p><p>Task is scalaz.concurrent.Task!!</p></li><li><p>doobie</p><p> Typechecking (experimental) Validate queries against the database schema in </p><p>runtimeval q: Query0[Account] = sql"select * from account where uid = $id".query[Account]q.check.unsafePerformSync</p><p> SQL Compiles and Typechecks C01 UID INTEGER (INTEGER) NOT NULL String - INTEGER (INTEGER) is ostensibly coercible to String according to the JDBC specification but is not a recommended target type. Fix this by changing the schema type to CHAR or VARCHAR; or the Scala type to Int or JdbcType. C02 LOGIN_ID VARCHAR (VARCHAR) NOT NULL String</p></li><li><p>ScalikeJDBC</p><p> A tidy SQL-based DB access library for Scala Naturally wrap JDBC APIs easy-to-use</p><p> QueryDSL is available (since 1.6)</p></li><li><p>ScalikeJDBC</p><p>val id = 1</p><p>// QueryDSLval a = Account.syntax("a")</p><p>val account: Option[Account] = DB readOnly { implicit s =&gt; withSQL { select.from(Account as a).where.eq(a.uid, id) }.map(Account(a)).single.apply()}</p></li><li><p>ScalikeJDBC</p><p>val id = 1</p><p>case class Email(name: String, address: String)</p><p>// basic SQLval email: Option[Email] = DB readOnly { implicit s =&gt; sql"select * from account where uid = ${id}" .map(rs =&gt; Email(rs.get("name"), rs.get("mail_address")) ).single.apply()}</p></li><li><p>Results</p><p>Version Monad Async Mapping Typesafe DSL</p><p>Genarated SQL</p><p>Timing PlainSQL</p><p>Slick 3.1.1 Required Always Required2</p><p>Supported Non-intuitive</p><p>Runtime Supported</p><p>Quill 0.10.0 Option 1 - Supported3</p><p>Intuitive Compile time</p><p>-</p><p>doobie 0.3.0 Required Option - - - - Supported4</p><p>ScalikeJDBC</p><p>2.4.2 - 1 Required2</p><p>Supported Intuitive Runtime Supported</p><p>1 Provides non-blocking API using postgresql-async or mysql-async2 A tool to generate table mappings from actual database schema is available3 Compile time SQL validation is available4 Runtime typechecking is available as experimental feature</p></li><li><p>Conclusion</p></li><li><p>Conclusion</p><p> Web Fraemwork All alternatives look good, but Play2 is not so bad </p><p>as well For servlet container, Skinny Micro would be good</p><p> Database Framework There is no de-facto standard library currently ScalikeJDBC looks good for us and almost users</p></li></ul>