48
Solid and Sustainable Development in Scala Kazuhiro Sera @seratch ScalikeJDBC / Skinny Framework Founder & Lead Developer

Solid And Sustainable Development in Scala

Embed Size (px)

DESCRIPTION

Presentation at ScalaMatsuri 2014 http://scalamatsuri.org/en/program/index.html#session-b-4 http://live.nicovideo.jp/watch/lv191315534#5:12:20

Citation preview

Page 1: Solid And Sustainable Development in Scala

Solid and Sustainable Development in Scala

Kazuhiro Sera @seratch ScalikeJDBC / Skinny Framework

Founder & Lead Developer

Page 2: Solid And Sustainable Development in Scala

Ask Me Later!

• 3 mags for questioners at the end of this session! Don’t miss it!

2

Page 3: Solid And Sustainable Development in Scala

Who Am I

• Kazuhiro Sera • @seratch on Twitter/GitHub • Scala enthusiast since 2010 • ScalikeJDBC, Skinny Framework, AWScala founder & project lead

• A web developer at M3, Inc (We’re a Gold Sponsor)

Page 4: Solid And Sustainable Development in Scala

Introduce My(Our) Products

Page 5: Solid And Sustainable Development in Scala

ScalikeJDBC

• scalikejdbc.org • “Scala-like JDBC” • Provides Scala-ish APIs • Started as a better querulous / Anorm • “Just write SQL and get things done” • QueryDSL for smoothness & type-safety • Stable enough: lots of companies already use it in production

Page 6: Solid And Sustainable Development in Scala

Dependencies

// build.sbt or project/Build.scala!! scalaVersion := “2.11.2” // or “2.10.4”!! libraryDependencies ++= Seq(! “org.scalikejdbc” %% “scalikejdbc” % “2.1.1”,! “com.h2database” % “h2” % “1.4.181”,! “ch.qos.logback” % “logback-classic” % “1.1.2”! )

Page 7: Solid And Sustainable Development in Scala

Basic Usage import scalikejdbc._!! ConnectionPool.singleton(! “jdbc:h2:mem:matsuri”, ! “user”, “secret”)!! DB autoCommit { implicit session =>! sql”create table attendee (name varchar(32))”.execute.apply()! val name = “seratch”! sql”insert into attendee (name) values ($name)”.update.apply()! }!! val names: Seq[String] = DB readOnly { implicit s =>! sql”select name from attendee”! .map(_.string(“name”)).list.apply()! }

SQL statement!(PreparedStatement)

execute/update!(JDBC operation)

Side effect !with DB connection

Extractor

Page 8: Solid And Sustainable Development in Scala

QueryDSL import scalikejdbc._! case class Attendee(name: String)! object Attendee extends SQLSyntaxSupport[Attendee] {! def apply(rs: WrappedResultSet, a: ResultName[Attendee]) = ! new Attendee(rs.get(a.name)) }!! implicit val session = AutoSession!!! val a = Attendee.syntax(“a”)! val seratch: Option[Attendee] = withSQL {! select.from(Attendee as a).where.eq(a.name, “seratch”)! }.map(rs => new Attendee(rs, a)).single.apply()!! // select a.name as n_on_a from attendee a where a.name = ?

QueryDSL!(Mostly SQL)

Actual SQL Query

Page 9: Solid And Sustainable Development in Scala

Skinny Framework

• skinny-framework.org • “Scala on Rails” • For Rails / Play1 lovers • 1.0.0 was out on 28th March • Already several experiences in production • Full-stack features: Web infrastructure, Scaffold generator, ORM, DB migration, JSON stuff, HTTP client, Mail sender, Job workers, Assets controller, etc..

Page 10: Solid And Sustainable Development in Scala

Boot in 2 minutes

! // install skinny command! brew tap skinny-framework/alt! brew install skinny!! // create app and start! skinny new myapp! cd myapp! skinny run!! // access localhost:8080 from your browser!

Page 11: Solid And Sustainable Development in Scala

