Application Architectures in Grails


Published on

Talk given at Spring One 2GX 2013. Tries to get Grails developers to start thinking about the structure of their applications.

Published in: Technology
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Application Architectures in Grails

  1. 1. Application architectures in Grails Peter Ledbrook e: t: @pledbrook Wednesday, 11 September 13
  2. 2. Book Author Wednesday, 11 September 13 We start with our domain classes using the usual hasMany, belongsTo etc.
  3. 3. Book AuthorDomain Scaffolded BookController Scaffolded AuthorController Wednesday, 11 September 13 Create instant web UI with scaffolded controllers Can be retained for administrative UI if secured by Spring Security, Shiro, etc.
  4. 4. Book AuthorDomain Scaffolded BookController Scaffolded AuthorController LibraryService LibraryController Views Wednesday, 11 September 13 Build out proper UI using controller actions and views, utilising business logic in the services Controllers stick to HTTP management
  5. 5. Thank you Wednesday, 11 September 13 Some time left for questions...
  6. 6. Only joking Wednesday, 11 September 13
  7. 7. Is it always the best architecture? Wednesday, 11 September 13 How many people are using it?
  8. 8. Book Author Why do we start here? Wednesday, 11 September 13
  9. 9. “The database is just a detail that you don’t need to figure out right away” NO DB - May 2012 Robert “Uncle Bob” Martin Wednesday, 11 September 13
  10. 10. Domain-driven Design Wednesday, 11 September 13 Not the same as domain classes first Model your domain first without integration concerns It’s in operation at all stages of development, not just up-front Reminded of a problem domain related to managing meetings and attendees - focused so hard on the DB tables that the program logic was a dog’s breakfast.
  11. 11. Think Physics Friction Gravity Wednesday, 11 September 13
  12. 12. from Wikmedia Commons Wednesday, 11 September 13 We can use the model to calculate useful information, such as how long it takes for a ball to roll down a hill The model only includes significant complexity - ignores the rest Formula 1 makes use of CFD because they need it at the bleeding edge
  13. 13. Remember: you’re trying to solve a business problem Wednesday, 11 September 13 You need to understand the problem domain The model needs to reflect that understanding Gradle is a great example of a rich, evolving, and useful domain model
  14. 14. The Life Preserver Domain REST Persistence Messaging Events Courtesy of Simplicity Itself Wednesday, 11 September 13 Note how persistence is treated as an integration point Opens up novel approaches Could use mybatis + Flyway instead of GORM for example
  15. 15. An example - reporting ReportController ReportService Jasper Wednesday, 11 September 13 High volume transactional web site, optimised for write Everything was OK at this point
  16. 16. An example - reporting ReportController ReportService Jasper + HTML reports with paging Breakage! Wednesday, 11 September 13 The logic for building reports was complex Who is responsible for the paging? The HTML generation? Where is the state kept? The service? A domain class?
  17. 17. An example - reporting PublisherReport HTTP Request Summary Table 1 Table 2 ... Table N It’s a command object! Wednesday, 11 September 13 Let’s try again The logic for building the report and pagination is in the PublisherReport class
  18. 18. An example - reporting class ReportController { def print(PublisherReport report) { JasperRenderer.render report } def json(PublisherReport report) { render report as JSON } ... } Wednesday, 11 September 13 The controller is now very thin The report can support parameters for sub-reports etc. The domain model is embodied in the command object
  19. 19. CQRS Wednesday, 11 September 13
  20. 20. C Q R S ommand uery esponsibility egregation Wednesday, 11 September 13 The writes use a different model from the reads Will be coming back to this later
  21. 21. What is my domain? Domain Model HTTP Database ? ? Wednesday, 11 September 13 Always ask yourself this question throughout life of project And is it closer to the user’s perspective or the persistence model? Or neither? Former argues for a model based on command objects, the latter based on domain classes.
  22. 22. Post content Wednesday, 11 September 13 The command model is very simple: author + post content + date
  23. 23. Wednesday, 11 September 13 Query model much more complex Multiple timelines Conversation threads Retweets
  24. 24. Wednesday, 11 September 13 So working from your domain first is a good thing And remember that different contexts have potentially different views of the model, i.e. the user/client, persistence, other system components DDD doesn’t preclude the CRUD/service-based architecture So what are the driving forces behind architecture beyond the model?
  25. 25. Rich clients Wednesday, 11 September 13 We’re not talking Warren Buffet here Things like GMail
  26. 26. Once upon a time... Wednesday, 11 September 13
  27. 27. Flash Wednesday, 11 September 13 Pretty (but often useless - or just pretty useless)
  28. 28. Java Wednesday, 11 September 13 Remember applets? Liked the approach (particularly WebStart) but not often used. The browser was a delivery mechanism, not a platform
  29. 29. It’s all about the Javascript! AngularJS Knockout.js Backbone.js Underscore.js jQuery.js Moustache Wednesday, 11 September 13 The browser is now a platform for rich applications But how do these impact the Grails app? The whole process of building a page on each request goes out the window
  30. 30. Google I/O 2012 Android activations to date 400 million Apple WWDC 2012 iOS devices sold to date 365 million Wednesday, 11 September 13 Let’s not forget Firefox OS Lots of people potentially hitting a site at any one time! Typical Grails architecture may struggle to handle the load (OpenSessionInViewInterceptor, transactions, GSPs, thread-per-request)
  31. 31. An aside If the whole Java client thing had worked out, would you use it for every web application you wrote? Would you use it for Wikipedia? Wednesday, 11 September 13 Before jumping onto the whole “single-page app” bandwagon, work out whether it’s appropriate for your app
  32. 32. Shared templates HandlebarsViewResolver or <hbt:render template="..."/> GSP Wednesday, 11 September 13 Not much to talk about on client architecture, but template reuse is something to think about View resolver only makes sense if client-side templates are complete views hbt is a fictitious tag namespace representing a plugin based on Handlebars for Java
  33. 33. AJAX + JSON endpoints enabler for async Wednesday, 11 September 13 Rich UIs don’t talk HTML - use JSON endpoints (aka “REST”) Asset delivery via Resources or asset-pipeline plugins More scope for asynchronicity, since no wait for full page update Grails 2.3 introduces some nice features for REST
  34. 34. What’s the need for SiteMesh & GSP then? Wednesday, 11 September 13 Difficult to impossible to remove these currently Grails 3 will finally extricate them, allowing you to remove them from your project
  35. 35. Aside 2 Don’t be afraid to use Ruby/Node.js tooling Grunt Bower Yeoman Compass/SASS Wednesday, 11 September 13 If you go for a heavy Javascript UI, consider Ruby/Node.js tooling Generally richer than Java-based tooling
  36. 36. Async for scalability Wednesday, 11 September 13 To solve the problem of dealing with large number of concurrent requests Without adding lots more servers
  37. 37. Grails Promise API import static grails.async.Promises.* class ReportController { def print(PublisherReport report) { task { // Expensive report creation here } } ... } Wednesday, 11 September 13 We can now return Promise instances from actions The expensive task no longer blocks the request thread, but...
  38. 38. Controller Request Thread Pool Worker Thread Pool HTTP Request Offload Task Return thread Wednesday, 11 September 13 The request threads are now free, but burden is on worker thread pool If all worker tasks are synchronous, have we gained scalability? In cases where just a few URLs are blocking for long(ish) periods of time, yes (kind of) But otherwise, now bottleneck is on worker thread pool
  39. 39. Make efficient use of server resources Wednesday, 11 September 13 Async all the way through - Grails Promises, GPars, messaging Remember that some things are inherently synchronous (think Fibonacci)
  40. 40. Grails app ReportController TagService PostService Remote access Wednesday, 11 September 13 NetFlix style model: coarse-grained, self-contained services/apps accessed from other apps Usually via REST
  41. 41. Async controllers import static grails.async.Promises.* class ReportController { def tagService def postService def home() { tasks tags: tagService.tagsWithCount() trends: tagService.trendingTags() timeline: postService.timeline( params.userId) } ... } Wednesday, 11 September 13 tagService and postService are both async The model values are evaluated in parallel This is a PromiseMap - view rendered only when all map values evaluated
  42. 42. Async controllers import static grails.async.Promises.* class TagService { def remoteTagService def tagsWithCount() { task { remoteTagService.tagsWithCount() } } ... } Wednesday, 11 September 13 You can also use @DelegateAsync to create async version of synchronous service Currently not Grails’ sweet spot due to the solution’s lightweight nature... ...perhaps makes sense with Grails 3?
  43. 43. Rich domain model + Promises API/GPars? Wednesday, 11 September 13 Fully async backend A good domain model makes it easy to identify parallelisable work No simple solutions though! Concurrency is still a tricky problem.
  44. 44. GPars supports • Dataflow • Communicating Sequential Processes (CSP) • Actor model Wednesday, 11 September 13
  45. 45. Messaging Wednesday, 11 September 13 A common solution to concurrency and scale
  46. 46. MyObject TheirObject call Wednesday, 11 September 13
  47. 47. MyObject TheirObject Router Router message response response message Headers Body Wednesday, 11 September 13 Decoupling via messages Encourages separation of concerns & responsibilities
  48. 48. MyObject OtherObject Router Router message message response response Cloud Wednesday, 11 September 13 Easy to change and move objects Scales well (think Actor model of concurrency)
  49. 49. Internal External JMS RabbitMQ Events Spring Integration Apache Camel Wednesday, 11 September 13 Internal and external can be integrated Events is a special case of messaging (which I look at next)
  50. 50. Spring Integration MyController MyService message DB Persister SplitterJMS Twitter A channel (pipe) Message endpoint Wednesday, 11 September 13 Based on Enterprise Integration Patterns (filters & pipes) Many options for routing and transforming messages Logging adapters and wire tapping for debug
  51. 51. Spring Integration Groovy DSL def builder = new IntegrationBuilder() def ic = builder.doWithSpringIntegration { messageFlow("flow") { filter { it == "World" } transform(inputChannel: "transformerChannel") { "Hello " + it } handle { println "**** $it ****" } } } ic.send "flow.inputChannel", "World" ic.send "transformerChannel", "Earth" Wednesday, 11 September 13
  52. 52. Debugging Code comprehension Performance (kind of) Wednesday, 11 September 13
  53. 53. Events Wednesday, 11 September 13 Special case of messaging
  54. 54. Event bus - ApplicationContext ApplicationContext PluginService publish PluginUpdateService YourListener GORM Wednesday, 11 September 13
  55. 55. Event bus - ApplicationContext class PluginService { def publish(PluginDetails info) { ... publishEvent(new PluginUpdateEvent(...)) } } class PluginUpdateService implements ApplicationListener<PluginUpdateEvent> { def onApplicationEvent(PluginUpdateEvent e) { ... } } Wednesday, 11 September 13 with spring-events plugin
  56. 56. Immutable “mesages” @groovy.transform.Immutable class PluginUpdateEvent { String name String version String group ... } Wednesday, 11 September 13 Event listeners on separate threads (from thread pool)
  57. 57. Event bus - (grails-)events Event bus (Reactor) PluginService publish PluginUpdateService YourListener Wednesday, 11 September 13
  58. 58. Event bus - (grails-)events Event bus (Reactor) Browser RabbitMQ (pending) events-push plugin Wednesday, 11 September 13
  59. 59. Event bus - (grails-)events class PluginService { def publish(PluginDetails info) { ... event "pluginUpdate", info } } class PluginUpdateService { @Selector def pluginUpdate(PluginDetails info) { ... } } Wednesday, 11 September 13 with spring-events plugin
  60. 60. AppEvents.groovy includes = ["push"] doWithReactor = { reactor("grailsReactor") { ext "browser", ["pluginUpdate"] } } Wednesday, 11 September 13 In grails-app/conf Can control which events are propagated to the browser The “push” include sets up a
  61. 61. Include grailsEvents.js window.grailsEvents = new grails.Events(baseUrl) grailsEvents.on("pluginUpdate", function(data) { // do something }); Wednesday, 11 September 13
  62. 62. A CQRS architecture Updates Views Concurrency via event bus Store changes Separate data stores for queries Wednesday, 11 September 13 Why? Updates and querying often have different data requirements. For example, Lanyrd use Redis structured data support All read databases can be rebuilt from master events DB CQRS designed for scale
  63. 63. Plugin Architectures App Feature plugin 1 Feature plugin 2 Feature plugin 3 Events/SI/Message broker Wednesday, 11 September 13 So why use messages to interact between the plugins?
  64. 64. Plugin Architectures App Feature plugin 1 Feature plugin 2 Feature plugin 3 Events/SI/Message broker App 2 Wednesday, 11 September 13 Easy to separate out into apps deployed independently
  65. 65. Ultimately, think about what you need... Wednesday, 11 September 13
  66. 66. ...don’t just go the “standard” route automatically Wednesday, 11 September 13
  67. 67. Thank you Wednesday, 11 September 13