SlideShare a Scribd company logo
Functional programming
techniques in real-world
microservices
07. 04. 2017
Andras Papp
Tech Lead @
THE B2C
MARKETING CLOUD

truly personalised
interactions
250,000+ campaigns / month

10+ billion messages /month

2+ billion end users
The tale of Prince Scalarot
The abandoned castle
of Princess Monada
The three trials
How should I change specific
values of a complex immutable
data structure?
Question 1
Unified profile of a customer
BigQuery
Unified Profile 

Service
Products
Campaign names
…
Events
ContactProfile(

lastEvents = LastEvents(

click = LastEvent(campaign = Campaign(id = 100), "iPhone")

…

),

purchases = PurchaseInfo(

last = Some(SinglePurchase(

campaign = Campaign(id = 101), quantity = 2

))
…

)
…
…

)
ContactProfile(

lastEvents = LastEvents(

click = LastEvent(campaign = Campaign(id = 100), “iPhone")
…

),

purchases = PurchaseInfo(

last = Some(SinglePurchase(

campaign = Campaign(id = 100), quantity = 2

))
…

)
…
…

)
Transformation
Campaign(id = 100)
Campaign(
id = 100,
name = "Abandoned Shopping Cart Campaign”

)
Immutable domain entities
Always creating a copy:
val campaignWithName = campaign.copy(
name = "Abandoned Shopping Cart Campaign”
)
Copyception
… and we have only updated ONE campaign
contactProfile.copy(

lastEvents = contactProfile.lastEvents.copy(

send = contactProfile.lastEvents.send.copy(

campaign = replaceCampaignWithName(campaignNames)

(contactProfile.lastEvents.send.campaign)

)

)

)
Lens
Type of the object to update is O

Type of the field to update is V
case class Lens[O, V] (

get: O => V,

set: (O, V) => O

)
Example lens instance
val campaignNameLens = Lens[Campaign, String](

get = _.name,

set = (campaign, name) => campaign.copy(name = name)

)



val campaign = Campaign(id = 1, name = "old name")



campaignNameLens.set(campaign, "new name")
// Campaign(1, "new name")
Composing lenses
def compose[Outer, Inner, Value](

outer: Lens[Outer, Inner],

inner: Lens[Inner, Value]

) = Lens[Outer, Value](


get = outer.get andThen inner.get,

set = (obj, value) => outer.set(

obj,

inner.set(outer.get(obj), value)

)

)
Composing lenses
val lastSendEvent = LastEvent(
campaign = Campaign(id = 100, name = “"),
device = “iPhone"
)



val lastEventCampaignLens = Lens[LastEvent, Campaign](

get = _.campaign,

set = (lastEvent, campaign) =>
lastEvent.copy(campaign = campaign)
)

val campaignNameLens = Lens[Campaign, String](

get = _.name,

set = (campaign, name) =>
campaign.copy(name = name)
)


val lastEventCampaignNameLens = compose(
lastEventCampaignLens, campaignNameLens)


lastEventCampaignNameLens.set(lastSendEvent, "Campaign Name")
Lens implementations
Monocle

Quicklens

Shapeless

Scalaz
Solution of Sir Lensalot
Using composed lenses
ContactProfile(

lastEvents = LastEvents(

click = LastEvent(campaign = Campaign(id = 100), “iPhone")
…

),

purchases = PurchaseInfo(

last = Some(SinglePurchase(

campaign = Campaign(id = 100), quantity = 2

))
…

)
…
…

)
val clickCampaignLens = 

lens[ContactProfile]
>> 'lastEvents
>> 'click
>> ‘campaign


val lastPurchaseLens =

lens[ContactProfile]
>> 'purchases
>> ‘last
Solving the problem
Define lenses using shapeless
def modifyBy(lens: Lens[ContactProfile, Campaign]) = 

(contactProfile: ContactProfile) => 

lens.modify(contactProfile)
(replaceCampaignWithName(campaignNames))


val contactProfileWithCampaignNames =

(
modifyBy(clickCampaignLens) andThen

modifyBy(lastPurchaseLens)
)(contactProfile)
Solving the problem
How can I convert between a third
party library’s representation and
my case classes in a generic way?
Question 2
def executeQuery(query: Query): Future[List[TableRow]] =
{

queryResult(query).map { result =>

if (result.getTotalRows.equals(BigInteger.ZERO)) {

List.empty[TableRow]

} else {

result.getRows.asScala.toList

}

}

}
Google BigQuery API
Running a query job in BigQuery results
in a list of TableRow objects
case class ClickEvent(campaignId: Int, eventTime: DateTime,
deviceName: String, customerId: Int, messageId: Int)


def loadClicks(contact: Contact): Future[List[ClickEvent]] = {

executeQuery(createQuery).map(_.map(

tableRow => {

val cells = tableRow.getF

ClickEvent(

campaignId = cells.get(0).getV.toString.toInt
eventTime = new DateTime(cells.get(1).getV.toString.toLong),
deviceName = cells.get(1).getV.toString

)

}

))

}
TableRow - model
We would like to work with our models
Solution of Sir General
Using generic programming
def executeQuery[T](query: Query): Future[List[T]] = {

queryResult(query).map { result =>

if (result.getTotalRows.equals(BigInteger.ZERO)) {

List.empty[T]

} else {

result.getRows.asScala.toList.map(_.as[T])

}

}

}



def loadClicks(contact: Contact): Future[List[ClickEvent]] = 

executeQuery[ClickEvent](createQuery(contact, clickTable))
Aim for a generic solution
Step 1: Move mapping
to type classes
trait BigQueryFormat[T] {

def fromTableRow(tableRow: TableRow): T

}



implicit val clickEventFormat = new BigQueryFormat[ClickEvent] {

def fromTableRow(tableRow: TableRow) = {

val cells = tableRow.getF

ClickEvent(

campaignId = cells.get(0).getV.toString.toInt,

eventTime = new DateTime(cells.get(1).getV.toString.toLong),

deviceName = cells.get(2).getV.toString

)

}

}
BigQueryFormat type class
BigQueryFormat trait and one simplified
concrete instance for ClickEvent
object syntax {



implicit class RichTableRow(val row: TableRow)
extends AnyVal {


def as[T]
(implicit format: BigQueryFormat[T]): T =
format.fromTableRow(row)
}



}
Syntax
Add ‘as’ function to TableRow
"create ClickEvent case class from TableRow" in {

val tableRow = new TableRow()

val now = DateTime.now

tableRow.setF(List[TableCell](

new TableCell().setV(1),

new TableCell().setV(now.getMillis),

new TableCell().setV("iPhone")

).asJava)



tableRow.as[ClickEvent] shouldEqual
ClickEvent(1, now, "iPhone")

}
Test it!
Convert a test TableRow to a case class
with our syntax: ’as’
What have we
achieved?
Step 2: Get rid of all
the mapping code
Sir General
visited the
sorcerer
The plan
Type class for cell values
trait BigQueryValue[T] {

def fromValue(v: AnyRef): T

}



implicit object stringPrimitive extends BigQueryValue[String] {

def fromValue(v: AnyRef) = v.toString

}



implicit object intPrimitive extends BigQueryValue[Int] {

def fromValue(v: AnyRef) = v.toString.toInt

}



implicit object BoolPrimitive extends BigQueryValue[Boolean] {

def fromValue(v: AnyRef) = v.toString.toBoolean

}



implicit object DatePrimitive extends BigQueryValue[DateTime] {

def fromValue(v: AnyRef) = new DateTime(v.toString.toLong)

}
…
BigQueryFormat for HList
General representation
ClickEvent(1, now, "iPhone")

1 :: now :: "iPhone" :: HNil
implicit object hNilBigQueryFormat
extends BigQueryFormat[HNil] {
def fromTableRow(m: TableRow) = HNil

}
BigQueryFormat for HList
Convert to the members of the HList
recursively, ultimately to HNil
implicit def hListBigQueryFormat[Head, Tail <: HList](

implicit valueFormatter: BigQueryValue[Head],

tailFormatter: BigQueryFormat[Tail]

): BigQueryFormat[Head :: Tail] = {



new BigQueryFormat[Head :: Tail] {



def fromTableRow(tableRow: TableRow) = {

val tableCells = tableRow.getF.toList

val cellValue = tableCells.head.getV

val resolvedValue = valueFormatter.fromValue(cellValue)

tableRow.setF(tableCells.tail.asJava)

val tail = tailFormatter.fromTableRow(tableRow)

resolvedValue :: tail

}

}

}
BigQueryFormat for HList
But what about HList?
implicit def hListBigQueryFormat[Head, Tail <: HList](

implicit valueFormatter: BigQueryValue[Head],

tailFormatter: BigQueryFormat[Tail]

): BigQueryFormat[Head :: Tail] = {



new BigQueryFormat[Head :: Tail] {



def fromTableRow(tableRow: TableRow) = {

val tableCells = tableRow.getF.toList

val cellValue = tableCells.head.getV

val resolvedValue = valueFormatter.fromValue(cellValue)

tableRow.setF(tableCells.tail.asJava)

val tail = tailFormatter.fromTableRow(tableRow)

resolvedValue :: tail

}

}

}
BigQueryFormat for HList
implicit def hListBigQueryFormat[Head, Tail <: HList](

implicit valueFormatter: BigQueryValue[Head],

tailFormatter: BigQueryFormat[Tail]

): BigQueryFormat[Head :: Tail] = {



new BigQueryFormat[Head :: Tail] {



def fromTableRow(tableRow: TableRow) = {

val tableCells = tableRow.getF.toList

val cellValue = tableCells.head.getV

val resolvedValue = valueFormatter.fromValue(cellValue)

tableRow.setF(tableCells.tail.asJava)

val tail = tailFormatter.fromTableRow(tableRow)

resolvedValue :: tail

}

}

}
BigQueryFormat for HList
What is happening?
Generic BigQueryFormat
Generic type class instance

BigQueryFormat for generic
representation

Create HList representation

Convert to expected type
Generic BigQueryFormat
implicit def genericBigQueryFormat[T, Repr](

implicit gen: Generic.Aux[T, Repr],

reprFormatter: BigQueryFormat[Repr]

): BigQueryFormat[T] = {



new BigQueryFormat[T] {

def fromTableRow(tableRow: TableRow) = {

val repr = reprFormatter.fromTableRow(tableRow)

gen.from(repr)

}

}



}
What is Aux?
Nice job!
We can remove all the concrete type
class instances for our model classes!
Related topics
Support ADTs

Field names of case classes

Handle conversion errors
Our library is open source!
https://github.com/emartech/
googlecloud-shapeless

Contribution is welcome!
How can I write pure functions
if I have to do a bunch of IO
operations?
Question 3
Overview
Microservices do a lot of API calls

There is a data store involved in most
cases

An IO operation depends on the result
of another IO operation
A sample side-
effecting function
A sample side-effecting
function
def create(customerId: Int,

fieldsToUse: List[ContactField],

contactApiClient: ContactFieldApi

): Future[List[FieldItem]] = {

for {

existingFields <- contactApiClient.list(customerId).map(_.data)

fieldRequests = FieldPayload.create(existingFields, fieldsToUse)

newFields <- createNewFields(fieldRequests, contactApiClient)

} yield newFields

}



private def createNewFields(

fieldRequests: List[CreateFieldRequest],

contactApiClient: ContactFieldApi

): Future[List[FieldItem]] = {

Future.sequence(fieldRequests.map(request => {

contactApiClient

.createField(request)

.map(createFieldItem(request))

}))

}
A sample side-effecting
function
def create(customerId: Int,

fieldsToUse: List[ContactField],

contactApiClient: ContactFieldApi

): Future[List[FieldItem]] = {

for {

existingFields <- contactApiClient.list(customerId).map(_.data)

fieldRequests = FieldPayload.create(existingFields, fieldsToUse)

newFields <- createNewFields(fieldRequests, contactApiClient)

} yield newFields

}



private def createNewFields(

fieldRequests: List[CreateFieldRequest],

contactApiClient: ContactFieldApi

): Future[List[FieldItem]] = {

Future.sequence(fieldRequests.map(request => {

contactApiClient

.createField(request)

.map(createFieldItem(request))

}))

}
Issues with this approach
Hard to unit test

Dependency management
complications

Business logic & the environment

Future on the interface

Future in function body
Solution of Sir Effectus
Using type classes
Type classes again!
Let’s create a type class for type classes!
RestIO type class
trait RestIO[F[_]] {



def listFields(customerId: Int): F[List[FieldItem]]



def createField(payload: CreateFieldRequest): F[CreateFieldResponse]



}



object RestIO {

object syntax {



def listFields[F[_]](customerId: Int)(implicit ev: RestIO[F]) =
ev.listFields(customerId)


def createField[F[_]](payload: CreateFieldRequest)
(implicit ev: RestIO[F]) =
ev.createField(payload)

}
}
Instance for Future
object ClientInstance {



implicit val suiteClientInstance = new RestIO[Future]
{



val contactApiClient = ContactFieldApi()



def listFields(customerId: Int)
: Future[List[FieldItem]] =

contactApiClient.list(customerId).map(_.data)



def createField(payload: CreateFieldRequest)
: Future[CreateFieldResponse] =
contactApiClient.createField(customerId, payload)
}
}
Changes in client code
[error] value map is not a member of type parameter F[List[FieldItem]]
import RestIO.syntax._



def create[F[_]: RestIO](

customerId: Int, 

fieldsToUse: List[ContactField]

): F[List[FieldItem]] = {

for {

existingFields <- listFields(customerId)

fieldRequests = FieldPayload.create(existingFields, fieldsToUse)

newFields <- createNewFields(fieldRequests)

} yield newFields

}
The interface is generic for the type class

Client class dependency is removed
Does it work?
[error] value map is not a member of type parameter F[List[FieldItem]]
import RestIO.syntax._



def create[F[_]: RestIO](

customerId: Int, 

fieldsToUse: List[ContactField]

): F[List[FieldItem]] = {

for {

existingFields <- listFields(customerId)

fieldRequests = FieldPayload.create(existingFields, fieldsToUse)

newFields <- createNewFields(fieldRequests)

} yield newFields

}
Monad to the rescue!
[error] value map is not a member of type parameter F[List[FieldItem]]
import cats._
import RestIO.syntax._



def create[F[_]: RestIO: Monad](

customerId: Int, 

fieldsToUse: List[ContactField]

): F[List[FieldItem]] = {

for {

existingFields <- listFields(customerId)

fieldRequests =
FieldPayload.create(existingFields, fieldsToUse)

newFields <- createNewFields(fieldRequests)

} yield newFields

}
Let’s use cats!
Changes in client code
[error] value map is not a member of type parameter F[List[FieldItem]]
private def createNewFields[F[_]: RestIO: Monad](

customerId: Int,

fieldRequests: List[CreateFieldRequest]

): F[List[FieldItem]] = {

fieldRequests.map(request => {

createField(request)

.map(createFieldItem(request))

}).sequence

}
Got rid of Future.sequence
Testing
new instance of RestIO for testing

simple stub

Id monad
Let’s create an instance for
testing!
trait FakeRestIO {



val expectedFieldListResponse
: Map[Int, List[FieldItem]] = Map()
…


implicit val fakeClientInstance = new RestIO[Id] {



override def createField(payload: CreateFieldRequest)
: Id[CreateFieldResponse] = {
expectedCreateFieldResponse(payload)

}
…

}



}
A sample test case
"create segment with new custom field" in new FakeRestIO {

override val expectedFieldListResponse = Map(

customerId -> List(

FieldItem(101, Some("Custom Email"))

)

)

val fieldsToUse = List(

ContactField("Custom Field"),

ContactField("Custom Email")

)

override val expectedCreateFieldResponse = Map(

CreateFieldRequest("Custom Field") ->
CreateFieldResponse(1001)
)

val expectedNewField = List(
FieldItem(1001, Some("Custom Field”))
)
create(customerId, fieldsToUse) shouldBe expectedNewField

}
Conclusions
More generic

Business logic has no side effects

Testing without Future

Dependency management by the
compiler
Learn more about type
classes
https://github.com/hablapps/
typeclassesforthemasses



Hablapps
Learn more about shapeless
https://github.com/underscoreio/
shapeless-guide
Thank You!
Q&A

More Related Content

What's hot

The Ring programming language version 1.5.2 book - Part 43 of 181
The Ring programming language version 1.5.2 book - Part 43 of 181The Ring programming language version 1.5.2 book - Part 43 of 181
The Ring programming language version 1.5.2 book - Part 43 of 181
Mahmoud Samir Fayed
 
Groovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony CodeGroovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony Code
stasimus
 
Sistema de ventas
Sistema de ventasSistema de ventas
Sistema de ventas
DAYANA RETO
 
The Ring programming language version 1.10 book - Part 54 of 212
The Ring programming language version 1.10 book - Part 54 of 212The Ring programming language version 1.10 book - Part 54 of 212
The Ring programming language version 1.10 book - Part 54 of 212
Mahmoud Samir Fayed
 
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night TurinAsync code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Fabio Collini
 
The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189
Mahmoud Samir Fayed
 
Scala best practices
Scala best practicesScala best practices
Scala best practices
Alexander Zaidel
 
The Ring programming language version 1.5.3 book - Part 44 of 184
The Ring programming language version 1.5.3 book - Part 44 of 184The Ring programming language version 1.5.3 book - Part 44 of 184
The Ring programming language version 1.5.3 book - Part 44 of 184
Mahmoud Samir Fayed
 
mobl: Een DSL voor mobiele applicatieontwikkeling
mobl: Een DSL voor mobiele applicatieontwikkelingmobl: Een DSL voor mobiele applicatieontwikkeling
mobl: Een DSL voor mobiele applicatieontwikkeling
Devnology
 
mobl presentation @ IHomer
mobl presentation @ IHomermobl presentation @ IHomer
mobl presentation @ IHomer
zefhemel
 
The Ring programming language version 1.3 book - Part 34 of 88
The Ring programming language version 1.3 book - Part 34 of 88The Ring programming language version 1.3 book - Part 34 of 88
The Ring programming language version 1.3 book - Part 34 of 88
Mahmoud Samir Fayed
 
Corona sdk
Corona sdkCorona sdk
qooxdoo Form Management
qooxdoo Form Managementqooxdoo Form Management
qooxdoo Form Management
Martin Wittemann
 
RxSwift 시작하기
RxSwift 시작하기RxSwift 시작하기
RxSwift 시작하기
Suyeol Jeon
 
Fabric.js @ Falsy Values
Fabric.js @ Falsy ValuesFabric.js @ Falsy Values
Fabric.js @ Falsy Values
Juriy Zaytsev
 
The Ring programming language version 1.5.2 book - Part 29 of 181
The Ring programming language version 1.5.2 book - Part 29 of 181The Ring programming language version 1.5.2 book - Part 29 of 181
The Ring programming language version 1.5.2 book - Part 29 of 181
Mahmoud Samir Fayed
 
The Rule of 10,000 Spark Jobs - Learning from Exceptions and Serializing Your...
The Rule of 10,000 Spark Jobs - Learning from Exceptions and Serializing Your...The Rule of 10,000 Spark Jobs - Learning from Exceptions and Serializing Your...
The Rule of 10,000 Spark Jobs - Learning from Exceptions and Serializing Your...
Matthew Tovbin
 
The Rule of 10,000 Spark Jobs: Learning From Exceptions and Serializing Your ...
The Rule of 10,000 Spark Jobs: Learning From Exceptions and Serializing Your ...The Rule of 10,000 Spark Jobs: Learning From Exceptions and Serializing Your ...
The Rule of 10,000 Spark Jobs: Learning From Exceptions and Serializing Your ...
Databricks
 
Famo.us: From Zero to UI
Famo.us: From Zero to UIFamo.us: From Zero to UI
Famo.us: From Zero to UI
timjchin
 
The Ring programming language version 1.9 book - Part 52 of 210
The Ring programming language version 1.9 book - Part 52 of 210The Ring programming language version 1.9 book - Part 52 of 210
The Ring programming language version 1.9 book - Part 52 of 210
Mahmoud Samir Fayed
 

What's hot (20)

The Ring programming language version 1.5.2 book - Part 43 of 181
The Ring programming language version 1.5.2 book - Part 43 of 181The Ring programming language version 1.5.2 book - Part 43 of 181
The Ring programming language version 1.5.2 book - Part 43 of 181
 
Groovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony CodeGroovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony Code
 
Sistema de ventas
Sistema de ventasSistema de ventas
Sistema de ventas
 
The Ring programming language version 1.10 book - Part 54 of 212
The Ring programming language version 1.10 book - Part 54 of 212The Ring programming language version 1.10 book - Part 54 of 212
The Ring programming language version 1.10 book - Part 54 of 212
 
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night TurinAsync code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
 
The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189
 
Scala best practices
Scala best practicesScala best practices
Scala best practices
 
The Ring programming language version 1.5.3 book - Part 44 of 184
The Ring programming language version 1.5.3 book - Part 44 of 184The Ring programming language version 1.5.3 book - Part 44 of 184
The Ring programming language version 1.5.3 book - Part 44 of 184
 
mobl: Een DSL voor mobiele applicatieontwikkeling
mobl: Een DSL voor mobiele applicatieontwikkelingmobl: Een DSL voor mobiele applicatieontwikkeling
mobl: Een DSL voor mobiele applicatieontwikkeling
 
mobl presentation @ IHomer
mobl presentation @ IHomermobl presentation @ IHomer
mobl presentation @ IHomer
 
The Ring programming language version 1.3 book - Part 34 of 88
The Ring programming language version 1.3 book - Part 34 of 88The Ring programming language version 1.3 book - Part 34 of 88
The Ring programming language version 1.3 book - Part 34 of 88
 
Corona sdk
Corona sdkCorona sdk
Corona sdk
 
qooxdoo Form Management
qooxdoo Form Managementqooxdoo Form Management
qooxdoo Form Management
 
RxSwift 시작하기
RxSwift 시작하기RxSwift 시작하기
RxSwift 시작하기
 
Fabric.js @ Falsy Values
Fabric.js @ Falsy ValuesFabric.js @ Falsy Values
Fabric.js @ Falsy Values
 
The Ring programming language version 1.5.2 book - Part 29 of 181
The Ring programming language version 1.5.2 book - Part 29 of 181The Ring programming language version 1.5.2 book - Part 29 of 181
The Ring programming language version 1.5.2 book - Part 29 of 181
 
The Rule of 10,000 Spark Jobs - Learning from Exceptions and Serializing Your...
The Rule of 10,000 Spark Jobs - Learning from Exceptions and Serializing Your...The Rule of 10,000 Spark Jobs - Learning from Exceptions and Serializing Your...
The Rule of 10,000 Spark Jobs - Learning from Exceptions and Serializing Your...
 
The Rule of 10,000 Spark Jobs: Learning From Exceptions and Serializing Your ...
The Rule of 10,000 Spark Jobs: Learning From Exceptions and Serializing Your ...The Rule of 10,000 Spark Jobs: Learning From Exceptions and Serializing Your ...
The Rule of 10,000 Spark Jobs: Learning From Exceptions and Serializing Your ...
 
Famo.us: From Zero to UI
Famo.us: From Zero to UIFamo.us: From Zero to UI
Famo.us: From Zero to UI
 
The Ring programming language version 1.9 book - Part 52 of 210
The Ring programming language version 1.9 book - Part 52 of 210The Ring programming language version 1.9 book - Part 52 of 210
The Ring programming language version 1.9 book - Part 52 of 210
 

Similar to Functional programming techniques in real-world microservices

Scala on Your Phone
Scala on Your PhoneScala on Your Phone
Scala on Your Phone
Michael Galpin
 
Data in Motion: Streaming Static Data Efficiently
Data in Motion: Streaming Static Data EfficientlyData in Motion: Streaming Static Data Efficiently
Data in Motion: Streaming Static Data Efficiently
Martin Zapletal
 
Flink Forward San Francisco 2018: Seth Wiesman - "Testing Stateful Streaming ...
Flink Forward San Francisco 2018: Seth Wiesman - "Testing Stateful Streaming ...Flink Forward San Francisco 2018: Seth Wiesman - "Testing Stateful Streaming ...
Flink Forward San Francisco 2018: Seth Wiesman - "Testing Stateful Streaming ...
Flink Forward
 
Serverless stateful
Serverless statefulServerless stateful
Serverless stateful
Patrick Di Loreto
 
ddd+scala
ddd+scaladdd+scala
ddd+scala
潤一 加藤
 
The Ring programming language version 1.7 book - Part 16 of 196
The Ring programming language version 1.7 book - Part 16 of 196The Ring programming language version 1.7 book - Part 16 of 196
The Ring programming language version 1.7 book - Part 16 of 196
Mahmoud Samir Fayed
 
The Ring programming language version 1.6 book - Part 15 of 189
The Ring programming language version 1.6 book - Part 15 of 189The Ring programming language version 1.6 book - Part 15 of 189
The Ring programming language version 1.6 book - Part 15 of 189
Mahmoud Samir Fayed
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
JDD 2016 - Pawel Byszewski - Kotlin, why?
JDD 2016 - Pawel Byszewski - Kotlin, why?JDD 2016 - Pawel Byszewski - Kotlin, why?
JDD 2016 - Pawel Byszewski - Kotlin, why?
PROIDEA
 
React new features and intro to Hooks
React new features and intro to HooksReact new features and intro to Hooks
React new features and intro to Hooks
Soluto
 
Knockoutjs UG meeting presentation
Knockoutjs UG meeting presentationKnockoutjs UG meeting presentation
Knockoutjs UG meeting presentation
Valdis Iljuconoks
 
Kotlin, why?
Kotlin, why?Kotlin, why?
Kotlin, why?
Paweł Byszewski
 
Model View Intent on Android
Model View Intent on AndroidModel View Intent on Android
Model View Intent on Android
Cody Engel
 
WPF L02-Graphics, Binding and Animation
WPF L02-Graphics, Binding and AnimationWPF L02-Graphics, Binding and Animation
WPF L02-Graphics, Binding and Animation
Mohammad Shaker
 
The Ring programming language version 1.5.3 book - Part 70 of 184
The Ring programming language version 1.5.3 book - Part 70 of 184The Ring programming language version 1.5.3 book - Part 70 of 184
The Ring programming language version 1.5.3 book - Part 70 of 184
Mahmoud Samir Fayed
 
Scala+swing
Scala+swingScala+swing
Scala+swing
perneto
 
Goal Based Data Production with Sim Simeonov
Goal Based Data Production with Sim SimeonovGoal Based Data Production with Sim Simeonov
Goal Based Data Production with Sim Simeonov
Databricks
 

Similar to Functional programming techniques in real-world microservices (20)

Scala on Your Phone
Scala on Your PhoneScala on Your Phone
Scala on Your Phone
 
Data in Motion: Streaming Static Data Efficiently
Data in Motion: Streaming Static Data EfficientlyData in Motion: Streaming Static Data Efficiently
Data in Motion: Streaming Static Data Efficiently
 
Flink Forward San Francisco 2018: Seth Wiesman - "Testing Stateful Streaming ...
Flink Forward San Francisco 2018: Seth Wiesman - "Testing Stateful Streaming ...Flink Forward San Francisco 2018: Seth Wiesman - "Testing Stateful Streaming ...
Flink Forward San Francisco 2018: Seth Wiesman - "Testing Stateful Streaming ...
 
Serverless stateful
Serverless statefulServerless stateful
Serverless stateful
 
ddd+scala
ddd+scaladdd+scala
ddd+scala
 
The Ring programming language version 1.7 book - Part 16 of 196
The Ring programming language version 1.7 book - Part 16 of 196The Ring programming language version 1.7 book - Part 16 of 196
The Ring programming language version 1.7 book - Part 16 of 196
 
The Ring programming language version 1.6 book - Part 15 of 189
The Ring programming language version 1.6 book - Part 15 of 189The Ring programming language version 1.6 book - Part 15 of 189
The Ring programming language version 1.6 book - Part 15 of 189
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
JDD 2016 - Pawel Byszewski - Kotlin, why?
JDD 2016 - Pawel Byszewski - Kotlin, why?JDD 2016 - Pawel Byszewski - Kotlin, why?
JDD 2016 - Pawel Byszewski - Kotlin, why?
 
React new features and intro to Hooks
React new features and intro to HooksReact new features and intro to Hooks
React new features and intro to Hooks
 
Knockoutjs UG meeting presentation
Knockoutjs UG meeting presentationKnockoutjs UG meeting presentation
Knockoutjs UG meeting presentation
 
Kotlin, why?
Kotlin, why?Kotlin, why?
Kotlin, why?
 
Model View Intent on Android
Model View Intent on AndroidModel View Intent on Android
Model View Intent on Android
 
WPF L02-Graphics, Binding and Animation
WPF L02-Graphics, Binding and AnimationWPF L02-Graphics, Binding and Animation
WPF L02-Graphics, Binding and Animation
 
The Ring programming language version 1.5.3 book - Part 70 of 184
The Ring programming language version 1.5.3 book - Part 70 of 184The Ring programming language version 1.5.3 book - Part 70 of 184
The Ring programming language version 1.5.3 book - Part 70 of 184
 
Scala+swing
Scala+swingScala+swing
Scala+swing
 
Goal Based Data Production with Sim Simeonov
Goal Based Data Production with Sim SimeonovGoal Based Data Production with Sim Simeonov
Goal Based Data Production with Sim Simeonov
 

Recently uploaded

socradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdfsocradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdf
SOCRadar
 
Launch Your Streaming Platforms in Minutes
Launch Your Streaming Platforms in MinutesLaunch Your Streaming Platforms in Minutes
Launch Your Streaming Platforms in Minutes
Roshan Dwivedi
 
Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024
Paco van Beckhoven
 
A Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of PassageA Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of Passage
Philip Schwarz
 
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit ParisNeo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j
 
Hand Rolled Applicative User Validation Code Kata
Hand Rolled Applicative User ValidationCode KataHand Rolled Applicative User ValidationCode Kata
Hand Rolled Applicative User Validation Code Kata
Philip Schwarz
 
AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing SuiteAI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
Google
 
Mobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona InfotechMobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona Infotech
Drona Infotech
 
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit ParisNeo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j
 
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptxLORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
lorraineandreiamcidl
 
What is Augmented Reality Image Tracking
What is Augmented Reality Image TrackingWhat is Augmented Reality Image Tracking
What is Augmented Reality Image Tracking
pavan998932
 
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
Alina Yurenko
 
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Crescat
 
openEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain SecurityopenEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain Security
Shane Coughlan
 
ALGIT - Assembly Line for Green IT - Numbers, Data, Facts
ALGIT - Assembly Line for Green IT - Numbers, Data, FactsALGIT - Assembly Line for Green IT - Numbers, Data, Facts
ALGIT - Assembly Line for Green IT - Numbers, Data, Facts
Green Software Development
 
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdfAutomated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
timtebeek1
 
GraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph TechnologyGraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph Technology
Neo4j
 
SWEBOK and Education at FUSE Okinawa 2024
SWEBOK and Education at FUSE Okinawa 2024SWEBOK and Education at FUSE Okinawa 2024
SWEBOK and Education at FUSE Okinawa 2024
Hironori Washizaki
 
Empowering Growth with Best Software Development Company in Noida - Deuglo
Empowering Growth with Best Software  Development Company in Noida - DeugloEmpowering Growth with Best Software  Development Company in Noida - Deuglo
Empowering Growth with Best Software Development Company in Noida - Deuglo
Deuglo Infosystem Pvt Ltd
 
Graspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code AnalysisGraspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code Analysis
Aftab Hussain
 

Recently uploaded (20)

socradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdfsocradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdf
 
Launch Your Streaming Platforms in Minutes
Launch Your Streaming Platforms in MinutesLaunch Your Streaming Platforms in Minutes
Launch Your Streaming Platforms in Minutes
 
Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024
 
A Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of PassageA Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of Passage
 
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit ParisNeo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
 
Hand Rolled Applicative User Validation Code Kata
Hand Rolled Applicative User ValidationCode KataHand Rolled Applicative User ValidationCode Kata
Hand Rolled Applicative User Validation Code Kata
 
AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing SuiteAI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
 
Mobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona InfotechMobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona Infotech
 
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit ParisNeo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
 
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptxLORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
 
What is Augmented Reality Image Tracking
What is Augmented Reality Image TrackingWhat is Augmented Reality Image Tracking
What is Augmented Reality Image Tracking
 
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
 
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
 
openEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain SecurityopenEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain Security
 
ALGIT - Assembly Line for Green IT - Numbers, Data, Facts
ALGIT - Assembly Line for Green IT - Numbers, Data, FactsALGIT - Assembly Line for Green IT - Numbers, Data, Facts
ALGIT - Assembly Line for Green IT - Numbers, Data, Facts
 
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdfAutomated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
 
GraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph TechnologyGraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph Technology
 
SWEBOK and Education at FUSE Okinawa 2024
SWEBOK and Education at FUSE Okinawa 2024SWEBOK and Education at FUSE Okinawa 2024
SWEBOK and Education at FUSE Okinawa 2024
 
Empowering Growth with Best Software Development Company in Noida - Deuglo
Empowering Growth with Best Software  Development Company in Noida - DeugloEmpowering Growth with Best Software  Development Company in Noida - Deuglo
Empowering Growth with Best Software Development Company in Noida - Deuglo
 
Graspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code AnalysisGraspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code Analysis
 

Functional programming techniques in real-world microservices

  • 1. Functional programming techniques in real-world microservices 07. 04. 2017
  • 3. THE B2C MARKETING CLOUD truly personalised interactions 250,000+ campaigns / month 10+ billion messages /month 2+ billion end users
  • 4. The tale of Prince Scalarot
  • 5. The abandoned castle of Princess Monada
  • 7. How should I change specific values of a complex immutable data structure? Question 1
  • 8.
  • 9.
  • 10. Unified profile of a customer BigQuery Unified Profile 
 Service Products Campaign names … Events
  • 11.
  • 12. ContactProfile(
 lastEvents = LastEvents(
 click = LastEvent(campaign = Campaign(id = 100), "iPhone")
 …
 ),
 purchases = PurchaseInfo(
 last = Some(SinglePurchase(
 campaign = Campaign(id = 101), quantity = 2
 )) …
 ) … …
 )
  • 13. ContactProfile(
 lastEvents = LastEvents(
 click = LastEvent(campaign = Campaign(id = 100), “iPhone") …
 ),
 purchases = PurchaseInfo(
 last = Some(SinglePurchase(
 campaign = Campaign(id = 100), quantity = 2
 )) …
 ) … …
 )
  • 14. Transformation Campaign(id = 100) Campaign( id = 100, name = "Abandoned Shopping Cart Campaign”
 )
  • 15. Immutable domain entities Always creating a copy: val campaignWithName = campaign.copy( name = "Abandoned Shopping Cart Campaign” )
  • 16. Copyception … and we have only updated ONE campaign contactProfile.copy(
 lastEvents = contactProfile.lastEvents.copy(
 send = contactProfile.lastEvents.send.copy(
 campaign = replaceCampaignWithName(campaignNames)
 (contactProfile.lastEvents.send.campaign)
 )
 )
 )
  • 17. Lens Type of the object to update is O Type of the field to update is V case class Lens[O, V] (
 get: O => V,
 set: (O, V) => O
 )
  • 18. Example lens instance val campaignNameLens = Lens[Campaign, String](
 get = _.name,
 set = (campaign, name) => campaign.copy(name = name)
 )
 
 val campaign = Campaign(id = 1, name = "old name")
 
 campaignNameLens.set(campaign, "new name") // Campaign(1, "new name")
  • 19. Composing lenses def compose[Outer, Inner, Value](
 outer: Lens[Outer, Inner],
 inner: Lens[Inner, Value]
 ) = Lens[Outer, Value]( 
 get = outer.get andThen inner.get,
 set = (obj, value) => outer.set(
 obj,
 inner.set(outer.get(obj), value)
 )
 )
  • 20. Composing lenses val lastSendEvent = LastEvent( campaign = Campaign(id = 100, name = “"), device = “iPhone" )
 
 val lastEventCampaignLens = Lens[LastEvent, Campaign](
 get = _.campaign,
 set = (lastEvent, campaign) => lastEvent.copy(campaign = campaign) )
 val campaignNameLens = Lens[Campaign, String](
 get = _.name,
 set = (campaign, name) => campaign.copy(name = name) ) 
 val lastEventCampaignNameLens = compose( lastEventCampaignLens, campaignNameLens) 
 lastEventCampaignNameLens.set(lastSendEvent, "Campaign Name")
  • 22. Solution of Sir Lensalot Using composed lenses
  • 23. ContactProfile(
 lastEvents = LastEvents(
 click = LastEvent(campaign = Campaign(id = 100), “iPhone") …
 ),
 purchases = PurchaseInfo(
 last = Some(SinglePurchase(
 campaign = Campaign(id = 100), quantity = 2
 )) …
 ) … …
 )
  • 24. val clickCampaignLens = 
 lens[ContactProfile] >> 'lastEvents >> 'click >> ‘campaign 
 val lastPurchaseLens =
 lens[ContactProfile] >> 'purchases >> ‘last Solving the problem Define lenses using shapeless
  • 25. def modifyBy(lens: Lens[ContactProfile, Campaign]) = 
 (contactProfile: ContactProfile) => 
 lens.modify(contactProfile) (replaceCampaignWithName(campaignNames)) 
 val contactProfileWithCampaignNames =
 ( modifyBy(clickCampaignLens) andThen
 modifyBy(lastPurchaseLens) )(contactProfile) Solving the problem
  • 26.
  • 27. How can I convert between a third party library’s representation and my case classes in a generic way? Question 2
  • 28.
  • 29. def executeQuery(query: Query): Future[List[TableRow]] = {
 queryResult(query).map { result =>
 if (result.getTotalRows.equals(BigInteger.ZERO)) {
 List.empty[TableRow]
 } else {
 result.getRows.asScala.toList
 }
 }
 } Google BigQuery API Running a query job in BigQuery results in a list of TableRow objects
  • 30. case class ClickEvent(campaignId: Int, eventTime: DateTime, deviceName: String, customerId: Int, messageId: Int) 
 def loadClicks(contact: Contact): Future[List[ClickEvent]] = {
 executeQuery(createQuery).map(_.map(
 tableRow => {
 val cells = tableRow.getF
 ClickEvent(
 campaignId = cells.get(0).getV.toString.toInt eventTime = new DateTime(cells.get(1).getV.toString.toLong), deviceName = cells.get(1).getV.toString
 )
 }
 ))
 } TableRow - model We would like to work with our models
  • 31. Solution of Sir General Using generic programming
  • 32. def executeQuery[T](query: Query): Future[List[T]] = {
 queryResult(query).map { result =>
 if (result.getTotalRows.equals(BigInteger.ZERO)) {
 List.empty[T]
 } else {
 result.getRows.asScala.toList.map(_.as[T])
 }
 }
 }
 
 def loadClicks(contact: Contact): Future[List[ClickEvent]] = 
 executeQuery[ClickEvent](createQuery(contact, clickTable)) Aim for a generic solution
  • 33. Step 1: Move mapping to type classes
  • 34. trait BigQueryFormat[T] {
 def fromTableRow(tableRow: TableRow): T
 }
 
 implicit val clickEventFormat = new BigQueryFormat[ClickEvent] {
 def fromTableRow(tableRow: TableRow) = {
 val cells = tableRow.getF
 ClickEvent(
 campaignId = cells.get(0).getV.toString.toInt,
 eventTime = new DateTime(cells.get(1).getV.toString.toLong),
 deviceName = cells.get(2).getV.toString
 )
 }
 } BigQueryFormat type class BigQueryFormat trait and one simplified concrete instance for ClickEvent
  • 35. object syntax {
 
 implicit class RichTableRow(val row: TableRow) extends AnyVal { 
 def as[T] (implicit format: BigQueryFormat[T]): T = format.fromTableRow(row) }
 
 } Syntax Add ‘as’ function to TableRow
  • 36. "create ClickEvent case class from TableRow" in {
 val tableRow = new TableRow()
 val now = DateTime.now
 tableRow.setF(List[TableCell](
 new TableCell().setV(1),
 new TableCell().setV(now.getMillis),
 new TableCell().setV("iPhone")
 ).asJava)
 
 tableRow.as[ClickEvent] shouldEqual ClickEvent(1, now, "iPhone")
 } Test it! Convert a test TableRow to a case class with our syntax: ’as’
  • 38. Step 2: Get rid of all the mapping code
  • 41. Type class for cell values trait BigQueryValue[T] {
 def fromValue(v: AnyRef): T
 }
 
 implicit object stringPrimitive extends BigQueryValue[String] {
 def fromValue(v: AnyRef) = v.toString
 }
 
 implicit object intPrimitive extends BigQueryValue[Int] {
 def fromValue(v: AnyRef) = v.toString.toInt
 }
 
 implicit object BoolPrimitive extends BigQueryValue[Boolean] {
 def fromValue(v: AnyRef) = v.toString.toBoolean
 }
 
 implicit object DatePrimitive extends BigQueryValue[DateTime] {
 def fromValue(v: AnyRef) = new DateTime(v.toString.toLong)
 } …
  • 42. BigQueryFormat for HList General representation ClickEvent(1, now, "iPhone")
 1 :: now :: "iPhone" :: HNil
  • 43. implicit object hNilBigQueryFormat extends BigQueryFormat[HNil] { def fromTableRow(m: TableRow) = HNil
 } BigQueryFormat for HList Convert to the members of the HList recursively, ultimately to HNil
  • 44. implicit def hListBigQueryFormat[Head, Tail <: HList](
 implicit valueFormatter: BigQueryValue[Head],
 tailFormatter: BigQueryFormat[Tail]
 ): BigQueryFormat[Head :: Tail] = {
 
 new BigQueryFormat[Head :: Tail] {
 
 def fromTableRow(tableRow: TableRow) = {
 val tableCells = tableRow.getF.toList
 val cellValue = tableCells.head.getV
 val resolvedValue = valueFormatter.fromValue(cellValue)
 tableRow.setF(tableCells.tail.asJava)
 val tail = tailFormatter.fromTableRow(tableRow)
 resolvedValue :: tail
 }
 }
 } BigQueryFormat for HList But what about HList?
  • 45. implicit def hListBigQueryFormat[Head, Tail <: HList](
 implicit valueFormatter: BigQueryValue[Head],
 tailFormatter: BigQueryFormat[Tail]
 ): BigQueryFormat[Head :: Tail] = {
 
 new BigQueryFormat[Head :: Tail] {
 
 def fromTableRow(tableRow: TableRow) = {
 val tableCells = tableRow.getF.toList
 val cellValue = tableCells.head.getV
 val resolvedValue = valueFormatter.fromValue(cellValue)
 tableRow.setF(tableCells.tail.asJava)
 val tail = tailFormatter.fromTableRow(tableRow)
 resolvedValue :: tail
 }
 }
 } BigQueryFormat for HList
  • 46. implicit def hListBigQueryFormat[Head, Tail <: HList](
 implicit valueFormatter: BigQueryValue[Head],
 tailFormatter: BigQueryFormat[Tail]
 ): BigQueryFormat[Head :: Tail] = {
 
 new BigQueryFormat[Head :: Tail] {
 
 def fromTableRow(tableRow: TableRow) = {
 val tableCells = tableRow.getF.toList
 val cellValue = tableCells.head.getV
 val resolvedValue = valueFormatter.fromValue(cellValue)
 tableRow.setF(tableCells.tail.asJava)
 val tail = tailFormatter.fromTableRow(tableRow)
 resolvedValue :: tail
 }
 }
 } BigQueryFormat for HList What is happening?
  • 47. Generic BigQueryFormat Generic type class instance BigQueryFormat for generic representation Create HList representation Convert to expected type
  • 48. Generic BigQueryFormat implicit def genericBigQueryFormat[T, Repr](
 implicit gen: Generic.Aux[T, Repr],
 reprFormatter: BigQueryFormat[Repr]
 ): BigQueryFormat[T] = {
 
 new BigQueryFormat[T] {
 def fromTableRow(tableRow: TableRow) = {
 val repr = reprFormatter.fromTableRow(tableRow)
 gen.from(repr)
 }
 }
 
 } What is Aux?
  • 49. Nice job! We can remove all the concrete type class instances for our model classes!
  • 50. Related topics Support ADTs Field names of case classes Handle conversion errors
  • 51. Our library is open source! https://github.com/emartech/ googlecloud-shapeless Contribution is welcome!
  • 52.
  • 53. How can I write pure functions if I have to do a bunch of IO operations? Question 3
  • 54.
  • 55. Overview Microservices do a lot of API calls There is a data store involved in most cases An IO operation depends on the result of another IO operation
  • 57. A sample side-effecting function def create(customerId: Int,
 fieldsToUse: List[ContactField],
 contactApiClient: ContactFieldApi
 ): Future[List[FieldItem]] = {
 for {
 existingFields <- contactApiClient.list(customerId).map(_.data)
 fieldRequests = FieldPayload.create(existingFields, fieldsToUse)
 newFields <- createNewFields(fieldRequests, contactApiClient)
 } yield newFields
 }
 
 private def createNewFields(
 fieldRequests: List[CreateFieldRequest],
 contactApiClient: ContactFieldApi
 ): Future[List[FieldItem]] = {
 Future.sequence(fieldRequests.map(request => {
 contactApiClient
 .createField(request)
 .map(createFieldItem(request))
 }))
 }
  • 58. A sample side-effecting function def create(customerId: Int,
 fieldsToUse: List[ContactField],
 contactApiClient: ContactFieldApi
 ): Future[List[FieldItem]] = {
 for {
 existingFields <- contactApiClient.list(customerId).map(_.data)
 fieldRequests = FieldPayload.create(existingFields, fieldsToUse)
 newFields <- createNewFields(fieldRequests, contactApiClient)
 } yield newFields
 }
 
 private def createNewFields(
 fieldRequests: List[CreateFieldRequest],
 contactApiClient: ContactFieldApi
 ): Future[List[FieldItem]] = {
 Future.sequence(fieldRequests.map(request => {
 contactApiClient
 .createField(request)
 .map(createFieldItem(request))
 }))
 }
  • 59. Issues with this approach Hard to unit test Dependency management complications Business logic & the environment Future on the interface Future in function body
  • 60. Solution of Sir Effectus Using type classes
  • 61. Type classes again! Let’s create a type class for type classes!
  • 62. RestIO type class trait RestIO[F[_]] {
 
 def listFields(customerId: Int): F[List[FieldItem]]
 
 def createField(payload: CreateFieldRequest): F[CreateFieldResponse]
 
 }
 
 object RestIO {
 object syntax {
 
 def listFields[F[_]](customerId: Int)(implicit ev: RestIO[F]) = ev.listFields(customerId) 
 def createField[F[_]](payload: CreateFieldRequest) (implicit ev: RestIO[F]) = ev.createField(payload)
 } }
  • 63. Instance for Future object ClientInstance {
 
 implicit val suiteClientInstance = new RestIO[Future] {
 
 val contactApiClient = ContactFieldApi()
 
 def listFields(customerId: Int) : Future[List[FieldItem]] =
 contactApiClient.list(customerId).map(_.data)
 
 def createField(payload: CreateFieldRequest) : Future[CreateFieldResponse] = contactApiClient.createField(customerId, payload) } }
  • 64. Changes in client code [error] value map is not a member of type parameter F[List[FieldItem]] import RestIO.syntax._
 
 def create[F[_]: RestIO](
 customerId: Int, 
 fieldsToUse: List[ContactField]
 ): F[List[FieldItem]] = {
 for {
 existingFields <- listFields(customerId)
 fieldRequests = FieldPayload.create(existingFields, fieldsToUse)
 newFields <- createNewFields(fieldRequests)
 } yield newFields
 } The interface is generic for the type class Client class dependency is removed
  • 65. Does it work? [error] value map is not a member of type parameter F[List[FieldItem]] import RestIO.syntax._
 
 def create[F[_]: RestIO](
 customerId: Int, 
 fieldsToUse: List[ContactField]
 ): F[List[FieldItem]] = {
 for {
 existingFields <- listFields(customerId)
 fieldRequests = FieldPayload.create(existingFields, fieldsToUse)
 newFields <- createNewFields(fieldRequests)
 } yield newFields
 }
  • 66. Monad to the rescue! [error] value map is not a member of type parameter F[List[FieldItem]] import cats._ import RestIO.syntax._
 
 def create[F[_]: RestIO: Monad](
 customerId: Int, 
 fieldsToUse: List[ContactField]
 ): F[List[FieldItem]] = {
 for {
 existingFields <- listFields(customerId)
 fieldRequests = FieldPayload.create(existingFields, fieldsToUse)
 newFields <- createNewFields(fieldRequests)
 } yield newFields
 } Let’s use cats!
  • 67. Changes in client code [error] value map is not a member of type parameter F[List[FieldItem]] private def createNewFields[F[_]: RestIO: Monad](
 customerId: Int,
 fieldRequests: List[CreateFieldRequest]
 ): F[List[FieldItem]] = {
 fieldRequests.map(request => {
 createField(request)
 .map(createFieldItem(request))
 }).sequence
 } Got rid of Future.sequence
  • 68. Testing new instance of RestIO for testing simple stub Id monad
  • 69. Let’s create an instance for testing! trait FakeRestIO {
 
 val expectedFieldListResponse : Map[Int, List[FieldItem]] = Map() … 
 implicit val fakeClientInstance = new RestIO[Id] {
 
 override def createField(payload: CreateFieldRequest) : Id[CreateFieldResponse] = { expectedCreateFieldResponse(payload)
 } …
 }
 
 }
  • 70. A sample test case "create segment with new custom field" in new FakeRestIO {
 override val expectedFieldListResponse = Map(
 customerId -> List(
 FieldItem(101, Some("Custom Email"))
 )
 )
 val fieldsToUse = List(
 ContactField("Custom Field"),
 ContactField("Custom Email")
 )
 override val expectedCreateFieldResponse = Map(
 CreateFieldRequest("Custom Field") -> CreateFieldResponse(1001) )
 val expectedNewField = List( FieldItem(1001, Some("Custom Field”)) ) create(customerId, fieldsToUse) shouldBe expectedNewField
 }
  • 71. Conclusions More generic Business logic has no side effects Testing without Future Dependency management by the compiler
  • 72.
  • 73. Learn more about type classes https://github.com/hablapps/ typeclassesforthemasses Hablapps
  • 74. Learn more about shapeless https://github.com/underscoreio/ shapeless-guide