"Scala in Goozy", Alexey Zlobin


Published on

Talk in Scala in Goozy (http://goozy.com/), by Alexey @CheatEx Zlobin, at scalaby#8

Published in: Technology
  • Be the first to comment

  • Be the first to like this

No Downloads
Total Views
On Slideshare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

"Scala in Goozy", Alexey Zlobin

  1. 1. Scala in Goozy Alexey Zlobin, e-Legion Alexey.Zlobin@gmail.com @CheatEx
  2. 2. Index1. Goozy overview2. Scalas place3. Lift4. Cake pattern in scale5. Scalaz and other fancy stuff6. Summary
  3. 3. What is Goozy?A social network built aroundthe concept of sticky note ● A note could be left anywhere in the Web. ● It is associated with particular page element.Central UI concept: usersrelated feed with all new notesand comments
  4. 4. Top-level architecture
  5. 5. Scalas placeAPI serverMain functions: ● Storage access ● Background tasks (feed writing) ● Email sending ● Text indexing
  6. 6. Why scala?● Fast● Сoncise● Expressive● Advanced OO● Some functional stuff ○ Simple concurrency● All Java legacy available
  7. 7. The team● 2 persons● Strong Java background● Fancy about technologies● Love to try new things
  8. 8. Lift Utilized features: ● REST ● JSON serialisation Thats it...
  9. 9. Lift: issuesLocalisation performance ● Hand-made localisation on standard resource bundles gave 4 times throughput improvement.Very memory-consuming JSON serialisation. ● Not so efficient PrettyPrinter is used ● Functional-styled string escapingPoor code style ● Extremely long map-match-if hierarchies ● Mutable, difficult to debug LiftRules design
  10. 10. Goozy API logical structureApplication is composedfrom three layers.Each layer consists fromseveral similarcomponents. LikeUserController,UserService, etc.
  11. 11. Conceptual problems● Components of each level depend from each other● Components most likely have several dependencies from the previous level● A lot of common stuff inside a level ○ Every storage needs a DB connection ○ Every service needs an entire storage system and access to text indexes ○ Etc...
  12. 12. The solution: cake pattern● Each logically closed piece of functionality is represented as component● Dependencies are expressed as self-types● Common features expressed as mix-ins● Common combinations of functionality are expressed as mix-ins of several components
  13. 13. Cake pattern: consequences+ All top-level architecture - Long dependency listsis expressed in one less ● Poor design?than 100 LOC file - Implicit dependency on mix-+ Compile-time in order (linearisation strikesdependency checks back) ● Prefer def and lazy+ The biggest file isaround 1000 LOC - A bit unclear how to deal with several dependencies of the same type but different runtime implementation
  14. 14. scalaz.Validation One sweet morning I sent a link to the colleague... On the next morning we had a new dependency and totally refactored request parameters analysis. Now everything is validated.
  15. 15. Initial solution: domain exceptionsclass GoozzyException( val message: String) extendsException(message)case class UserNotFound( userId: String) extendsGoozzyException( "user " + userId + " not found")
  16. 16. Error handling: own error typeabstract class Error( val httpCode: Int, val code: String)abstract class PermissionError( code: String) extendsValidationError(403, code)case class Banned( userId: String, groupId: String) extendsPermissionError("BANNED")
  17. 17. Validations and exceptionsProblem: exceptions are hereSolution: catchem and convert!
  18. 18. Validations and exceptions1. Define the "converter"2. Write some utilities to make them more accessible
  19. 19. One more error for whatever happencase class ExceptionalError( @transient exception: Throwable) extendsInternalError
  20. 20. The converterFunction is a perfect abstraction!val dispatchException: Function[ Throwable, Error] = { case UserNotFound(name) => MissedEntity(name, "user") ... //dont remove it!!! case t => ExceptionalError(t) }
  21. 21. Useful utils: generictype GzVal[+OUT] = Validation[Error, OUT]def safeVal[T]: Catch[GzVal[T]] = handling(classOf[Throwable]) by { e => dispatchException(e).fail }def safe[T](block: => T): GzVal[T] = safeVal( block.success )def editData(up: Edit): GzVal[Data] = safeVal { //all the dangerous stuff here }
  22. 22. Useful utils: specificJust a trivial code which parses strings, extractsvalues from mongo objects, etc.See my blog for details...
  23. 23. Validation: the good thingSome code was pretty:for { data <- getData(recordId) userData <- getUserData(userId) permission <- checkPermission(data, userData) newData <- updateData(data)} yield { //all dirty hacks there}
  24. 24. Conversion: the problemBut some other code was...
  25. 25. Not so prettydef readFields(rec: DBObject, ...): GzVal[Fields] = { val deletedBy = for (userId <- get[ObjectId](note, "deleted_by"); user <- getUserData(userId).toSuccess(MissedEntity(...))) yield user for { id <- get[String](rec, "_id") content <- get[String](rec, "content") updated <- asValidOption(get[DateTime](rec, "upd")) //twelve more user <- getUserById(userId, currentUserId map (new ObjectId(_))) .toSuccess( MissedEntity(userId.toString, "user"): ValidationError) } yield DataRecord(/*you wont see it*/)}
  26. 26. Improved solution1. Dont play haskell2. Play java3. ...when it is easier
  27. 27. Error handling: big picture
  28. 28. Validation: pros and cons+ Comprehensible error - Monads and Applicativesaggregation and reporting cause massive brain damage+ The only imaginable wayto deal with 20+ request - Complicated error reportsparameters with 2-3 from the compilerconstraints on each - You cant just ignore+ Unified approach to possibility of runtimerequest validation and exceptionruntime error handling+ Type-level check forcorrect error handling
  29. 29. Lessons1. Always prefer simple tools2. Options and Eithers (Validations): they really work ○ It is possible to live with both exceptions and eithers ○ Performance consequences are not clear ○ Some times I had to use things far behind my understanding (sequence, traverse)3. Server-side testing is difficult ○ Testing approach should be established before any code is written
  30. 30. References1. http://www.assembla. com/spaces/liftweb/wiki/REST_Web_Services - REST support in Lift2. http://jonasboner.com/2008/10/06/real-world-scala- dependency-injection-di.html - complete cake pattern intro3. https://gist.github.com/970717 - easy Validation example4. http://www.scala-lang. org/api/current/scala/util/control/Exception$.html - built-in DSL for exception handling
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.