Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Scala in Goozy Alexey Zlobin, e-Legion [email_address] @CheatEx
Index <ul><ul><li>Goozy overview </li></ul></ul><ul><ul><li>Scala's place </li></ul></ul><ul><ul><li>Lift </li></ul></ul><...
What is Goozy? <ul><li>A social network built around the concept of sticky note </li></ul><ul><ul><li>A note could be left...
Top-level architecture
Scala's place <ul><li>API server </li></ul><ul><li>Main functions: </li></ul><ul><ul><li>Storage access </li></ul></ul><ul...
Why scala? <ul><ul><li>Fast </li></ul></ul><ul><ul><li>Сoncise </li></ul></ul><ul><ul><li>Expressive </li></ul></ul><ul><u...
The team <ul><ul><li>2 persons </li></ul></ul><ul><ul><li>Strong Java background </li></ul></ul><ul><ul><li>Fancy about te...
Lift <ul><li>Utilized features: </li></ul><ul><ul><li>REST </li></ul></ul><ul><ul><li>JSON serialisation </li></ul></ul><u...
Lift: issues <ul><li>Localisation performance </li></ul><ul><ul><li>Hand-made localisation on standard resource bundles ga...
Goozy API logical structure <ul><li>Application is composed from three layers. </li></ul><ul><li>Each layer consists from ...
Conceptual problems <ul><ul><li>Components of each level depend from each other </li></ul></ul><ul><ul><li>Components most...
The solution: cake pattern <ul><ul><li>Each logically closed piece of functionality is represented as component </li></ul>...
Cake pattern: consequences <ul><li>+ All top-level architecture is expressed in one less than 100 LOC file </li></ul><ul><...
scalaz.Validation <ul><li>One sweet morning I sent a link to the colleague... </li></ul><ul><li>On the next morning we had...
Initial solution: domain exceptions <ul><li>class GoozzyException( </li></ul><ul><li>     val message: String) extends </l...
Error handling: own error type <ul><li>abstract class Error( </li></ul><ul><li>     val httpCode: Int, </li></ul><ul><li> ...
Validations and exceptions <ul><li>Problem: exceptions are here </li></ul><ul><li>Solution: catch'em and convert! </li></ul>
Validations and exceptions <ul><ul><li>Define the &quot;converter&quot; </li></ul></ul><ul><ul><li>Write some utilities to...
One more error for whatever happen <ul><li>case class ExceptionalError( </li></ul><ul><li>     @transient exception: Throw...
The converter <ul><li>Function is a perfect abstraction! </li></ul><ul><li>val dispatchException: Function[ </li></ul><ul>...
Useful utils: generic <ul><li>type GzVal[+OUT] = Validation[Error, OUT] </li></ul><ul><li>def safeVal[T]: Catch[GzVal[T]] ...
Useful utils: specific <ul><li>Just a trivial code which parses strings, extracts values from mongo objects, etc. </li></u...
Validation: the good thing <ul><li>Some code was pretty: </li></ul><ul><li>for { </li></ul><ul><li>     data <- getData(re...
Conversion: the problem <ul><li>But some other code was... </li></ul>
Not so pretty <ul><li>def readFields(rec: DBObject, ...): GzVal[Fields] = { </li></ul><ul><li>   val deletedBy = </li></ul...
Improved solution <ul><ul><li>Don't play haskell </li></ul></ul><ul><ul><li>Play java </li></ul></ul><ul><ul><li>...when i...
Error handling: big picture
Validation: pros and cons <ul><li>+ Comprehensible error aggregation and reporting </li></ul><ul><li>+ The only imaginable...
Lessons <ul><ul><li>Always prefer simple tools </li></ul></ul><ul><ul><li>Options and Eithers (Validations): they really w...
References <ul><ul><li>http://www.assembla.com/spaces/liftweb/wiki/REST_Web_Services  - REST support in Lift  </li></ul></...
Upcoming SlideShare
Loading in …5
×

"Scala in Goozy", Alexey Zlobin

643 views

Published on

Talk in Scala in Goozy (), by Alexey @CheatEx Zlobin, at scalaby#8

Published in: Technology
  • Be the first to comment

  • Be the first to like this

"Scala in Goozy", Alexey Zlobin

  1. 1. Scala in Goozy Alexey Zlobin, e-Legion [email_address] @CheatEx
  2. 2. Index <ul><ul><li>Goozy overview </li></ul></ul><ul><ul><li>Scala's place </li></ul></ul><ul><ul><li>Lift </li></ul></ul><ul><ul><li>Cake pattern in scale </li></ul></ul><ul><ul><li>Scalaz and other fancy stuff </li></ul></ul><ul><ul><li>Summary </li></ul></ul>
  3. 3. What is Goozy? <ul><li>A social network built around the concept of sticky note </li></ul><ul><ul><li>A note could be left anywhere in the Web. </li></ul></ul><ul><ul><li>It is associated with particular page element. </li></ul></ul><ul><li>Central UI concept: user's related feed with all new notes and comments </li></ul>
  4. 4. Top-level architecture
  5. 5. Scala's place <ul><li>API server </li></ul><ul><li>Main functions: </li></ul><ul><ul><li>Storage access </li></ul></ul><ul><ul><li>Background tasks (feed writing) </li></ul></ul><ul><ul><li>Email sending </li></ul></ul><ul><ul><li>Text indexing </li></ul></ul>
  6. 6. Why scala? <ul><ul><li>Fast </li></ul></ul><ul><ul><li>Сoncise </li></ul></ul><ul><ul><li>Expressive </li></ul></ul><ul><ul><li>Advanced OO </li></ul></ul><ul><ul><li>Some functional stuff </li></ul></ul><ul><ul><ul><li>Simple concurrency </li></ul></ul></ul><ul><ul><li>All Java legacy available </li></ul></ul>
  7. 7. The team <ul><ul><li>2 persons </li></ul></ul><ul><ul><li>Strong Java background </li></ul></ul><ul><ul><li>Fancy about technologies </li></ul></ul><ul><ul><li>Love to try new things </li></ul></ul>
  8. 8. Lift <ul><li>Utilized features: </li></ul><ul><ul><li>REST </li></ul></ul><ul><ul><li>JSON serialisation </li></ul></ul><ul><li>That's it... </li></ul>
  9. 9. Lift: issues <ul><li>Localisation performance </li></ul><ul><ul><li>Hand-made localisation on standard resource bundles gave 4 times throughput improvement. </li></ul></ul><ul><li>Very memory-consuming JSON serialisation. </li></ul><ul><ul><li>Not so efficient PrettyPrinter is used </li></ul></ul><ul><ul><li>Functional-styled string escaping </li></ul></ul><ul><li>Poor code style </li></ul><ul><ul><li>Extremely long map-match-if hierarchies </li></ul></ul><ul><ul><li>Mutable, difficult to debug LiftRules design </li></ul></ul>
  10. 10. Goozy API logical structure <ul><li>Application is composed from three layers. </li></ul><ul><li>Each layer consists from several similar components. Like UserController, UserService, etc. </li></ul>
  11. 11. Conceptual problems <ul><ul><li>Components of each level depend from each other </li></ul></ul><ul><ul><li>Components most likely have several dependencies from the previous level </li></ul></ul><ul><ul><li>  A lot of common stuff inside a level </li></ul></ul><ul><ul><ul><li>Every storage needs a DB connection </li></ul></ul></ul><ul><ul><ul><li>Every service needs an entire storage system and access to text indexes </li></ul></ul></ul><ul><ul><ul><li>Etc... </li></ul></ul></ul>
  12. 12. The solution: cake pattern <ul><ul><li>Each logically closed piece of functionality is represented as component </li></ul></ul><ul><ul><li>Dependencies are expressed as self-types </li></ul></ul><ul><ul><li>Common features expressed as mix-ins </li></ul></ul><ul><ul><li>Common combinations of functionality are expressed as mix-ins of several components </li></ul></ul>
  13. 13. Cake pattern: consequences <ul><li>+ All top-level architecture is expressed in one less than 100 LOC file </li></ul><ul><li>+ Compile-time dependency checks </li></ul><ul><li>+ The biggest file is around 1000 LOC </li></ul><ul><li>- Long dependency lists </li></ul><ul><ul><li>Poor design? </li></ul></ul><ul><li>- Implicit dependency on mix-in order (linearisation strikes back) </li></ul><ul><ul><li>Prefer def and lazy </li></ul></ul><ul><li>- A bit unclear how to deal with several dependencies of the same type but different runtime implementation </li></ul>
  14. 14. scalaz.Validation <ul><li>One sweet morning I sent a link to the colleague... </li></ul><ul><li>On the next morning we had a new dependency and totally refactored request parameters analysis. </li></ul><ul><li>Now everything is validated. </li></ul>
  15. 15. Initial solution: domain exceptions <ul><li>class GoozzyException( </li></ul><ul><li>     val message: String) extends </li></ul><ul><li>Exception(message) </li></ul><ul><li>case class UserNotFound( </li></ul><ul><li>     userId: String) extends </li></ul><ul><li>GoozzyException( </li></ul><ul><li>     &quot;user &quot; + userId + &quot; not found&quot;) </li></ul>
  16. 16. Error handling: own error type <ul><li>abstract class Error( </li></ul><ul><li>     val httpCode: Int, </li></ul><ul><li>     val code: String) </li></ul><ul><li>abstract class PermissionError( </li></ul><ul><li>     code: String) extends </li></ul><ul><li>ValidationError(403, code) </li></ul><ul><li>case class Banned( </li></ul><ul><li>     userId: String, </li></ul><ul><li>    groupId: String) extends </li></ul><ul><li>PermissionError(&quot;BANNED&quot;) </li></ul>
  17. 17. Validations and exceptions <ul><li>Problem: exceptions are here </li></ul><ul><li>Solution: catch'em and convert! </li></ul>
  18. 18. Validations and exceptions <ul><ul><li>Define the &quot;converter&quot; </li></ul></ul><ul><ul><li>Write some utilities to make them more accessible </li></ul></ul>
  19. 19. One more error for whatever happen <ul><li>case class ExceptionalError( </li></ul><ul><li>     @transient exception: Throwable) extends </li></ul><ul><li>InternalError </li></ul>
  20. 20. The converter <ul><li>Function is a perfect abstraction! </li></ul><ul><li>val dispatchException: Function[ </li></ul><ul><li>     Throwable, </li></ul><ul><li>     Error] = { </li></ul><ul><li>    case UserNotFound(name) => </li></ul><ul><li>         MissedEntity(name, &quot;user&quot;) </li></ul><ul><li>    ... </li></ul><ul><li>    //don't remove it!!! </li></ul><ul><li>    case t => ExceptionalError(t) </li></ul><ul><li>  } </li></ul>
  21. 21. Useful utils: generic <ul><li>type GzVal[+OUT] = Validation[Error, OUT] </li></ul><ul><li>def safeVal[T]: Catch[GzVal[T]] = </li></ul><ul><li>     handling(classOf[Throwable]) by { e => </li></ul><ul><li>         dispatchException(e).fail </li></ul><ul><li>     } </li></ul><ul><li>def safe[T](block: => T): GzVal[T] = </li></ul><ul><li>     safeVal( block.success ) </li></ul><ul><li>def editData(up: Edit): </li></ul><ul><li>     GzVal[Data] = safeVal { </li></ul><ul><li>        //all the dangerous stuff here </li></ul><ul><li>     } </li></ul>
  22. 22. Useful utils: specific <ul><li>Just a trivial code which parses strings, extracts values from mongo objects, etc. </li></ul><ul><li>See my blog for details... </li></ul>
  23. 23. Validation: the good thing <ul><li>Some code was pretty: </li></ul><ul><li>for { </li></ul><ul><li>    data <- getData(recordId) </li></ul><ul><li>    userData <- getUserData(userId) </li></ul><ul><li>     permission <- </li></ul><ul><li>         checkPermission(data, userData) </li></ul><ul><li>    newData <- updateData(data) </li></ul><ul><li>} yield { </li></ul><ul><li>    //all dirty hacks there </li></ul><ul><li>} </li></ul>
  24. 24. Conversion: the problem <ul><li>But some other code was... </li></ul>
  25. 25. Not so pretty <ul><li>def readFields(rec: DBObject, ...): GzVal[Fields] = { </li></ul><ul><li>  val deletedBy = </li></ul><ul><li>    for (userId <- get[ObjectId](note, &quot;deleted_by&quot;); </li></ul><ul><li>            user <- getUserData(userId).toSuccess(MissedEntity(...))) </li></ul><ul><li>    yield user </li></ul><ul><li>  for { </li></ul><ul><li>     id <- get[String](rec, &quot;_id&quot;) </li></ul><ul><li>    content <- get[String](rec, &quot;content&quot;) </li></ul><ul><li>    updated <- asValidOption(get[DateTime](rec, &quot;upd&quot;)) </li></ul><ul><li>    //twelve more </li></ul><ul><li>    user <- getUserById(userId, currentUserId map </li></ul><ul><li>       (new ObjectId(_))) </li></ul><ul><li>        .toSuccess( </li></ul><ul><li>           MissedEntity(userId.toString, &quot;user&quot;): </li></ul><ul><li>             ValidationError) </li></ul><ul><li>  } yield DataRecord(/*you won't see it*/) </li></ul><ul><li>} </li></ul>
  26. 26. Improved solution <ul><ul><li>Don't play haskell </li></ul></ul><ul><ul><li>Play java </li></ul></ul><ul><ul><li>...when it is easier </li></ul></ul>
  27. 27. Error handling: big picture
  28. 28. Validation: pros and cons <ul><li>+ Comprehensible error aggregation and reporting </li></ul><ul><li>+ The only imaginable way to deal with 20+ request parameters with 2-3 constraints on each </li></ul><ul><li>+ Unified approach to request validation and runtime error handling </li></ul><ul><li>+ Type-level check for correct error handling </li></ul><ul><li>- Monads and Applicatives cause massive brain damage </li></ul><ul><li>- Complicated error reports from the compiler </li></ul><ul><li>- You can't just ignore possibility of runtime exception </li></ul>
  29. 29. Lessons <ul><ul><li>Always prefer simple tools </li></ul></ul><ul><ul><li>Options and Eithers (Validations): they really work </li></ul></ul><ul><ul><ul><li>It is possible to live with both exceptions and eithers </li></ul></ul></ul><ul><ul><ul><li>Performance consequences are not clear </li></ul></ul></ul><ul><ul><ul><li>Some times I had to use things far behind my understanding (sequence, traverse) </li></ul></ul></ul><ul><ul><li>Server-side testing is difficult </li></ul></ul><ul><ul><ul><li>Testing approach should be established before any code is written </li></ul></ul></ul>
  30. 30. References <ul><ul><li>http://www.assembla.com/spaces/liftweb/wiki/REST_Web_Services  - REST support in Lift  </li></ul></ul><ul><ul><li>http://jonasboner.com/2008/10/06/real-world-scala-dependency-injection-di.html  - complete cake pattern intro </li></ul></ul><ul><ul><li>https://gist.github.com/970717  - easy Validation example </li></ul></ul><ul><ul><li>  http://www.scala-lang.org/api/current/scala/util/control/Exception$.html  - built-in DSL for exception handling </li></ul></ul>

×