Your SlideShare is downloading. ×
0
Application architectures
in Grails
Peter Ledbrook
e: p.ledbrook@cacoethes.co.uk
t: @pledbrook
Wednesday, 11 September 13
Book Author
Wednesday, 11 September 13
We start with our domain classes using the usual hasMany, belongsTo etc.
Book AuthorDomain
Scaffolded
BookController
Scaffolded
AuthorController
Wednesday, 11 September 13
Create instant web UI w...
Book AuthorDomain
Scaffolded
BookController
Scaffolded
AuthorController
LibraryService
LibraryController Views
Wednesday, ...
Thank you
Wednesday, 11 September 13
Some time left for questions...
Only joking
Wednesday, 11 September 13
Is it always the best
architecture?
Wednesday, 11 September 13
How many people are using it?
Book Author
Why do we start here?
Wednesday, 11 September 13
“The database is just a
detail that you don’t need
to figure out right away”
NO DB - May 2012
Robert “Uncle Bob” Martin
Wed...
Domain-driven Design
Wednesday, 11 September 13
Not the same as domain classes first
Model your domain first without integra...
Think Physics
Friction
Gravity
Wednesday, 11 September 13
from Wikmedia Commons
Wednesday, 11 September 13
We can use the model to calculate useful information, such as how long it...
Remember: you’re
trying to solve a
business problem
Wednesday, 11 September 13
You need to understand the problem domain
T...
The Life Preserver
Domain
REST
Persistence
Messaging
Events
Courtesy of Simplicity Itself
Wednesday, 11 September 13
Note ...
An example - reporting
ReportController ReportService
Jasper
Wednesday, 11 September 13
High volume transactional web site...
An example - reporting
ReportController ReportService
Jasper + HTML reports with paging
Breakage!
Wednesday, 11 September ...
An example - reporting
PublisherReport
HTTP
Request
Summary
Table 1
Table 2
...
Table N
It’s a command object!
Wednesday, ...
An example - reporting
class ReportController {
def print(PublisherReport report) {
JasperRenderer.render report
}
def jso...
CQRS
Wednesday, 11 September 13
C
Q
R
S
ommand
uery
esponsibility
egregation
Wednesday, 11 September 13
The writes use a different model from the reads
Wi...
What is my domain?
Domain Model
HTTP Database
? ?
Wednesday, 11 September 13
Always ask yourself this question throughout ...
Post content
Wednesday, 11 September 13
The command model is very simple: author + post content + date
Wednesday, 11 September 13
Query model much more complex
Multiple timelines
Conversation threads
Retweets
Wednesday, 11 September 13
So working from your domain first is a good thing
And remember that different contexts have pote...
Rich clients
Wednesday, 11 September 13
We’re not talking Warren Buffet here
Things like GMail
Once upon a time...
Wednesday, 11 September 13
Flash
Wednesday, 11 September 13
Pretty (but often useless - or just pretty useless)
Java
Wednesday, 11 September 13
Remember applets?
Liked the approach (particularly WebStart) but not often used. The brows...
It’s all about the
Javascript!
AngularJS
Knockout.js
Backbone.js
Underscore.js
jQuery.js
Moustache
Wednesday, 11 September...
Google I/O 2012
Android activations to date
400 million
Apple WWDC 2012
iOS devices sold to date
365 million
Wednesday, 11...
An aside
If the whole Java client thing had worked out, would
you use it for every web application you wrote?
Would you us...
Shared templates
HandlebarsViewResolver
or
<hbt:render template="..."/>
GSP
Wednesday, 11 September 13
Not much to talk ab...
AJAX + JSON endpoints
enabler for async
Wednesday, 11 September 13
Rich UIs don’t talk HTML - use JSON endpoints (aka “RES...
What’s the need for
SiteMesh & GSP then?
Wednesday, 11 September 13
Difficult to impossible to remove these currently
Grai...
Aside 2
Don’t be afraid to use Ruby/Node.js tooling
Grunt
Bower
Yeoman
Compass/SASS
Wednesday, 11 September 13
If you go f...
Async
for scalability
Wednesday, 11 September 13
To solve the problem of dealing with large number of concurrent requests
...
Grails Promise API
import static grails.async.Promises.*
class ReportController {
def print(PublisherReport report) {
task...
Controller
Request
Thread Pool
Worker
Thread Pool
HTTP
Request
Offload
Task
Return thread
Wednesday, 11 September 13
The re...
Make efficient use of
server resources
Wednesday, 11 September 13
Async all the way through - Grails Promises, GPars, messa...
Grails app
ReportController
TagService PostService
Remote
access
Wednesday, 11 September 13
NetFlix style model: coarse-gr...
Async controllers
import static grails.async.Promises.*
class ReportController {
def tagService
def postService
def home()...
Async controllers
import static grails.async.Promises.*
class TagService {
def remoteTagService
def tagsWithCount() {
task...
Rich domain model +
Promises API/GPars?
Wednesday, 11 September 13
Fully async backend
A good domain model makes it easy t...
GPars supports
• Dataflow
• Communicating Sequential Processes (CSP)
• Actor model
Wednesday, 11 September 13
Messaging
Wednesday, 11 September 13
A common solution to concurrency and scale
MyObject TheirObject
call
Wednesday, 11 September 13
MyObject TheirObject
Router
Router
message
response response
message
Headers
Body
Wednesday, 11 September 13
Decoupling vi...
MyObject OtherObject
Router
Router
message
message
response
response
Cloud
Wednesday, 11 September 13
Easy to change and m...
Internal External
JMS
RabbitMQ
Events
Spring Integration
Apache Camel
Wednesday, 11 September 13
Internal and external can...
Spring Integration
MyController
MyService
message
DB Persister
SplitterJMS Twitter
A channel (pipe)
Message endpoint
Wedne...
Spring Integration Groovy DSL
def builder = new IntegrationBuilder()
def ic = builder.doWithSpringIntegration {
messageFlo...
Debugging
Code comprehension
Performance (kind of)
Wednesday, 11 September 13
Events
Wednesday, 11 September 13
Special case of messaging
Event bus - ApplicationContext
ApplicationContext
PluginService
publish
PluginUpdateService YourListener
GORM
Wednesday, 1...
Event bus - ApplicationContext
class PluginService {
def publish(PluginDetails info) {
...
publishEvent(new PluginUpdateEv...
Immutable “mesages”
@groovy.transform.Immutable
class PluginUpdateEvent {
String name
String version
String group
...
}
We...
Event bus - (grails-)events
Event bus (Reactor)
PluginService
publish
PluginUpdateService YourListener
Wednesday, 11 Septe...
Event bus - (grails-)events
Event bus (Reactor)
Browser
RabbitMQ (pending)
events-push plugin
Wednesday, 11 September 13
Event bus - (grails-)events
class PluginService {
def publish(PluginDetails info) {
...
event "pluginUpdate", info
}
}
cla...
AppEvents.groovy
includes = ["push"]
doWithReactor = {
reactor("grailsReactor") {
ext "browser", ["pluginUpdate"]
}
}
Wedn...
Include grailsEvents.js
window.grailsEvents = new grails.Events(baseUrl)
grailsEvents.on("pluginUpdate", function(data) {
...
A CQRS architecture
Updates
Views
Concurrency
via event bus
Store changes
Separate data stores
for queries
Wednesday, 11 S...
Plugin Architectures
App
Feature
plugin 1
Feature
plugin 2
Feature
plugin 3
Events/SI/Message broker
Wednesday, 11 Septemb...
Plugin Architectures
App
Feature
plugin 1
Feature
plugin 2
Feature
plugin 3
Events/SI/Message broker
App 2
Wednesday, 11 S...
Ultimately, think about
what you need...
Wednesday, 11 September 13
...don’t just go the
“standard” route
automatically
Wednesday, 11 September 13
Thank you
Wednesday, 11 September 13
Upcoming SlideShare
Loading in...5
×

Application Architectures in Grails

5,946

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

Transcript of "Application Architectures in Grails"

  1. 1. Application architectures in Grails Peter Ledbrook e: p.ledbrook@cacoethes.co.uk 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
  1. A particular slide catching your eye?

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

×