how and why we evolved a legacy java web application to scala... and we are still alive!

114
How and why we evolved a legacy java application to scala And we are still alive ! 24/06/2015

Upload: katia-aresti

Post on 03-Aug-2015

177 views

Category:

Technology


2 download

TRANSCRIPT

Page 1: How and why we evolved a legacy Java web application to Scala... and we are still alive!

How and why we evolved a legacy java application to scala

And we are still alive !24/06/2015

Page 2: How and why we evolved a legacy Java web application to Scala... and we are still alive!

I am

@karesti

Page 3: How and why we evolved a legacy Java web application to Scala... and we are still alive!

The Software Dream

Page 4: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Software is more like Madonna

Page 5: How and why we evolved a legacy Java web application to Scala... and we are still alive!

FACTS

Page 6: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Web applications get old (very) fast

Page 7: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Continuous small refactoring does not avoid long-term technical debt

Page 8: How and why we evolved a legacy Java web application to Scala... and we are still alive!

From strach Refactoring

Continuous Dilemma

Page 9: How and why we evolved a legacy Java web application to Scala... and we are still alive!

2014

Page 10: How and why we evolved a legacy Java web application to Scala... and we are still alive!

French Job Search Website Launched in 2000

Page 11: How and why we evolved a legacy Java web application to Scala... and we are still alive!

2008

Page 12: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Problems in 2014

Page 13: How and why we evolved a legacy Java web application to Scala... and we are still alive!

High Cost Adding New FunctionalitiesHigh Technical Debt

Page 14: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Coupled Architecture

S O A Spaghettis Oriented Architecture

Page 15: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Lack of real KPI

Page 16: How and why we evolved a legacy Java web application to Scala... and we are still alive!

In a 100% Linux Environment

Page 17: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Monolithic Architecture

Page 18: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DAO

ServiceBatch

MVC

RDMS

Page 19: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DAO

ServiceBatch

MVC

RDMS

Anonymous UserJob Search, Detail, Newsletter

Page 20: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DAO

ServiceBatch

MVC

RDMS

Anonymous UserJob Search, Detail, Newsletter

Jobs

Page 21: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DAO

ServiceBatch

MVC

RDMS

Anonymous UserJob Search, Detail, Newsletter

Jobs

Jobs

External App

Page 22: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DAO

ServiceBatch

MVC

RDMS

Anonymous UserJob Search, Detail, Newsletter

Jobs

Jobs

External App

Connected User BoardCV, Mail Alert, Newsletter

Page 23: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DAO

ServiceBatch

MVC

RDMS

Anonymous UserJob Search, Detail, Newsletter

Jobs

Jobs

External App

Data

Mailing App

Connected User BoardCV, Mail Alert, Newsletter

Page 24: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DAO

ServiceBatch

MVC

RDMS

Anonymous UserJob Search, Detail, Newsletter

Jobs

Jobs

External App

Data

Mailing App

External App

Connected User BoardCV, Mail Alert, Newsletter

Page 25: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DAO

ServiceBatch

MVC

RDMS

Anonymous UserJob Search, Detail, Newsletter

Jobs

Jobs

External App

Data

Mailing App

External App

Mobile Version

Connected User BoardCV, Mail Alert, Newsletter

Page 26: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DAO

ServiceBatch

MVC

RDMS

Anonymous UserJob Search, Detail, Newsletter

Jobs

Jobs

External App

Data

Mailing App

External App

Mobile Version

Connected User BoardCV, Mail Alert, Newsletter

Page 27: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DAO

ServiceBatch

MVC

RDMSExternal

App

Connected User BoardCV, Mail Alert, Newsletter

Anonymous UserJob Search, Detail, Newsletter

Non connectedAlerts, Newsletter

Jobs

Jobs

External App

Data

Mailing App

Mobile Version

Partners

Page 28: How and why we evolved a legacy Java web application to Scala... and we are still alive!

How do we fix this

Page 29: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Target

Page 30: How and why we evolved a legacy Java web application to Scala... and we are still alive!
Page 31: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Where do we start

