Making most of Scala Akka, Scala, Spray, Specs2; all in 50 minutes! - - PowerPoint PPT Presentation

making most of scala
SMART_READER_LITE
LIVE PREVIEW

Making most of Scala Akka, Scala, Spray, Specs2; all in 50 minutes! - - PowerPoint PPT Presentation

Making most of Scala Akka, Scala, Spray, Specs2; all in 50 minutes! Jan Machacek Chief whip at Cake Solutions Author of Pro Spring, Pro Spring 2.5 and other books & articles Contributor to Akka Patterns, Specs2 Spring, Scalad,


slide-1
SLIDE 1

Making most of Scala

Akka, Scala, Spray, Specs2; all in 50 minutes!

slide-2
SLIDE 2

Jan Machacek

  • Chief whip at Cake Solutions
  • Author of Pro Spring, Pro Spring 2.5 and other

books & articles

  • Contributor to Akka Patterns, Specs2 Spring,

Scalad, Spring Extensions, Spock Spring Integration

  • Editor of the Open Source Journal
  • @honzam399, github.com/janm399,

janm@cakesolutions.net

slide-3
SLIDE 3

Brings all together

  • Object oriented language

Everything is an object: even 1, true, ...; mixin composition (multiple implementation inheritance with terms & conditions)

  • Functional programming constructs

Even a function is object that can be bound to a variable, passed as argument

  • Static typing and pattern matching

Compiler infers & enforces type safety;

  • Compiles to Java bytecode

Adopt Scala slowly, don’t throw away your existing Java code

slide-4
SLIDE 4

Heavy lifting to DSLs

  • Use Scala to implement the most complex code

Java of the future: everything in Java, functions, traits, pattern matching; rich type inference

  • Use Scala to implement the ordinary code

Typical programming tasks can be done with much less syntactical noise

  • Use Scala for DSLs

It is easy to design & implement statically typed DSLs

slide-5
SLIDE 5

Let’s build something

  • A proper e-commerce app, where you must create

an account when you want to buy a widget for £10, giving your date of birth, a letter from the hospital in which your mother was born, ...; then receive text message with an activation code. Only to find out that they don’t deliver to your work address

slide-6
SLIDE 6

Let’s build something

  • We’ll do a HTTP POST with a JSON document

that can be mapped onto the User instance

  • Unmarshal the JSON document
  • Register the user and send the activation code
  • Marshal the instance of the reply (succeeded,

failed) into a JSON document

slide-7
SLIDE 7

Let’s build something

Depends on Composes Instantiates Io Core Api Web Application Main

slide-8
SLIDE 8

The components

trait ServerCore { implicit def actorSystem: ActorSystem implicit val timeout = Timeout(30000) val core = actorSystem.actorOf( Props[ApplicationActor], "application") Await.ready(core ? Start(), timeout.duration) }

slide-9
SLIDE 9

The components

trait HttpIO { implicit def actorSystem: ActorSystem lazy val ioBridge = IOExtension(actorSystem).ioBridge private lazy val httpClient: ActorRef = actorSystem.actorOf(Props(new HttpClient(ioBridge))) def makeConduit(host: String): ActorRef = actorSystem.actorOf(Props(new HttpConduit( httpClient, host, port = 443, sslEnabled = true))) }

slide-10
SLIDE 10

The components

trait HttpIO { implicit def actorSystem: ActorSystem lazy val ioBridge = IOExtension(actorSystem).ioBridge private lazy val httpClient: ActorRef = actorSystem.actorOf(Props(new HttpClient(ioBridge))) def makeConduit(host: String): ActorRef = actorSystem.actorOf(Props(new HttpConduit( httpClient, host, port = 443, sslEnabled = true))) } trait ActorHttpIO extends HttpIO { this: Actor => final implicit def actorSystem = context.system }

slide-11
SLIDE 11

The components

trait Api extends RouteConcatenation { this: ServerCore => val routes = new HomeService().route ~ new UserService().route val rootService = actorSystem.actorOf(Props( new RoutedHttpService(routes))) }

slide-12
SLIDE 12

The components

trait Web extends HttpIO { this: Api with ServerCore => val httpServer = actorSystem.actorOf(Props( new HttpServer(ioBridge, SingletonHandler(rootService))), name = "http-server" ) httpServer ! HttpServer.Bind("localhost", 8080) }

slide-13
SLIDE 13

The components

