Actually, I’m a
Spy Turtle.
Actually, I’m a
Spy Turtle.
QW
I recommend Uncle
Bob’s “The Little
Mocker” post
WS
Validate client
Authenticate user
Issue access token
clientId, clientSecret
email, password
access token
Client
User
class SignInService(clientService: ClientService,
authenticatorService: AuthentictorService,
accessTokenService: TokenService
) {
def signIn(clientId: String, clientSecret: String, email: String,
password: String): Future[AccessToken] = {
for {
client <- clientService.validateClient(clientId, clientSecret)
user <- authenticatorService.authenticateUser(email, password)
accessToken <- accessTokenService.createAccessToken(user.id,
client.id)
} yield (accessToken)
}
class SignInService(clientService: ClientService,
authenticatorService: AuthentictorService,
accessTokenService: TokenService
) {
def signIn(clientId: String, clientSecret: String, email: String,
password: String): Future[AccessToken] = {
for {
client <- clientService.validateClient(clientId, clientSecret)
user <- authenticatorService.authenticateUser(email, password)
accessToken <- accessTokenService.createAccessToken(user.id,
client.id)
} yield (accessToken)
}
class SignInService(clientService: ClientService,
authenticatorService: AuthentictorService,
accessTokenService: TokenService
) {
def signIn(clientId: String, clientSecret: String, email: String,
password: String): Future[AccessToken] = {
for {
client <- clientService.validateClient(clientId, clientSecret)
user <- authenticatorService.authenticateUser(email, password)
accessToken <- accessTokenService.createAccessToken(user.id,
client.id)
} yield (accessToken)
}
class SignInService(clientService: ClientService,
authenticatorService: AuthentictorService,
accessTokenService: TokenService
) {
def signIn(clientId: String, clientSecret: String, email: String,
password: String): Future[AccessToken] = {
for {
client <- clientService.validateClient(clientId, clientSecret)
user <- authenticatorService.authenticateUser(email, password)
accessToken <- accessTokenService.createAccessToken(user.id,
client.id)
} yield (accessToken)
}
T
class SignInService(clientService: ClientService,
authenticatorService: AuthentictorService,
accessTokenService: TokenService
) {
def signIn(clientId: String, clientSecret: String, email: String,
password: String): Future[AccessToken] = {
for {
client <- clientService.validateClient(clientId, clientSecret)
user <- authenticatorService.authenticateUser(email, password)
accessToken <- accessTokenService.createAccessToken(user.id,
client.id)
} yield (accessToken)
}
class ClientService(jsonClient: JsonClient) {
def validateClient(clientId: String, clientSecret: String):
Future[Client]={
jsonClient.getJson(
Path() / "clients" / clientId
).map {
case JsonResponse(OkStatus, json, _, _) => {
val id = (json  "id").as[Long]
val secret = (json  "secret").as[String]
if (secret != clientSecret) {
throw new Exception("Client doesn't match")
}
Client(id)
class ClientService(jsonClient: JsonClient) {
def validateClient(clientId: String, clientSecret: String):
Future[Client]={
jsonClient.getJson(
Path() / "clients" / clientId
).map {
case JsonResponse(OkStatus, json, _, _) => {
val id = (json  "id").as[Long]
val secret = (json  "secret").as[String]
if (secret != clientSecret) {
throw new Exception("Client doesn't match")
}
Client(id)
class ClientService(jsonClient: JsonClient) {
def validateClient(clientId: String, clientSecret: String):
Future[Client]={
jsonClient.getJson(
Path() / "clients" / clientId
).map {
case JsonResponse(OkStatus, json, _, _) => {
val id = (json  "id").as[Long]
val secret = (json  "secret").as[String]
if (secret != clientSecret) {
throw new Exception("Client doesn't match")
}
Client(id)
class ClientService(jsonClient: JsonClient) {
def validateClient(clientId: String, clientSecret: String):
Future[Client]={
jsonClient.getJson(
Path() / "clients" / clientId
).map {
case JsonResponse(OkStatus, json, _, _) => {
val id = (json  "id").as[Long]
val secret = (json  "secret").as[String]
if (secret != clientSecret) {
throw new Exception("Client doesn't match")
}
Client(id)
class ClientService(jsonClient: JsonClient) {
def validateClient(clientId: String, clientSecret: String):
Future[Client]={
jsonClient.getJson(
Path() / "clients" / clientId
).map {
case JsonResponse(OkStatus, json, _, _) => {
val id = (json  "id").as[Long]
val secret = (json  "secret").as[String]
if (secret != clientSecret) {
throw new Exception("Client doesn't match")
}
Client(id)
EL
class ClientService(jsonClient: JsonClient) {
def validateClient(clientId: String, clientSecret: String):
Future[Client]={
jsonClient.getJson(
Path() / "clients" / clientId
).map {
case JsonResponse(OkStatus, json, _, _) => {
val id = (json  "id").as[Long]
val secret = (json  "secret").as[String]
if (secret != clientSecret) {
throw new Exception("Client doesn't match")
}
Client(id)
"returns client if credentials match" in new Scope {
val jsonClient = mock[JsonClient]
val clientService = new ClientService(jsonClient)
val jsonResponse = JsonResponse(OkStatus, Json.fromString(...))
when(jsonClient.get(Path() / "clients" / "clientId")
.thenReturn(Future.value(jsonResponse))
clientService.validateClient("clientId", "secret")
==== Client(5)
}
"returns client if credentials match" in new Scope {
val jsonClient = mock[JsonClient]
val clientService = new ClientService(jsonClient)
val jsonResponse = JsonResponse(OkStatus,Json.fromString(...))
when(jsonClient.get(Path() / "clients" / "clientId")
.thenReturn(Future.value(jsonResponse))
clientService.validateClient("clientId", "secret")
==== Client(5)
}
"returns client if credentials match" in new Scope {
val jsonClient = mock[JsonClient]
val clientService = new ClientService(jsonClient)
val jsonResponse = JsonResponse(OkStatus, Json.fromString(...)
when(jsonClient.get(Path() / "clients" / "clientId")
.thenReturn(Future.value(jsonResponse))
clientService.validateClient("clientId", "secret")
==== Client(5)
}
"returns client if credentials match" in new Scope {
val jsonClient = mock[JsonClient]
val clientService = new ClientService(jsonClient)
val jsonResponse = JsonResponse(OkStatus,Json.fromString(...))
when(jsonClient.get(Path() / "clients" / "clientId")
.thenReturn(Future.value(jsonResponse))
clientService.validateClient("clientId", "secret")
==== Client(5)
}
"returns client if credentials match" in new Scope {
val jsonClient = mock[JsonClient]
val clientService = new ClientService(jsonClient)
val jsonResponse = JsonResponse(OkStatus,Json.fromString(...))
when(jsonClient.get(Path() / "clients" / "clientId")
.thenReturn(Future.value(jsonResponse))
clientService.validateClient("clientId", "secret")
==== Client(5)
}
"returns client if credentials match" in new Scope {
val jsonClient = mock[JsonClient]
val clientService = new ClientService(jsonClient)
val jsonResponse = JsonResponse(OkStatus,Json.fromString(...))
when(jsonClient.get(Path() / "clients" / "clientId")
.thenReturn(Future.value(jsonResponse))
clientService.validateClient("clientId", "secret")
==== Client(5)
}
B
F
class ClientService(jsonClient: JsonClient) {
def validateClient(clientId: String, clientSecret: String):
Future[Client]={
jsonClient.getJson(
Path() / "clients" / clientId
).map {
case JsonResponse(OkStatus, json, _, _) => {
val id = (json  "id").as[Long]
val secret = (json  "secret").as[String]
if (secret != clientSecret) {
throw new Exception("Client doesn't match")
}
Client(id)
class ClientService(getJson: (Path) => Future[JsonResponse]) {
def validateClient(clientId: String, clientSecret: String):
Future[Client]={
getJson(
Path() / "clients" / clientId
).map {
case JsonResponse(OkStatus, json, _, _) => {
val id = (json  "id").as[Long]
val secret = (json  "secret").as[String]
if (secret != clientSecret) {
throw new Exception("Client doesn't match")
}
Client(id)
:I
val jsonClient = JsonClient(host)
val clientService = new ClientService(jsonClient.getJson)
val jsonClient = JsonClient(host)
val clientService = new ClientService(jsonClient.getJson)
"returns client if credentials match" in new Scope {
val jsonResponse = JsonResponse(OkStatus, Json.fromString(...)
val clientService = new ClientService(_ => Future.value(jsonResponse))
clientService.validateClient("clientId", "secret") ==== Client(5)
}
"returns client if credentials match" in new Scope {
val jsonResponse = JsonResponse(OkStatus, Json.fromString(...)
val clientService = new ClientService(_ => Future.value(jsonResponse))
clientService.validateClient("clientId", "secret") ==== Client(5)
}
"returns client if credentials match" in new Scope {
val jsonResponse = JsonResponse(OkStatus, Json.fromString(...)
val clientService = new ClientService(_ => Future.value(jsonResponse))
clientService.validateClient("clientId", "secret") ==== Client(5)
}
"returns client if credentials match" in new Scope {
val jsonResponse = JsonResponse(OkStatus, Json.fromString(...)
val clientService = new ClientService(_ => Future.value(jsonResponse))
clientService.validateClient("clientId", "secret") ==== Client(5)
}
"returns client if credentials match" in new Scope {
val jsonResponse = JsonResponse(OkStatus, Json.fromString(...)
val clientService = new ClientService(_ => Future.value(jsonResponse))
clientService.validateClient("clientId", "secret") ==== Client(5)
}
!E
when(jsonClient.getJson(Path()/"clients"/"clientId")
.thenReturn(Future.value(jsonResponse))
:CA
"returns client if credentials match" in new Scope {
val jsonResponse = JsonResponse(OkStatus, Json.fromString(...)
var arg: Option[Path] = None
val getJson = (path:Path) => {
arg = Some(path)
Future.value(jsonResponse)
}
val clientService = new ClientService(getJson)
clientService.validateClient("clientId", "secret")) ==== Client(5)
arg ==== Some(Path() / "clients" / "clientId")
}
"returns client if credentials match" in new Scope {
val jsonResponse = JsonResponse(OkStatus, Json.fromString(...)
var arg: Option[Path] = None
val getJson = (path:Path) => {
arg = Some(path)
Future.value(jsonResponse)
}
val clientService = new ClientService(getJson)
clientService.validateClient("clientId", "secret")) ==== Client(5)
arg ==== Some(Path() / "clients" / "clientId")
}
:S
:MF
–Martin Fowler
http://martinfowler.com/articles/mocksArentStubs.html#TheDifferenceBetweenMocksAndStubs
~Martin Fowler
http://martinfowler.com/articles/mocksArentStubs.html#TheDifferenceBetweenMocksAndStubs
~Martin Fowler
http://martinfowler.com/articles/mocksArentStubs.html#TheDifferenceBetweenMocksAndStubs
intermediate state
~Martin Fowler
http://martinfowler.com/articles/mocksArentStubs.html#TheDifferenceBetweenMocksAndStubs
Side effects
Yay! No more
mock soup
FSB
Validate client
Authenticate user
Issue access token
clientId, clientSecret
email, password
access token
Client
User
class SignInService(
clientService: ClientService,
authenticatorService: AuthentictorService,
accessTokenService: TokenService
) {
def signIn(clientId: String, clientSecret: String, email: String,
password: String): Future[AccessToken] = {
for {
client <- clientService.validateClient(clientId, clientSecret)
user <- authenticatorService.authenticateUser(email, password)
accessToken <- accessTokenService.createAccessToken(user.id,
client.id)
} yield (accessToken)
}
class SignInService(
clientService: ClientService,
authenticatorService: AuthentictorService,
accessTokenService: TokenService
) {
def signIn(clientId: String, clientSecret: String, email: String,
password: String): Future[AccessToken] = {
for {
client <- clientService.validateClient(clientId, clientSecret)
user <- authenticatorService.authenticateUser(email, password)
accessToken <- accessTokenService.createAccessToken(user.id,
client.id)
} yield (accessToken)
}
class SignInService(
validateClient: (String, String) => Future[Client],
authenticateUser: (String, String) => Future[User],
createAccessToken: (Long, Long) => Future[AccessToken]
) {
def signIn(clientId: String, clientSecret: String, email: String,
password: String): Future[AccessToken] = {
for {
client <- validateClient(clientId, clientSecret)
user <- authenticateUser(email, password)
accessToken <- createAccessToken(user.id, client.id)
} yield (accessToken)
}
:FO
object SignIn {
def signIn(validateClient: (String, String) => Future[Client],
authenticateUser: (String, String) => Future[User],
createAccessToken: (Long, Long) => Future[AccessToken])
(clientId: String, clientSecret: String, email: String,
password: String): Future[AccessToken] = {
for {
client <- validateClient(clientId, clientSecret)
user <- authenticateUser(email, password)
accessToken <- createAccessToken(user.id, client.id)
} yield (accessToken)
}
}
object SignIn {
def signIn(validateClient: (String, String) => Future[Client],
authenticateUser: (String, String) => Future[User],
createAccessToken: (Long, Long) => Future[AccessToken])
(clientId: String, clientSecret: String, email: String,
password: String): Future[AccessToken] = {
for {
client <- validateClient(clientId, clientSecret)
user <- authenticateUser(email, password)
accessToken <- createAccessToken(user.id, client.id)
} yield (accessToken)
}
}
:C
object SignIn {
def signIn(validateClient: (String, String) => Future[Client],
authenticateUser: (String, String) => Future[User],
createAccessToken: (Long, Long) => Future[AccessToken])
(clientId: String, clientSecret: String, email: String,
password: String): Future[AccessToken] = {
for {
client <- validateClient(clientId, clientSecret)
user <- authenticateUser(email, password)
accessToken <- createAccessToken(user.id, client.id)
} yield (accessToken)
}
}
class SignInService(
validateClient: (String, String) => Future[Client],
authenticateUser: (String, String) => Future[User],
createAccessToken: (Long, Long) => Future[AccessToken]
) {
def signIn(clientId: String, clientSecret: String, email: String,
password: String): Future[AccessToken] = {
for {
client <- validateClient(clientId, clientSecret)
user <- authenticateUser(email, password)
accessToken <- createAccessToken(user.id, client.id)
} yield (accessToken)
}
:N
class SignInService(
validateClient: (String, String) => Future[Client],
authenticateUser: (String, String) => Future[User],
createAccessToken: (Long, Long) => Future[AccessToken]
) {
def signIn(clientId: String, clientSecret: String, email: String,
password: String): Future[AccessToken] = {
for {
client <- validateClient(clientId, clientSecret)
user <- authenticateUser(email, password)
accessToken <- createAccessToken(user.id, client.id)
} yield (accessToken)
}
class SignInService(
validateClient: (String, String) => Future[Client],
authenticateUser: (String, String) => Future[User],
createAccessToken: (Long, Long) => Future[AccessToken]
) {
def signIn(clientId: String, clientSecret: String, email: String,
password: String): Future[AccessToken] = {
for {
client <- validateClient(clientId, clientSecret)
user <- authenticateUser(email, password)
accessToken <- createAccessToken(user.id, client.id)
} yield (accessToken)
}
class SignInService(
validateClient: (String, String) => Future[Client],
authenticateUser: (String, String) => Future[User],
createAccessToken: (Long, Long) => Future[AccessToken]
) {
def signIn(clientId: String, clientSecret: String, email: String,
password: String): Future[AccessToken] = {
for {
client <- validateClient(clientId, clientSecret)
user <- authenticateUser(email, password)
accessToken <- createAccessToken(user.id, client.id)
} yield (accessToken)
}
:IJA
class SignInService(
validateClient: (String, String) => Future[Client],
authenticateUser: (String, String) => Future[User],
createAccessToken: (Long, Long) => Future[AccessToken]
) {
def signIn(clientId: String, clientSecret: String, email: String,
password: String): Future[AccessToken] = {
for {
client <- validateClient(clientId, clientSecret)
user <- authenticateUser(email, password)
accessToken <- createAccessToken(user.id, client.id)
} yield (accessToken)
}
class SignInService(
validateClient: (ClientId, ClientSecret) => Future[Client],
authenticateUser: (String, String) => Future[User],
createAccessToken: (Long, Long) => Future[AccessToken]
) {
def signIn(clientId: String, clientSecret: String, email: String,
password: String): Future[AccessToken] = {
for {
client <- validateClient(clientId, clientSecret)
user <- authenticateUser(email, password)
accessToken <- createAccessToken(user.id, client.id)
} yield (accessToken)
}
:W
object SignInApi extends FinagleBasedServer with AppConfigComponent{
val clientsJsonClient = JsonClient(host)
val tokensClient = JsonClient(host)
val dbClient = MySqlClient(dbconfig)
val passwordsRepo = new PasswordRepository(dbClient.prepare, dbClient.execute)
val clientService = new ClientService(clientsJsonClient.get)
val authenticatorService = new AuthentictorService(passwordsRepo.getPassword)
val tokenService = new TokenService(tokensClient.get)
val signInService = new SignInService(
clientService.validateClient,
authenticatorService.authenticateUser,
tokenService.createAccessToken
)
val signInHandler = new SignInHandler(signInService.signIn)
override def createRoutes(httpServer: HttpServer): Unit = {
httpServer.register("/sign-in", signInHandler)
}
}
MSEV
trait JsonClients {
self: ConfigComponent =>
val clientsJsonClient = JsonClient(config.clientsHost)
val tokensClient = JsonClient(config.tokensHost)
}
trait Repositories {
self: ConfigComponent =>
val dbClient = MySqlClient(config.dbHost, config.dbName, ...)
val passwordsRepo = new PasswordRepository(dbClient.prepare,
dbClient.execute)
}
trait Repositories {
self: ConfigComponent =>
val dbClient = MySqlClient(config.dbHost, config.dbName, ...)
val passwordRepo = new PasswordRepository(dbClient.prepare,
dbClient.execute)
}
trait Services extends JsonClients with Repositories {
self: ConfigComponent =>
val clientService = new ClientService(clientsJsonClient.get)
val authenticatorService = new AuthentictorService(passwordsRepo.getPassword
val tokenService = new TokenService(tokensClient.get)
val signInService = new SignInService(
clientService.validateClient,
authenticatorService.authenticateUser,
tokenService.createAccessToken
)
}
trait Services extends JsonClients with Repositories {
self: ConfigComponent =>
val clientService = new ClientService(clientsJsonClient.get)
val authenticatorService = new AuthentictorService(passwordsRepo.getPassword
val tokenService = new TokenService(tokensClient.get)
val signInService = new SignInService(
clientService.validateClient,
authenticatorService.authenticateUser,
tokenService.createAccessToken
)
}
trait Services extends JsonClients with Repositories {
self: ConfigComponent =>
val clientService = new ClientService(clientsJsonClient.get)
val authenticatorService = new AuthentictorService(passwordsRepo.getPassword
val tokenService = new TokenService(tokensClient.get)
val signInService = new SignInService(
clientService.validateClient,
authenticatorService.authenticateUser,
tokenService.createAccessToken
)
}
trait Services extends JsonClients with Repositories {
self: ConfigComponent =>
val clientService = new ClientService(clientsJsonClient.get)
val authenticatorService = new AuthentictorService(passwordsRepo.getPassword
val tokenService = new TokenService(tokensClient.get)
val signInService = new SignInService(
clientService.validateClient,
authenticatorService.authenticateUser,
tokenService.createAccessToken
)
}
object ExampleSignInApi extends FinagleBasedServer
with ConfigComponent
with Services{
val signInHandler = new SignInHandler(signInService.signIn)
override def createRoutes(httpServer: HttpServer): Unit = {
httpServer.register("/sign-in", signInHandler)
}
}
S
object ExampleSignInApi extends FinagleBasedServer
with ConfigComponent
with Services{
val signInHandler = new SignInHandler(signInService.signIn)
override def createRoutes(httpServer: HttpServer): Unit = {
httpServer.register("/sign-in", signInHandler)
}
}
S
H
●
●
●
●
E
def getUser(id: Int): Reader[DB,User]
= Reader( (db:DB) => User("duana") )
def getTracks(user: User): Reader[DB,Track]
= Reader( (db:DB) => Track(42))
val myProgram: Reader[DB,String] =
for {
user <- getUser(123)
track <- getTracks(user)
} yield track.toString
myProgram.perform(db)
●
●
●
●
TE
Manual DI Magic DI
● Requires discipline
● Static check
● No framework or fancy
language features to learn
● Boring
● Enforces consistency
● Runtime check?
● Problems can be hare-y
● Slick
U
Manual DI Magic DI (Framework)
● Requires discipline
● Static check
● No framework or fancy
language features to learn
● Boring
● Enforces consistency
● Runtime check?
● Problems can be hare-y
● Slick
Stop spreading
the news!
:L
Manual DI Magic DI (Framework)
● Requires discipline
● Static check
● No framework or fancy
language features to learn
● Boring
● Enforces consistency
● Runtime check?
● Problems can be hare-y
● Slick
Manual DI Magic DI (Framework)
● Requires discipline
● Static check
● No framework or fancy
language features to learn
● Boring
● Enforces consistency
● Runtime check?
● Problems can be hare-y
● Slick
Manual DI Magic DI (Framework)
● Requires discipline
● Static check
● No framework or fancy
language features to learn
● Boring
● Enforces consistency
● Runtime check?
● Problems can be hare-y
● Slick
:M
def makeTalk(
askChrisBerkhout: (Talk) => Better[Talk],
askAaronLevinAboutFP: (None) => Some[Idea],
askBrianGuthrieAboutClojure: (Idea) => Better[Idea]
)(ideas: Talk): Better[Talk]
makeTalk(..) was invoked by com.thoughtworks.Birgitta
@ThoughtWorks, @SoundCloudDev
@chrisberkhout, @aaronmblevin, @bguthrie, @birgitta410,
@davcamer, @rentalcustard
@theophani, @ellenkoenig, @harrydeanhudson
Questions @starkcoffee

Dependency Injection in Functional Programming

  • 8.
  • 9.
    Actually, I’m a SpyTurtle. QW I recommend Uncle Bob’s “The Little Mocker” post
  • 14.
  • 15.
    Validate client Authenticate user Issueaccess token clientId, clientSecret email, password access token Client User
  • 16.
    class SignInService(clientService: ClientService, authenticatorService:AuthentictorService, accessTokenService: TokenService ) { def signIn(clientId: String, clientSecret: String, email: String, password: String): Future[AccessToken] = { for { client <- clientService.validateClient(clientId, clientSecret) user <- authenticatorService.authenticateUser(email, password) accessToken <- accessTokenService.createAccessToken(user.id, client.id) } yield (accessToken) }
  • 17.
    class SignInService(clientService: ClientService, authenticatorService:AuthentictorService, accessTokenService: TokenService ) { def signIn(clientId: String, clientSecret: String, email: String, password: String): Future[AccessToken] = { for { client <- clientService.validateClient(clientId, clientSecret) user <- authenticatorService.authenticateUser(email, password) accessToken <- accessTokenService.createAccessToken(user.id, client.id) } yield (accessToken) }
  • 18.
    class SignInService(clientService: ClientService, authenticatorService:AuthentictorService, accessTokenService: TokenService ) { def signIn(clientId: String, clientSecret: String, email: String, password: String): Future[AccessToken] = { for { client <- clientService.validateClient(clientId, clientSecret) user <- authenticatorService.authenticateUser(email, password) accessToken <- accessTokenService.createAccessToken(user.id, client.id) } yield (accessToken) }
  • 19.
    class SignInService(clientService: ClientService, authenticatorService:AuthentictorService, accessTokenService: TokenService ) { def signIn(clientId: String, clientSecret: String, email: String, password: String): Future[AccessToken] = { for { client <- clientService.validateClient(clientId, clientSecret) user <- authenticatorService.authenticateUser(email, password) accessToken <- accessTokenService.createAccessToken(user.id, client.id) } yield (accessToken) } T
  • 20.
    class SignInService(clientService: ClientService, authenticatorService:AuthentictorService, accessTokenService: TokenService ) { def signIn(clientId: String, clientSecret: String, email: String, password: String): Future[AccessToken] = { for { client <- clientService.validateClient(clientId, clientSecret) user <- authenticatorService.authenticateUser(email, password) accessToken <- accessTokenService.createAccessToken(user.id, client.id) } yield (accessToken) }
  • 21.
    class ClientService(jsonClient: JsonClient){ def validateClient(clientId: String, clientSecret: String): Future[Client]={ jsonClient.getJson( Path() / "clients" / clientId ).map { case JsonResponse(OkStatus, json, _, _) => { val id = (json "id").as[Long] val secret = (json "secret").as[String] if (secret != clientSecret) { throw new Exception("Client doesn't match") } Client(id)
  • 22.
    class ClientService(jsonClient: JsonClient){ def validateClient(clientId: String, clientSecret: String): Future[Client]={ jsonClient.getJson( Path() / "clients" / clientId ).map { case JsonResponse(OkStatus, json, _, _) => { val id = (json "id").as[Long] val secret = (json "secret").as[String] if (secret != clientSecret) { throw new Exception("Client doesn't match") } Client(id)
  • 23.
    class ClientService(jsonClient: JsonClient){ def validateClient(clientId: String, clientSecret: String): Future[Client]={ jsonClient.getJson( Path() / "clients" / clientId ).map { case JsonResponse(OkStatus, json, _, _) => { val id = (json "id").as[Long] val secret = (json "secret").as[String] if (secret != clientSecret) { throw new Exception("Client doesn't match") } Client(id)
  • 24.
    class ClientService(jsonClient: JsonClient){ def validateClient(clientId: String, clientSecret: String): Future[Client]={ jsonClient.getJson( Path() / "clients" / clientId ).map { case JsonResponse(OkStatus, json, _, _) => { val id = (json "id").as[Long] val secret = (json "secret").as[String] if (secret != clientSecret) { throw new Exception("Client doesn't match") } Client(id)
  • 25.
    class ClientService(jsonClient: JsonClient){ def validateClient(clientId: String, clientSecret: String): Future[Client]={ jsonClient.getJson( Path() / "clients" / clientId ).map { case JsonResponse(OkStatus, json, _, _) => { val id = (json "id").as[Long] val secret = (json "secret").as[String] if (secret != clientSecret) { throw new Exception("Client doesn't match") } Client(id) EL
  • 26.
    class ClientService(jsonClient: JsonClient){ def validateClient(clientId: String, clientSecret: String): Future[Client]={ jsonClient.getJson( Path() / "clients" / clientId ).map { case JsonResponse(OkStatus, json, _, _) => { val id = (json "id").as[Long] val secret = (json "secret").as[String] if (secret != clientSecret) { throw new Exception("Client doesn't match") } Client(id)
  • 27.
    "returns client ifcredentials match" in new Scope { val jsonClient = mock[JsonClient] val clientService = new ClientService(jsonClient) val jsonResponse = JsonResponse(OkStatus, Json.fromString(...)) when(jsonClient.get(Path() / "clients" / "clientId") .thenReturn(Future.value(jsonResponse)) clientService.validateClient("clientId", "secret") ==== Client(5) }
  • 28.
    "returns client ifcredentials match" in new Scope { val jsonClient = mock[JsonClient] val clientService = new ClientService(jsonClient) val jsonResponse = JsonResponse(OkStatus,Json.fromString(...)) when(jsonClient.get(Path() / "clients" / "clientId") .thenReturn(Future.value(jsonResponse)) clientService.validateClient("clientId", "secret") ==== Client(5) }
  • 29.
    "returns client ifcredentials match" in new Scope { val jsonClient = mock[JsonClient] val clientService = new ClientService(jsonClient) val jsonResponse = JsonResponse(OkStatus, Json.fromString(...) when(jsonClient.get(Path() / "clients" / "clientId") .thenReturn(Future.value(jsonResponse)) clientService.validateClient("clientId", "secret") ==== Client(5) }
  • 30.
    "returns client ifcredentials match" in new Scope { val jsonClient = mock[JsonClient] val clientService = new ClientService(jsonClient) val jsonResponse = JsonResponse(OkStatus,Json.fromString(...)) when(jsonClient.get(Path() / "clients" / "clientId") .thenReturn(Future.value(jsonResponse)) clientService.validateClient("clientId", "secret") ==== Client(5) }
  • 31.
    "returns client ifcredentials match" in new Scope { val jsonClient = mock[JsonClient] val clientService = new ClientService(jsonClient) val jsonResponse = JsonResponse(OkStatus,Json.fromString(...)) when(jsonClient.get(Path() / "clients" / "clientId") .thenReturn(Future.value(jsonResponse)) clientService.validateClient("clientId", "secret") ==== Client(5) }
  • 32.
    "returns client ifcredentials match" in new Scope { val jsonClient = mock[JsonClient] val clientService = new ClientService(jsonClient) val jsonResponse = JsonResponse(OkStatus,Json.fromString(...)) when(jsonClient.get(Path() / "clients" / "clientId") .thenReturn(Future.value(jsonResponse)) clientService.validateClient("clientId", "secret") ==== Client(5) } B
  • 33.
  • 34.
    class ClientService(jsonClient: JsonClient){ def validateClient(clientId: String, clientSecret: String): Future[Client]={ jsonClient.getJson( Path() / "clients" / clientId ).map { case JsonResponse(OkStatus, json, _, _) => { val id = (json "id").as[Long] val secret = (json "secret").as[String] if (secret != clientSecret) { throw new Exception("Client doesn't match") } Client(id)
  • 35.
    class ClientService(getJson: (Path)=> Future[JsonResponse]) { def validateClient(clientId: String, clientSecret: String): Future[Client]={ getJson( Path() / "clients" / clientId ).map { case JsonResponse(OkStatus, json, _, _) => { val id = (json "id").as[Long] val secret = (json "secret").as[String] if (secret != clientSecret) { throw new Exception("Client doesn't match") } Client(id) :I
  • 36.
    val jsonClient =JsonClient(host) val clientService = new ClientService(jsonClient.getJson)
  • 37.
    val jsonClient =JsonClient(host) val clientService = new ClientService(jsonClient.getJson)
  • 38.
    "returns client ifcredentials match" in new Scope { val jsonResponse = JsonResponse(OkStatus, Json.fromString(...) val clientService = new ClientService(_ => Future.value(jsonResponse)) clientService.validateClient("clientId", "secret") ==== Client(5) }
  • 39.
    "returns client ifcredentials match" in new Scope { val jsonResponse = JsonResponse(OkStatus, Json.fromString(...) val clientService = new ClientService(_ => Future.value(jsonResponse)) clientService.validateClient("clientId", "secret") ==== Client(5) }
  • 40.
    "returns client ifcredentials match" in new Scope { val jsonResponse = JsonResponse(OkStatus, Json.fromString(...) val clientService = new ClientService(_ => Future.value(jsonResponse)) clientService.validateClient("clientId", "secret") ==== Client(5) }
  • 41.
    "returns client ifcredentials match" in new Scope { val jsonResponse = JsonResponse(OkStatus, Json.fromString(...) val clientService = new ClientService(_ => Future.value(jsonResponse)) clientService.validateClient("clientId", "secret") ==== Client(5) }
  • 42.
    "returns client ifcredentials match" in new Scope { val jsonResponse = JsonResponse(OkStatus, Json.fromString(...) val clientService = new ClientService(_ => Future.value(jsonResponse)) clientService.validateClient("clientId", "secret") ==== Client(5) } !E
  • 43.
  • 44.
    "returns client ifcredentials match" in new Scope { val jsonResponse = JsonResponse(OkStatus, Json.fromString(...) var arg: Option[Path] = None val getJson = (path:Path) => { arg = Some(path) Future.value(jsonResponse) } val clientService = new ClientService(getJson) clientService.validateClient("clientId", "secret")) ==== Client(5) arg ==== Some(Path() / "clients" / "clientId") }
  • 45.
    "returns client ifcredentials match" in new Scope { val jsonResponse = JsonResponse(OkStatus, Json.fromString(...) var arg: Option[Path] = None val getJson = (path:Path) => { arg = Some(path) Future.value(jsonResponse) } val clientService = new ClientService(getJson) clientService.validateClient("clientId", "secret")) ==== Client(5) arg ==== Some(Path() / "clients" / "clientId") } :S
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
    Validate client Authenticate user Issueaccess token clientId, clientSecret email, password access token Client User
  • 53.
    class SignInService( clientService: ClientService, authenticatorService:AuthentictorService, accessTokenService: TokenService ) { def signIn(clientId: String, clientSecret: String, email: String, password: String): Future[AccessToken] = { for { client <- clientService.validateClient(clientId, clientSecret) user <- authenticatorService.authenticateUser(email, password) accessToken <- accessTokenService.createAccessToken(user.id, client.id) } yield (accessToken) }
  • 54.
    class SignInService( clientService: ClientService, authenticatorService:AuthentictorService, accessTokenService: TokenService ) { def signIn(clientId: String, clientSecret: String, email: String, password: String): Future[AccessToken] = { for { client <- clientService.validateClient(clientId, clientSecret) user <- authenticatorService.authenticateUser(email, password) accessToken <- accessTokenService.createAccessToken(user.id, client.id) } yield (accessToken) }
  • 55.
    class SignInService( validateClient: (String,String) => Future[Client], authenticateUser: (String, String) => Future[User], createAccessToken: (Long, Long) => Future[AccessToken] ) { def signIn(clientId: String, clientSecret: String, email: String, password: String): Future[AccessToken] = { for { client <- validateClient(clientId, clientSecret) user <- authenticateUser(email, password) accessToken <- createAccessToken(user.id, client.id) } yield (accessToken) } :FO
  • 56.
    object SignIn { defsignIn(validateClient: (String, String) => Future[Client], authenticateUser: (String, String) => Future[User], createAccessToken: (Long, Long) => Future[AccessToken]) (clientId: String, clientSecret: String, email: String, password: String): Future[AccessToken] = { for { client <- validateClient(clientId, clientSecret) user <- authenticateUser(email, password) accessToken <- createAccessToken(user.id, client.id) } yield (accessToken) } }
  • 57.
    object SignIn { defsignIn(validateClient: (String, String) => Future[Client], authenticateUser: (String, String) => Future[User], createAccessToken: (Long, Long) => Future[AccessToken]) (clientId: String, clientSecret: String, email: String, password: String): Future[AccessToken] = { for { client <- validateClient(clientId, clientSecret) user <- authenticateUser(email, password) accessToken <- createAccessToken(user.id, client.id) } yield (accessToken) } }
  • 58.
    :C object SignIn { defsignIn(validateClient: (String, String) => Future[Client], authenticateUser: (String, String) => Future[User], createAccessToken: (Long, Long) => Future[AccessToken]) (clientId: String, clientSecret: String, email: String, password: String): Future[AccessToken] = { for { client <- validateClient(clientId, clientSecret) user <- authenticateUser(email, password) accessToken <- createAccessToken(user.id, client.id) } yield (accessToken) } }
  • 59.
    class SignInService( validateClient: (String,String) => Future[Client], authenticateUser: (String, String) => Future[User], createAccessToken: (Long, Long) => Future[AccessToken] ) { def signIn(clientId: String, clientSecret: String, email: String, password: String): Future[AccessToken] = { for { client <- validateClient(clientId, clientSecret) user <- authenticateUser(email, password) accessToken <- createAccessToken(user.id, client.id) } yield (accessToken) } :N
  • 60.
    class SignInService( validateClient: (String,String) => Future[Client], authenticateUser: (String, String) => Future[User], createAccessToken: (Long, Long) => Future[AccessToken] ) { def signIn(clientId: String, clientSecret: String, email: String, password: String): Future[AccessToken] = { for { client <- validateClient(clientId, clientSecret) user <- authenticateUser(email, password) accessToken <- createAccessToken(user.id, client.id) } yield (accessToken) }
  • 61.
    class SignInService( validateClient: (String,String) => Future[Client], authenticateUser: (String, String) => Future[User], createAccessToken: (Long, Long) => Future[AccessToken] ) { def signIn(clientId: String, clientSecret: String, email: String, password: String): Future[AccessToken] = { for { client <- validateClient(clientId, clientSecret) user <- authenticateUser(email, password) accessToken <- createAccessToken(user.id, client.id) } yield (accessToken) }
  • 62.
    class SignInService( validateClient: (String,String) => Future[Client], authenticateUser: (String, String) => Future[User], createAccessToken: (Long, Long) => Future[AccessToken] ) { def signIn(clientId: String, clientSecret: String, email: String, password: String): Future[AccessToken] = { for { client <- validateClient(clientId, clientSecret) user <- authenticateUser(email, password) accessToken <- createAccessToken(user.id, client.id) } yield (accessToken) } :IJA
  • 63.
    class SignInService( validateClient: (String,String) => Future[Client], authenticateUser: (String, String) => Future[User], createAccessToken: (Long, Long) => Future[AccessToken] ) { def signIn(clientId: String, clientSecret: String, email: String, password: String): Future[AccessToken] = { for { client <- validateClient(clientId, clientSecret) user <- authenticateUser(email, password) accessToken <- createAccessToken(user.id, client.id) } yield (accessToken) }
  • 64.
    class SignInService( validateClient: (ClientId,ClientSecret) => Future[Client], authenticateUser: (String, String) => Future[User], createAccessToken: (Long, Long) => Future[AccessToken] ) { def signIn(clientId: String, clientSecret: String, email: String, password: String): Future[AccessToken] = { for { client <- validateClient(clientId, clientSecret) user <- authenticateUser(email, password) accessToken <- createAccessToken(user.id, client.id) } yield (accessToken) } :W
  • 65.
    object SignInApi extendsFinagleBasedServer with AppConfigComponent{ val clientsJsonClient = JsonClient(host) val tokensClient = JsonClient(host) val dbClient = MySqlClient(dbconfig) val passwordsRepo = new PasswordRepository(dbClient.prepare, dbClient.execute) val clientService = new ClientService(clientsJsonClient.get) val authenticatorService = new AuthentictorService(passwordsRepo.getPassword) val tokenService = new TokenService(tokensClient.get) val signInService = new SignInService( clientService.validateClient, authenticatorService.authenticateUser, tokenService.createAccessToken ) val signInHandler = new SignInHandler(signInService.signIn) override def createRoutes(httpServer: HttpServer): Unit = { httpServer.register("/sign-in", signInHandler) } } MSEV
  • 66.
    trait JsonClients { self:ConfigComponent => val clientsJsonClient = JsonClient(config.clientsHost) val tokensClient = JsonClient(config.tokensHost) }
  • 67.
    trait Repositories { self:ConfigComponent => val dbClient = MySqlClient(config.dbHost, config.dbName, ...) val passwordsRepo = new PasswordRepository(dbClient.prepare, dbClient.execute) }
  • 68.
    trait Repositories { self:ConfigComponent => val dbClient = MySqlClient(config.dbHost, config.dbName, ...) val passwordRepo = new PasswordRepository(dbClient.prepare, dbClient.execute) }
  • 69.
    trait Services extendsJsonClients with Repositories { self: ConfigComponent => val clientService = new ClientService(clientsJsonClient.get) val authenticatorService = new AuthentictorService(passwordsRepo.getPassword val tokenService = new TokenService(tokensClient.get) val signInService = new SignInService( clientService.validateClient, authenticatorService.authenticateUser, tokenService.createAccessToken ) }
  • 70.
    trait Services extendsJsonClients with Repositories { self: ConfigComponent => val clientService = new ClientService(clientsJsonClient.get) val authenticatorService = new AuthentictorService(passwordsRepo.getPassword val tokenService = new TokenService(tokensClient.get) val signInService = new SignInService( clientService.validateClient, authenticatorService.authenticateUser, tokenService.createAccessToken ) }
  • 71.
    trait Services extendsJsonClients with Repositories { self: ConfigComponent => val clientService = new ClientService(clientsJsonClient.get) val authenticatorService = new AuthentictorService(passwordsRepo.getPassword val tokenService = new TokenService(tokensClient.get) val signInService = new SignInService( clientService.validateClient, authenticatorService.authenticateUser, tokenService.createAccessToken ) }
  • 72.
    trait Services extendsJsonClients with Repositories { self: ConfigComponent => val clientService = new ClientService(clientsJsonClient.get) val authenticatorService = new AuthentictorService(passwordsRepo.getPassword val tokenService = new TokenService(tokensClient.get) val signInService = new SignInService( clientService.validateClient, authenticatorService.authenticateUser, tokenService.createAccessToken ) }
  • 73.
    object ExampleSignInApi extendsFinagleBasedServer with ConfigComponent with Services{ val signInHandler = new SignInHandler(signInService.signIn) override def createRoutes(httpServer: HttpServer): Unit = { httpServer.register("/sign-in", signInHandler) } } S
  • 74.
    object ExampleSignInApi extendsFinagleBasedServer with ConfigComponent with Services{ val signInHandler = new SignInHandler(signInService.signIn) override def createRoutes(httpServer: HttpServer): Unit = { httpServer.register("/sign-in", signInHandler) } } S
  • 75.
  • 76.
  • 77.
    def getUser(id: Int):Reader[DB,User] = Reader( (db:DB) => User("duana") ) def getTracks(user: User): Reader[DB,Track] = Reader( (db:DB) => Track(42)) val myProgram: Reader[DB,String] = for { user <- getUser(123) track <- getTracks(user) } yield track.toString myProgram.perform(db)
  • 78.
  • 79.
  • 80.
  • 81.
    Manual DI MagicDI ● Requires discipline ● Static check ● No framework or fancy language features to learn ● Boring ● Enforces consistency ● Runtime check? ● Problems can be hare-y ● Slick
  • 82.
    U Manual DI MagicDI (Framework) ● Requires discipline ● Static check ● No framework or fancy language features to learn ● Boring ● Enforces consistency ● Runtime check? ● Problems can be hare-y ● Slick Stop spreading the news!
  • 83.
    :L Manual DI MagicDI (Framework) ● Requires discipline ● Static check ● No framework or fancy language features to learn ● Boring ● Enforces consistency ● Runtime check? ● Problems can be hare-y ● Slick
  • 84.
    Manual DI MagicDI (Framework) ● Requires discipline ● Static check ● No framework or fancy language features to learn ● Boring ● Enforces consistency ● Runtime check? ● Problems can be hare-y ● Slick
  • 85.
    Manual DI MagicDI (Framework) ● Requires discipline ● Static check ● No framework or fancy language features to learn ● Boring ● Enforces consistency ● Runtime check? ● Problems can be hare-y ● Slick :M
  • 87.
    def makeTalk( askChrisBerkhout: (Talk)=> Better[Talk], askAaronLevinAboutFP: (None) => Some[Idea], askBrianGuthrieAboutClojure: (Idea) => Better[Idea] )(ideas: Talk): Better[Talk] makeTalk(..) was invoked by com.thoughtworks.Birgitta
  • 88.
    @ThoughtWorks, @SoundCloudDev @chrisberkhout, @aaronmblevin,@bguthrie, @birgitta410, @davcamer, @rentalcustard @theophani, @ellenkoenig, @harrydeanhudson Questions @starkcoffee