Page 32: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DAO

ServiceBatch

MVC

RDMSExternal

App

Jobs

Jobs

External App

Data

Mailing App

Mobile Version

Partners

Connected User BoardCV, Mail Alert, Newsletter

Anonymous UserJob Search, Detail, Newsletter

Non connectedAlerts, Newsletter

Page 33: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Mars – September 2014

User Board

Page 34: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DAO

ServiceBatch

MVC

RDMS

Data

Mailing App

User BoardUser Board Front-End

REST API

Mongo

Page 35: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DAO

ServiceBatch

MVC

RDMS

Data

Mailing App

Where do we start ?User Board Front-End

REST API

Mongo

Partners

Page 36: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DAO

ServiceBatch

MVC

RDMS

Data

Mailing App

Where do we start ?User Board Front-End

REST API

Mongo

Partners

Page 37: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DAO

ServiceBatch

MVC

RDMS

Data

Mailing App

Where do we start ?User Board Front-End

REST API

Mongo

Partners

Page 38: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DAO

ServiceBatch

MVC

RDMS

Data

Mailing App

Where do we start ?User Board Front-End

REST API

Mongo

Partners

External App

Page 39: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DAO

ServiceBatch

MVC

RDMS

Data

Mailing App

Where do we start ?User Board Front-End

REST API

Mongo

Partners

External App

Page 40: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DAO

ServiceBatch

MVC

RDMS

Data

Mailing App

Where do we start ?User Board Front-End

REST API

Mongo

Partners

External App

Data

Batch

Page 41: How and why we evolved a legacy Java web application to Scala... and we are still alive!

API First

Page 42: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Macro

Page 43: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Operation

Page 44: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Focus on technical choices

Page 45: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Main Language

• Growing and Solid Community

• Powerful Frameworks and Utilities

• JVM

Page 46: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Backend

• REST oriented• Template Type Safe• Hot Reloading• “Simplifies” Scala• Reactive programming

Page 47: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Batch System

• Actors

• Scale Up – concurrency

• Scale Out – remoting

• Fault Tolerance

Page 48: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Front End

• Sass built in with play

• AngularJS, popular, community, experience

• Foundation, solid and easy CSS framework

Page 49: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Database

• Flexible schema• Document oriented makes sense with CVs• Low transactions• Application code rules the database schema– Already the case with SQL Server

• No DBA*– We rule the database as dev

Page 50: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Migrating Data Challenge

Page 51: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Status

• +10 years of candidate data

• Crucial for business

• Cannot fail really, C A N O T F A I L !!

Page 52: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Strategy

• Start migration as soon as possible– Started in April – Mai

• Migrate data incrementally

• Verify as much as possible

• Legacy ID

Page 53: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Akka Actors

• One actor per data

• Concurrence execution when possible

• Handle updates for the crucial moment => between SQL Server Stop and MongoDB Up

Page 54: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Madrid MUG

This section is specially dedicated to the Madrid MUG Members

Page 55: How and why we evolved a legacy Java web application to Scala... and we are still alive!

SQL Model

• 8 tables for the user profile• Complex joins• SQLServer => Lost in a Linux World• Devs => Backup, dump …

Page 56: How and why we evolved a legacy Java web application to Scala... and we are still alive!

(Very) Simplified Schema

Page 57: How and why we evolved a legacy Java web application to Scala... and we are still alive!

MongoDB Model

• 1 document / profile• Object <> Document• Simplified model– Option Scala

• Using Reactive Mongo Driver• Using Jongo in Java

Page 58: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Mongo Collections

Page 59: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Model choices

• ++ reads / -- writes• Stable Reference data *– Sometimes sectors can change…

• Low and non risky transactions– Ex : user deletes account

Page 60: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Testing

Page 61: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Unit Testing

• Using Specs framework

• Using Mockito – Not as useful as in Java

• Some tests are not necessary – Constructors, Builders … Scala Type Safe and

Immutability

Page 62: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Testing the Front END

• Unit testing JS with Karma

• Selenium – Very fragile tests– Proxy Nightmare– Endless navigator problems– Just vital tests after production