trait ServerCore { implicit def actorSystem: ActorSystem ... } trait Api { this: ServerCore => ... } trait Web { this: Api with ServerCore => ... } class Application(val actorSystem: ActorSystem) extends Api class Application(val actorSystem: ActorSystem) extends Web class Application(val actorSystem: ActorSystem) extends Api with Web class Application(val actorSystem: ActorSystem) extends ServerCore with Api with Web

slide-14
SLIDE 14

The components

  • bject Main extends App {

val system = ActorSystem("AkkaPatterns") class Application(val actorSystem: ActorSystem) extends ServerCore with Api with Web new Application(system) } trait ServerCore { implicit def actorSystem: ActorSystem ... }

slide-15
SLIDE 15

Let’s build something

Io Core Api Web Application Main

slide-16
SLIDE 16

Let’s build something

Application { POST /users (ru: RegisterUser) → 422: Failure 200: RegistedUser GET /users/{UserReference} → 404: None 200: Some(User) }

slide-17
SLIDE 17

The components

trait Api extends RouteConcatenation { this: ServerCore => val routes = new HomeService().route ~ new UserService().route val rootService = actorSystem.actorOf(Props( new RoutedHttpService(routes))) }

val route = path("users") { post { handleWith { ru: RegisterUser => (userRegistrationActor ? ru).mapTo[ Either[ApplicationFailure, RegisteredUser]] } } }

slide-18
SLIDE 18

A message!

class UserService(implicit val actorSystem: ActorSystem) extends Directives with UserServiceMarshallers with DefaultTimeout { def userRegistrationActor = actorSystem.actorFor("/user/application/registration") val route = path("users") { post { handleWith { ru: RegisterUser => (userActor ? ru).mapTo[ Either[ApplicationFailure, RegisteredUser]] } } } }

slide-19
SLIDE 19

A message!

case class RegisterUser( username: String, password: String, email: String, mobile: String)

slide-20
SLIDE 20

A message!

case class RegisterUser( username: String, password: String, email: EmailAddress, mobile: MobileNumber)

sealed trait Address case class EmailAddress(address: String) extends Address case class MobileNumber(number: String) extends Address

slide-21
SLIDE 21

A message!

case class RegisterUser( username: String, password: String, email: EmailAddress, mobile: MobileNumber)

sealed trait Address case class EmailAddress(address: String) extends Address case class MobileNumber(number: String) extends Address

case class RegisteredUser(user: User) trait ApplicationFailure case class ValidationFailed(…) extends ApplicationFailure case object UsernameTaken extends ApplicationFailure

slide-22
SLIDE 22

A message!

case class User(id: UserReference, username: String, hashedPassword: String, activationCode: Option[String], email: EmailAddress, mobile: MobileNumber) package object domain { type UserReference = UUID }

slide-23
SLIDE 23

A message!

class UserService(implicit val actorSystem: ActorSystem) extends Directives with UserServiceMarshallers with DefaultTimeout { def userRegistrationActor = actorSystem.actorFor("/user/application/registration") val route = path("users") { post { handleWith { ru: RegisterUser => (userRegistrationActor ? ru).mapTo[ Either[ApplicationFailure, RegisteredUser]] } } } }

slide-24
SLIDE 24

RegisterUser ApplicationFailure RegisteredUser

slide-25
SLIDE 25

RegisterUser ApplicationFailure RegisteredUser

slide-26
SLIDE 26

Hierarchies

http://en.wikipedia.org/wiki/Class_sketch

slide-27
SLIDE 27

The user actor structure

  • UserActor creates and supervises the
  • MessageDeliveryActor, which sends e-mails

and text messages

  • UserRegistrationActor, which takes care of

the registration (and sends the activation code using the MessageDeliveryActor)

  • UserManagementActor, which provides the

boring “management” operations

slide-28
SLIDE 28

The user actor structure

class UserActor extends Actor { val messageDelivery = context.actorOf(Props[MessageDeliveryActor]) val registration = context.actorOf( Props(new UserRegistrationActor(messageDelivery)), "registration") val management = context.actorOf( Props(new UserManagementActor(messageDelivery)), "management") ... } class MessageDeliveryActor extends Actor class UserRegistrationActor(md: ActorRef) extends Actor class UserManagementActor(md: ActorRef) extends Actor

slide-29
SLIDE 29

The user actor structure

