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. 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.
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. 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
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. 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. 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. 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
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.
23. Wednesday, 11 September 13
Query model much more complex
Multiple timelines
Conversation threads
Retweets
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?
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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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)
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. 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. 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. GPars supports
• Dataflow
• Communicating Sequential Processes (CSP)
• Actor model
Wednesday, 11 September 13
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. 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
57. Event bus - (grails-)events
Event bus (Reactor)
PluginService
publish
PluginUpdateService YourListener
Wednesday, 11 September 13
58. Event bus - (grails-)events
Event bus (Reactor)
Browser
RabbitMQ (pending)
events-push plugin
Wednesday, 11 September 13
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. 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
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