Model (DAO) import skinny.orm._! import scalikejdbc._!! case class User(id: Long, name: Option[String])!! // companion: data mapper! object User extends SkinnyCRUDMapper[User] {! def defaultAlias = createAlias(“u”)! def extract(rs: WrappedResultSet, u: ResultName[User])! = autoConstruct(rs, u) }!! User.findById(123)! User.count()! User.createWithAttributes(‘name -> “Alice”)! User.updateById(123).withAttributes(‘name -> “Bob”)! User.deleteBy(sqls.eq(u.name, “Bob”))

CRUD Mapper Object!(No need to be companion)

Entity

Smooth APIs

Page 12: Solid And Sustainable Development in Scala

Controller + Route package controller! class UsersController extends ApplicationController {! def showUsers = {! set(“users”, User.findAll())! render(“/users/index”) } }!! // Routings! object Controllers {! val users = new UsersController with Routes {! get(“/users/”)(showUsers).as(‘showUsers) }! def mount(ctx: ServletContext): Unit = {! users.mount(ctx)! } }

Set value in request scope!(Visible in views)

Expects “src/main/webapp/!WEB-INF/views/users/index.html.ssp”

Page 13: Solid And Sustainable Development in Scala

View Template

// src/main/webapp/WEB-INF/views/users/index.html.ssp!! <%@val users: Seq[User] %>!! <table class=“table”>! #for (user <- users)! <tr>! <td>${user.id}</td>! <td>${user.name}</td>! </tr>! #end! </table>

import from request scope

Loop, if/else syntax!in Scalate

Page 14: Solid And Sustainable Development in Scala

Web Development

• Interactive feedback loop is most important especially when changing UI

• Actually Scala compilation is so slow that waiting view templates compilation makes developers much stressed

• Skinny doesn’t compile all the view templates when developing (unlike Twirl)

Page 15: Solid And Sustainable Development in Scala

My Good Parts for Solid and Safe

Development

Page 16: Solid And Sustainable Development in Scala

My Good Parts•Simplified Class-based OOP And Immutable Data Structure •Working On Problems Without Overkill Abstraction •Writing Tests Without Question •Keep Infrastructure Lightweight •No Surprises For Newcomer

Page 17: Solid And Sustainable Development in Scala

Simplified Class-based OOP

And Immutable Data Structure

Page 18: Solid And Sustainable Development in Scala

Class-based OOP

• Already so popular (Java, Ruby, Python ..) • Old style is friendly with mutability (e.g. setters, bang methods), but that’s not a prerequisite

• OOP can be more solid and safer by keeping immutability and avoiding inheritance anti-patterns

Page 19: Solid And Sustainable Development in Scala

Scala or Java 8?

• Scala case class is simpler than Java beans with (great) Lombok

• Scala high-order functions are simpler than Java 8 Stream API

• Immutability is well-treated in Scala • Fairness: Java decisively beats Scala in comparison with compilation speed..

Page 20: Solid And Sustainable Development in Scala

Immutability

• Do away with mutable states • Re-assignment? No way! Don’t use `var` • Immutability makes your apps not only scalable but also more solid

• When you modify a case class, call #copy() and return new state instead of using setters for mutability inside

Page 21: Solid And Sustainable Development in Scala

Immutable Entity // entity with behaviors! case class User(id: Long, name: Option[String])! extends SkinnyRecord[User] {! override def skinnyCRUDMapper = User }! // data mapper! object User extends SkinnyCRUDMapper[User] {! override def defaultAlias = createAlias(“u”)! override def extract(rs: WrappedResultSet, u: ResultName[User])! = autoConstruct(rs, u) }!! val noNames: Seq[User] = User.where(‘name -> “”).apply()! val anons: Seq[User] = noNames.map { user => ! user.copy(name = “Anonymous”).save()! }

Both of “noNames” and “anons” are immutable

Page 22: Solid And Sustainable Development in Scala

Trait Chaos

•Mixing traits can show you terrible chaos • We should have self-discipline • Prefer `override` modifier to detect API changes when mixing many traits

• Collect the same sort of traits and place them in same place to avoid code duplication or unwanted complexity

Page 23: Solid And Sustainable Development in Scala