class UserRegistrationActor(messageDelivery: ActorRef) extends Actor { def receive = { case RegisterUser(username, password, email, mobile) => val user = ... // prepare the User instance messageDelivery ! DeliverActivationCode( mobile, user.activationCode.get) sender ! Right(RegisteredUser(user)) } }

slide-30
SLIDE 30

The user actor structure

class MessageDeliveryActor extends Actor { def receive = { case DeliverActivationCode(MobileNumber(number), code) => deliverTextMessage(number, "Your code is " + code) } }

slide-31
SLIDE 31

The user actor structure

class MessageDeliveryActor extends Actor { this: TextMessageDelivery => def receive = { case DeliverActivationCode(MobileNumber(number), code) => deliverTextMessage(number, "Your code is " + code) } }

slide-32
SLIDE 32

The user actor structure

class MessageDeliveryActor extends Actor { this: TextMessageDelivery => def receive = { case DeliverActivationCode(MobileNumber(number), code) => deliverTextMessage(number, "Your code is " + code) } } trait TextMessageDelivery { def deliverTextMessage(number: String, message: String) }

slide-33
SLIDE 33

The user actor structure

class MessageDeliveryActor extends Actor { this: TextMessageDelivery => def receive = { case DeliverActivationCode(MobileNumber(number), code) => deliverTextMessage(number, "Your code is " + code) } } trait TextMessageDelivery { def deliverTextMessage(number: String, message: String) } trait NexmoTextMessageDelivery extends TextMessageDelivery { this: HttpIO => private lazy val pipeline = HttpConduit.sendReceive(makeConduit("rest.nexmo.com")) def deliverTextMessage(number: String, message: String) { val request = HttpRequest(POST, "https://...") pipeline(request) onSuccess { ... } } }

slide-34
SLIDE 34

The user actor structure

class UserActor extends Actor { val messageDelivery = context.actorOf(Props[MessageDeliveryActor]) val registration = context.actorOf( Props(new UserRegistrationActor(messageDelivery)), "registration") val management = context.actorOf( Props(new UserManagementActor(messageDelivery)), "management") ... }

slide-35
SLIDE 35

The user actor structure

class UserActor extends Actor { val messageDelivery = context.actorOf(Props( new MessageDeliveryActor with MexmoTextMessageDelivery with ActorHttpIO)) val registration = context.actorOf( Props(new UserRegistrationActor(messageDelivery)), "registration") val management = context.actorOf( Props(new UserManagementActor(messageDelivery)), "management") ... }

slide-36
SLIDE 36

Testing

slide-37
SLIDE 37

Observe the messages

class UserRegistrationActorSpec extends TestKit(ActorSystem()) with Specification with ImplicitSender { val actor = TestActorRef( new UserRegistrationActor(testActor)) "register the user and send the code" in { actor ! RegisterUser("janm", "*********", ...) val code = expectMsgType[DeliverActivationCode] val user = expectMsgType[Either[ ApplicationFailure, RegisteredUser]] user.activationCode mustEqual Some(code) } }

case RegisterUser(username, password, email, mobile) => val user = ... // prepare the User instance messageDelivery ! DeliverActivationCode( mobile, user.activationCode.get) sender ! Right(RegisteredUser(user))

slide-38
SLIDE 38

Observe the messages

class UserRegistrationActorIntegrationSpec extends TestKit(ActorSystem()) with Specification with ImplicitSender with CoreSystem { val actor = system.actorFor( "/user/application/user/registration") "register the user and send the code" in { actor ! RegisterUser("janm", "*********", ...) val user = expectMsgType[Either[ ApplicationFailure, RegisteredUser]] ... } }

slide-39
SLIDE 39

Observe the messages

class UserServiceSpec extends TestKit(ActorSystem()) with TestKitRouteTest with Specification with ImplicitSender with CoreSystem with Api { "register the user and send the code" in { val username = "janm" Post(login, RegisterUser(username, "****", ...)) ~> routes ~> check { val resp = entityAs[RegisteredUser] resp.user.username mustEqual username } } }

case class RegisteredUser(user: User)

slide-40
SLIDE 40

Scala, Akka, Spray, ...

  • Akka brings asynchronous & non–blocking

behaviour (!, ?) and ability to dynamically change the structure of your application at runtime

  • Spray wraps it in REST API
  • Specs2 simplifies very expressive tests
  • Scala gives type safety and expressiveness,

functions as first-class citizens (directives in the

route), very flexible way to “inject” dependencies

slide-41
SLIDE 41