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.
Scaffolded
BookController

Domain

Book

Scaffolded
AuthorController

Author

Wednesday, 11 September 13

Create instant w...
Scaffolded
LibraryController
BookController

Views

Scaffolded
LibraryService
AuthorController

Domain

Book

Author

Wedn...
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?
Why do we start here?

Book

Wednesday, 11 September 13

Author
“The database is just a
detail that you don’t need
to figure out right away”
NO DB - May 2012
Robert “Uncle Bob” Martin

We...
Domain-driven Design

Wednesday, 11 September 13

Not the same as domain classes first
Model your domain first without integ...
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 i...
Remember: you’re
trying to solve a
business problem

Wednesday, 11 September 13

You need to understand the problem domain...
The Life Preserver

Domain

Persistence

Messaging

REST

Events
Courtesy of Simplicity Itself
Wednesday, 11 September 13
...
An example - reporting
ReportController

Jasper

Wednesday, 11 September 13

High volume transactional web site, optimised...
An example - reporting
ReportController

Jasper

ReportService

+ HTML reports with paging

Breakage!
Wednesday, 11 Septem...
An example - reporting
HTTP
Request

PublisherReport

Summary
Table 1
Table 2
...

Table N

It’s a command object!

Wednes...
An example - reporting
class ReportController {
def print(PublisherReport report) {
JasperRenderer.render report
}
def jso...
CQRS

Wednesday, 11 September 13
C ommand
Query
Responsibility
Segregation
Wednesday, 11 September 13

The writes use a different model from the reads
Will...
What is my domain?

?

HTTP

Domain Model

?

Database

Wednesday, 11 September 13

Always ask yourself this question thro...
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 pot...
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 bro...
Knockout.js
AngularJS
Backbone.js

It’s all about the
Javascript!

Moustache

Underscore.js

jQuery.js

Wednesday, 11 Sept...
Google I/O 2012

400 million
Android activations to date

Apple WWDC 2012

365 million
iOS devices sold to date

Wednesday...
An aside
If the whole Java client thing had worked out, would
you use it for every web application you wrote?

Would you u...
Shared templates
HandlebarsViewResolver

or
GSP
<hbt:render template="..."/>

Wednesday, 11 September 13

Not much to talk...
AJAX + JSON endpoints
enabler for async

Wednesday, 11 September 13

Rich UIs don’t talk HTML - use JSON endpoints (aka “R...
What’s the need for
SiteMesh & GSP then?

Wednesday, 11 September 13

Difficult to impossible to remove these currently
Gr...
Aside 2

Don’t be afraid to use Ruby/Node.js tooling
Compass/SASS
Grunt

Bower
Yeoman

Wednesday, 11 September 13

If you ...
Async
for scalability

Wednesday, 11 September 13

To solve the problem of dealing with large number of concurrent request...
Grails Promise API
import static grails.async.Promises.*
class ReportController {
def print(PublisherReport report) {
task...
Request
Thread Pool

Worker
Thread Pool

Return thread

HTTP
Request

Controller

Offload

Task

Wednesday, 11 September 13...
Make efficient use of
server resources

Wednesday, 11 September 13

Async all the way through - Grails Promises, GPars, mes...
TagService

PostService

Remote
access
Grails app
ReportController

Wednesday, 11 September 13

NetFlix style model: coars...
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...
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

Wednesday, 11 September 13

call

TheirObject
message
Headers

Router

Body

message

MyObject

TheirObject
response

response

Router

Wednesday, 11 September 13

Deco...
Cloud
Router
message

message

OtherObject

MyObject

response

response

Router

Wednesday, 11 September 13

Easy to chan...
Internal

External

Events

JMS

Spring Integration

RabbitMQ

Apache Camel

Wednesday, 11 September 13

Internal and exte...
Spring Integration
MyController

MyService

Message endpoint

message
DB Persister

JMS

Splitter

Wednesday, 11 September...
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
GORM

ApplicationContext
publish
PluginService

Wednesday, 11 September 13

PluginUpdateSer...
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
...
}

W...
Event bus - (grails-)events
Event bus (Reactor)
publish
PluginService

Wednesday, 11 September 13

PluginUpdateService

Yo...
Event bus - (grails-)events
Event bus (Reactor)

RabbitMQ (pending)

events-push plugin
Browser

Wednesday, 11 September 1...
Event bus - (grails-)events
class PluginService {
def publish(PluginDetails info) {
...
event "pluginUpdate", info
}
}
cla...
AppEvents.groovy
includes = ["push"]
doWithReactor = {
reactor("grailsReactor") {
ext "browser", ["pluginUpdate"]
}
}

Wed...
Include grailsEvents.js
window.grailsEvents = new grails.Events(baseUrl)
grailsEvents.on("pluginUpdate", function(data) {
...
A CQRS architecture
Updates
Store changes

Concurrency
via event bus
Views

Separate data stores
for queries
Wednesday, 11...
Plugin Architectures
App

Feature
plugin 1

Feature
plugin 2

Feature
plugin 3

Events/SI/Message broker
Wednesday, 11 Sep...
Plugin Architectures
App

Feature
plugin 1

App 2

Feature
plugin 2

Feature
plugin 3

Events/SI/Message broker
Wednesday,...
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 with Grails

1,754

Published on

Speaker: Peter Ledbrook
Grails is geared towards an MVC architecture with a database back-end and a service layer. But is this the only way to write Grails applications? What other architectures are possible and what is required to implement them?
In this talk we'll answer those questions by looking at several different architectures in the context of Grails. From single-page apps to event based back-ends, you'll get a good overview of just what is possible and you'll be able to make an informed decision on how to implement your next project.

Published in: Technology
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,754
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
35
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

Application Architectures with 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. Scaffolded BookController Domain Book Scaffolded AuthorController Author 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. Scaffolded LibraryController BookController Views Scaffolded LibraryService AuthorController Domain Book Author 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. Why do we start here? Book Wednesday, 11 September 13 Author
  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 Persistence Messaging REST 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 Jasper Wednesday, 11 September 13 High volume transactional web site, optimised for write Everything was OK at this point ReportService
  16. 16. An example - reporting ReportController Jasper ReportService + 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 HTTP Request PublisherReport 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 ommand Query Responsibility Segregation 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? ? HTTP Domain Model ? 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. Knockout.js AngularJS Backbone.js It’s all about the Javascript! Moustache Underscore.js jQuery.js 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 400 million Android activations to date Apple WWDC 2012 365 million iOS devices sold to date 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 GSP <hbt:render template="..."/> 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 Compass/SASS Grunt Bower Yeoman 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. Request Thread Pool Worker Thread Pool Return thread HTTP Request Controller Offload Task 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. TagService PostService Remote access Grails app ReportController 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 Wednesday, 11 September 13 call TheirObject
  47. 47. message Headers Router Body message MyObject TheirObject response response Router Wednesday, 11 September 13 Decoupling via messages Encourages separation of concerns & responsibilities
  48. 48. Cloud Router message message OtherObject MyObject response response Router Wednesday, 11 September 13 Easy to change and move objects Scales well (think Actor model of concurrency)
  49. 49. Internal External Events JMS Spring Integration RabbitMQ 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 endpoint message DB Persister JMS Splitter 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 A channel (pipe) Twitter
  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 GORM ApplicationContext publish PluginService Wednesday, 11 September 13 PluginUpdateService YourListener
  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) publish PluginService Wednesday, 11 September 13 PluginUpdateService YourListener
  58. 58. Event bus - (grails-)events Event bus (Reactor) RabbitMQ (pending) events-push plugin Browser 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 Store changes Concurrency via event bus Views 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 App 2 Feature plugin 2 Feature plugin 3 Events/SI/Message broker 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.

×