Web Controller package controller! class MainController extends ApplicationController ! with concern.TimeLogging {! def index = logElapsedTime { render(“/main/index”) }! }!! // for controllers! package controller.concern! trait TimeLogging { self: SkinnyController with Logging =>! def millis: Long = System.currentTimeMillis ! def logElapsedTime[A](action: => A): A = {! val before = millis! val result = action! logger.debug(“Elapsed time: ${millis - before} millis”)! result }! }

The “concern” just follows Rails style. Anyway, naming should be simple and!

easy-to-understand for anyone

Page 24: Solid And Sustainable Development in Scala

Coding Style Tips

• Use sbt-scalariform without question (similar: go-lang’s fmt)

• Don’t toss similar classes or traits into single scala file except `sealed` pattern

• Don’t place classes under different package directory (although it’s possible)

• Do you really need cake pattern? • Prefer eloquent method signature than explaining a lot in scaladocs

Page 25: Solid And Sustainable Development in Scala

Working On Problems Without Overkill Abstraction

Page 26: Solid And Sustainable Development in Scala

The Real As-Is

• Abstraction often improves things, but that’s not always the best way to solve real-world problems

• I/O issue is a typical case that we should comprehend problems as-is

• Database access / SQL is not a collection but just an external I/O operation

• Overkill abstraction makes your code difficult to maintain for other developers

Page 27: Solid And Sustainable Development in Scala

Don’t Hide the SQL

• “You don’t need another DSL to access relational databases” - Anorm

• You must recognize what is working effectively in the SQL layer

• Utility to write DAO easily is fine but hiding SQL is not good

• Need to grab the cause from raw queries when dealing with troubles (comfortable logging also can help)

Page 28: Solid And Sustainable Development in Scala

SQL Ops As-Is // A programmer belongs to a company and has several skills!! implicit val session = AutoSession!! val p: Option[Programmer] = withSQL {! select.from(Programmer as p)! .leftJoin(Company as c).on(p.companyId, c.id)! .leftJoin(ProgrammerSkill as ps).on(ps.programmerId, p.id)! .leftJoin(Skill as s).on(ps.skillId, s.id)! .where.eq(p.id, 123).and.isNull(p.deletedAt)! }! .one(rs => Programmer(rs, p, c))! .toMany(rs => Skill.opt(rs, s))! .map { (programmer, skills) => programmer.copy(skills = skills) }! .single! .apply()

I believe everybody can understand this code

Extracts one-to-many relationships here

Page 29: Solid And Sustainable Development in Scala

Skinny ORM

• ORM built on ScalikeJDBC • Highly inspired by Rails ActiveRecord • SQL queries for CRUD apps are common enough, so it’s reasonable to avoid writing mostly same code everywhere

• Skinny ORM doesn't prevent you from using ScaikeJDBC APIs directly

• A part of Skinny Framework but you can use it in Play apps too

Page 30: Solid And Sustainable Development in Scala

Dependencies

// build.sbt or project/Build.scala!! scalaVersion := “2.11.2” // or “2.10.4”!! libraryDependencies ++= Seq(! //“org.scalikejdbc” %% “scalikejdbc” % “2.1.1”,! “org.skinny-framework” %% “skinny-orm” % “1.3.1”,! “com.h2database” % “h2” % “1.4.181”,! “ch.qos.logback” % “logback-classic” % “1.1.2”! )

Page 31: Solid And Sustainable Development in Scala

Mappers // entities! case class Company(id: Long, name: String)! case class Employee(id: Long, name: String,! companyId: Long, company: Option[Company])!! // mappers! object Company extends SkinnyCRUDMapper[Company] {! def extract(rs: WrappedResultSet, rn: ResultName[Company]) =! autoConstruct(rs, rn) }! object Employee extends SkinnyCRUDMapper[Employee] {! def extract(rs: WrappedResultSet, rn: ResultName[Employee]) =! autoConstruct(rs, rn, “company”)! // simple association definition! lazy val companyRef = belongsTo[Company](! Company, (e, c) => e.copy(company = c)) }

Page 32: Solid And Sustainable Development in Scala