Page 63: How and why we evolved a legacy Java web application to Scala... and we are still alive!

API Tests are CRUCIAL

Page 64: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Testing the REST API

• No Mocking MongoDB

• Using Embedded Mongo

• Start and Stop Mongo once for every test– DRY data is a hard part

https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo

Page 65: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Always thinking on KPI

Page 66: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Build Measure Learn

Page 67: How and why we evolved a legacy Java web application to Scala... and we are still alive!

SCALA USER GROUP

This section is specially dedicated to the Madrid Scala User Group.Thank you to Nouhoum Traoré who spoke about it at scala.io in Paris

Page 68: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Front-end

● Client API

● No DB Access

● Mostly Javascript Code

Page 69: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Front-end

● 9 % Scala● 26.2 % CSS

●64.8 % JS

Page 70: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Le frontend : asset pipeline

pipelineStages := Seq(rjs, digest, gzip)

Page 71: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Le frontend : asset pipeline

curl http://keljob.com/assets/js/e454f1013e30b783818c8efaf3a8e3a5-startup.js

HTTP/1.1 200 OKCache-Control: public, max-age=31536000Content-Length: 171997Content-Type: application/javascript; charset=utf-8Date: Tue, 14 Oct 2014 22:39:23 GMTETag: e454f1013e30b783818c8efaf3a8e3a5Last-Modified: Wed, 08 Oct 2014 12:35:10 GMT

Page 72: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Compressing content

import play.api.mvc._import play.filters.gzip.GzipFilter

object Global extends WithFilters(new GzipFilter()) {

...}

Page 73: How and why we evolved a legacy Java web application to Scala... and we are still alive!

API : links on JSON

implicit val AccountWrites = new Writes[Account] {

override def writes(account: Account): JsValue =

Json.obj(

"id" -> account.id,

"email" -> account.email,

"creationDate" -> account.creationDate.toString(),

"links" -> Json.obj(

"self" -> routes.AccountDetailCtrl.get(account.id).url,

"alerts" ->

routes.JobAlertDetailCtrl.getAlertsOf(account.id).url,

"cv" -> routes.CvDetailController.getCvOf(account.id).url

)

)

}

Page 74: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Single responsibility Object : Controllers@Singleton

class AccountValidationController @Inject() (

accountValidator: AccountValidator) extends Controller