Reasonable?!! val companyId = Company.createWithAttributes(‘name -> “Sun”)! val empId = Employee.createWithAttributes(! ‘name -> “Alice”, ‘companyId -> companyId)!! val emp: Option[Employee] = Employee.findById(empId)! val empWithCompany: Option[Employee] = ! Employee.joins(companyRef).findById(123)!! Company.updateById(companyId).withAttributes(‘name -> “Oracle”)!! val e = Employee.defaultAlias! Employee.deleteBy(sqls.eq(e.id, empId))! Company.deleteById(companyId)

Using ScalikeJBDC API!is also possible

Right, these CRUD operations are not SQL-ish. However, I believe they’re reasonable because these patterns are already common enough.

Page 33: Solid And Sustainable Development in Scala

Writing Tests Without Question

Page 34: Solid And Sustainable Development in Scala

Not Only Compiler

• It’s true that compiler helps you by detecting mistakes in coding

• Writing tests is a reasonable way to verify your code meets requirements / specifications as expected

• You can’t skip automated tests even if your apps are written in Scala

Page 35: Solid And Sustainable Development in Scala

scoverage• At this time, the only option available for us is scoverage (SCCT inheritor)

• Add the dependency into your projects right now if you don’t use it yet

Page 36: Solid And Sustainable Development in Scala

Keep Infrastructure Lightweight

Page 37: Solid And Sustainable Development in Scala

Avoid SBT Hacks

• sbt is not so easy for Scala newbies, upgrading sbt is all the more so

• Play depends on sbt version (e.g. Play 2.1 w/ sbt 0.12), upgrading Play is about not only Play API changes but sbt things

• Your own sbt plugins/hacks make your projects difficult to maintain for a long period and involve others

• Don’t try to do everything there

Page 38: Solid And Sustainable Development in Scala

Skinny TaskRunner

• Just want a simple and “rake”-like task runner (no sbt plugin)

• Simplistic but pragmatic idea: “mainClass” of “task” sbt project can be dispatcher of task runner system

• Tasks are written in Scala (no sbt plugin) • Not only writing code but upgrading scala/sbt version become pretty easy

Page 39: Solid And Sustainable Development in Scala

Tasks

// sbt settings! // mainClass := Some("TaskRunner")!! // task/src/main/scala/TaskRunner.scala! object TaskRunner extends skinny.task.TaskLauncher {! register("assets:precompile", (params) => {! val buildDir = params.headOption.getOrElse(“build")! // AssetsPrecompileTask is a Scala object! skinny.task.AssetsPrecompileTask.main(Array(buildDir))! })! }! ! // skinny task:run assets:precompile [dir]

Pure Scala function!

task name

mainClass as the dispatcher

Page 40: Solid And Sustainable Development in Scala

Use Only Essentials

• The same issue as Ruby gems, what’s worse, Scala’s eco-system is still so smaller than Ruby’s one

• Binary incompatibility makes existing libraries outdated every Scala major version release

• Are you really ready to fork them? • Using Java assets (e.g. commons) internally is also worth thinking about

Page 41: Solid And Sustainable Development in Scala

No Surprises for Newcomer

Page 42: Solid And Sustainable Development in Scala

Can Feel Welcome?

• Is joining your Scala projects easy? Can newcomer understand overview at once?

• Don’t stick to doing on the sbt, don’t disfavor using other tools (e.g. Grunt)

• Well-known style (e.g. Rails style) is preferred for collaborative works

• Is asynchronosity really required now? • Indeed, your DSL is very simple but easy to modify them (for others)?

Page 43: Solid And Sustainable Development in Scala

Newcomers may not know Scala well.

Attract them to Scala! (Don’t scare them)

Page 44: Solid And Sustainable Development in Scala

AMA! #ScalaMatsuri2

• Unconference tomorrow • “Ask Me Anything” • ScalikeJDBC • Skinny Framework • AWScala • Anything else!

Page 45: Solid And Sustainable Development in Scala

Questions?

Page 46: Solid And Sustainable Development in Scala

Questions?

Page 47: Solid And Sustainable Development in Scala

Questions?

Page 48: Solid And Sustainable Development in Scala

Thanks

• Amazing “Maturi Urakata” (the conference volunteer staff)

• Great invited speakers • You all here!