{

def validateAccount(token: String) = Action.async

{ request =>

accountValidator.validate(token).map {

case Some(account) => Ok(Json.toJson(account))

case _ =>

UnprocessableEntity(Json.toJson(InvalidToken))

}

}

Page 75: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Single responsibility Object : Servicesclass AccountAuthenticator @Inject() (...)

class AccountValidator @Inject() (...)

class AccountCreator @Inject() (...)

class AccountSettingsUpdater @Inject() (...)

Page 76: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Single Responsibility Object : Actors

import akka.actor._

...

class CvExporter(...) extends Actor { def commonBehavior(): Receive = ??? def deleteBehavior(): Receive = ??? def updateBehavior(): Receive = ???

def receive = commonBehavior orElse updateBehavior orElse deleteBehavior

}

Page 77: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Error Handling in Services

sealed trait NewsletterError

case object InvalidNewsletterActivationToken extends

NewsletterError

case object NewsletterUpdateError extends NewsletterError

class NewsletterActivator @Inject() (...) {

def activate(code: String): Future[Either[NewsletterError, Boolean]]

= ???

}

Page 78: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Error handling in controllers

class NewsletterActivationController (

newsletterActivor: NewsletterActivator) extends Controller {

def activate(token: String) = Action.async { request =>

newsletterActivor.activate(token).map {

case InvalidNewsletterActivationToken => ???

case NewsletterUpdateError => ???

}.recoverApiError("Oops !!!")

}

}

Page 79: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Error handling in controllers

implicit class ApiErrorRecover(result: Future[Result]) {

def recoverApiError(message: String) =

result recover {

case NonFatal(e) => InternalServerError(

Json.toJson(SimpleError(message))

)

}

}

Page 80: How and why we evolved a legacy Java web application to Scala... and we are still alive!

The Team

Page 81: How and why we evolved a legacy Java web application to Scala... and we are still alive!

The (original) Team

• 3 Developers and a Product Owner– Legacy + Backend + Scala– Full-Stack– Java + Backend + MongoDB

• From 5-10 years of experience• People who are able to leave their confortable

coding zone• Want to communicate

Page 82: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Project Management Method

Page 83: How and why we evolved a legacy Java web application to Scala... and we are still alive!

SCRUM

Page 84: How and why we evolved a legacy Java web application to Scala... and we are still alive!

KANBAN

Page 85: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Programing MF

Page 86: How and why we evolved a legacy Java web application to Scala... and we are still alive!

(SOME) DIFFICULTIES

Page 87: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DEFINING THE INITIAL SCOPE

Page 88: How and why we evolved a legacy Java web application to Scala... and we are still alive!

MVP

Page 89: How and why we evolved a legacy Java web application to Scala... and we are still alive!

MVP

Page 90: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Maximal Viable Product

Page 91: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DEALING WITH NO TECHNICAL PEOPLE

Page 92: How and why we evolved a legacy Java web application to Scala... and we are still alive!
Page 93: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Demo

Page 94: How and why we evolved a legacy Java web application to Scala... and we are still alive!

We have a situation …

Page 95: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Y

FIF Pattern

Fancy Interface First

Page 96: How and why we evolved a legacy Java web application to Scala... and we are still alive!

SCALA

Warning ! This section might contain some trolls

Page 97: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Personal journey to Scala

Page 98: How and why we evolved a legacy Java web application to Scala... and we are still alive!

SIMPLE ??? Build Tool

"de.flapdoodle.embed" % "de.flapdoodle.embed.mongo" % "1.46.0” "org.mongodb" %% "casbah" % "2.5.0"

Page 99: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Reactive Futures …

Page 100: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Implicits

accountFuture.filter(_.isDefined).map(_.get)

Page 101: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Cake Pattern

Page 102: How and why we evolved a legacy Java web application to Scala... and we are still alive!

DI Framework vs Cake Pattern

@Singletonclass LoginController @Inject() (accountAuthenticator: AccountAuthenticator) extends Controller

trait LoginController { this: Controller with UserServiceComponent =>

object LoginController extends LoginController with Controller with MongoDbUserServiceComponent

Page 103: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Loving Case Class And Constructors

case class Account( id: Option[BSONObjectID] = None,

email: String, creationDate: DateTime,

optin: Boolean = false, optoutScenario: Option[DateTime] = None, source: String = Account.DEFAULT_SOURCE,

deletionDate: Option[DateTime] = None)

Account(“[email protected]”, creationDate = creationDate,

optin = true)

Page 104: How and why we evolved a legacy Java web application to Scala... and we are still alive!

After 2-3 months as happy as being at Machu Picchu

Page 105: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Loving both …

Page 106: How and why we evolved a legacy Java web application to Scala... and we are still alive!

September – December 2014

Page 107: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Once in production

– Creating accounts more easily– Parsing CV on upload– Can apply with the CV – Follow applications

• And other awesome stuff built fast and furiously !

Page 108: How and why we evolved a legacy Java web application to Scala... and we are still alive!

January – March 2015

Search and Relooking

Page 109: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Relooking and positioning

Page 110: How and why we evolved a legacy Java web application to Scala... and we are still alive!

BatchDataMailing

App

2015Web Front-

End

User REST API

Mongo

Partners in WIP

External App Data

Search REST API

Elastic Search

Batch

Page 111: How and why we evolved a legacy Java web application to Scala... and we are still alive!

SEOMost Important Challenge

Page 112: How and why we evolved a legacy Java web application to Scala... and we are still alive!

Challenge 2 : Performance

• Gatling – Play!

• Comparing performance

Page 113: How and why we evolved a legacy Java web application to Scala... and we are still alive!
Page 114: How and why we evolved a legacy Java web application to Scala... and we are still alive!