SlideShare una empresa de Scribd logo
1 de 53
Descargar para leer sin conexión
Grails: Patterns &
Practices
Paul Bowler
Senior Consultant, OpenCredo
Who are you?
Coding Dojo
• Agile!
• Teams of 2-4 people
• 1 sprint = 20 minutes
• Domain-Drive Design (I’m the Product Owner)
• Test-Driven Development (Maybe!)
• Yes, you can use the user guide and internet!
• User demo at the end of each sprint
• Discussion + Refactoring
• Prize for best app!
?
5 ‘Maturity’ Levels
?
?
?
?
Domain-Driven Design
• “Domain-driven design (DDD) is an approach to developing
software for complex needs by deeply connecting the
implementation to an evolving model of the core business
concepts.”
• The premise of domain-driven design is the following:
• Placing the project's primary focus on the core domain and
domain logic
• Basing complex designs on a model
• Initiating a creative collaboration between technical and
domain experts to iteratively cut ever closer to the
conceptual heart of the problem.
User Story 1
“As a pomodoro fan, I would like to be able to add
tasks to a uniquely named activity inventory, so that I
can see what work I need to complete over the next
few weeks.”
Useful Commands
• grails create-app pomodoro
• grails create-domain-class <domain>
• grails generate-all <domain>
• grails generate-controller <domain> and
add ‘static scaffold = true’ to controller
Considerations
• Associations
• One-to-One
• One-to-Many
• Many-to-Many
• Constraints &Validation
• Time-Stamping?
• Default values (and field values inViews?)
You did create some
tests first, right?
Implementation
class Task {
Inventory inventory
String description
static belongsTo = [Inventory]
static constraints = {
description(nullable: false, blank: false)
}
}
class Inventory {
String name
static hasMany = [tasks: Task]
static constraints = {
name(nullable: false, blank: false, unique: true)
}
}
Domain Tests
class InventoryTests extends GrailsUnitTestCase {
void testConstraints() {
def existingInventory = new Inventory(name: "Paul’s Inventory")
mockForConstraintsTests(Inventory, [ existingInventory ])
! ! // Validation should fail if both properties are null.
! ! def inventory = new Inventory()
! ! assertFalse inventory.validate()
! ! assertEquals "nullable", inventory.errors["description"]
! ! // So let's demonstrate the unique constraint.
! ! inventory = new Inventory(name: "Paul’s Inventory")
! ! assertFalse inventory.validate()
! ! assertEquals "unique", inventory.errors["name"]
! ! // Validation should pass!
! ! inventory = new Inventory(name: "John’s Inventory")
! ! assertTrue inventory.validate()
! }
}
Gotcha!
• Potential performance issue with mapped
collections:
• Adding to the Set requires loading all
instances from the database to ensure
uniqueness
• Likewise for mapped List
• Works fine in development, but what if
you have 1,000,000+ rows?
Implementation (2)
class Task {
Inventory inventory
String description
static constraints = {
description(nullable: false, blank: false)
}
}
class Inventory {
String name
}
Side-effects?
• Different syntax for adding Tasks
• No cascading deletes
• Custom finder required to find all Tasks in
an Inventory
• Scaffolding breaks!
User Story 2
“As a pomodoro fan, I would like to be able move tasks
onto a ‘To Do Today’ sheet, so that I can see work to be
completed today and view my work history.”
Considerations
• Does the ‘Today’ list share any common
attributes with the Inventory?
• How about a more intuitive URL scheme?
?
Level 1
?
?
?
Views
Level 1 -Views
Controller
Model
PageView PageViewPageView
Model Model
Level 1 ‘Smells’
• Logic built into pages:
• Overuse of Request Parameters
• If-Then tags
• Inline groovy using ${...}
• Poor use of layouts
• Little use of tags
• Domain classes as simple ‘active records’
• Page-based information architecture
?
Level 2
?
?
Controllers
Views
Level 1-2 Refactoring
• Move logic out of pages into controllers
• Reduce pages into fragments
• Use layouts to construct device or stakeholder-
centric views from pages and fragments
• Use available tag libraries
• Create your own tag libraries!
• Stylesheets rule - minimise markup
User Story 3
“As a pomodoro fan, I would like to have an optimised
workflow for US2, so that I can save time and reduce
input mistakes.”
Considerations
• Web Flow plugin?
• Command Objects?
• What changes need to be made to domain
classes?
Web Flow
class InventoryController {
…
def inventoryFlow = {
showInventory {
on("done").to "saveInventory"
on("continue").to "addTask"
}
…
addTask {
redirect(controller:"task", action:"create")
}
saveInventory()
}
}
<g:form action="inventory">
<g:submitButton name="continue" value="Add Another"></g:submitButton>
<g:submitButton name="done" value="I’m Done"></g:submitButton>
</g:form>
User Story 4
“As a pomodoro fan, I would like to be able update the
number of iterations I’ve completed on each task in my
‘To Do Today’ list, so that I can keep track of my
progress and improve my future estimates.”
Considerations
• Can we do this without page refreshes?
• How can we test this?
• Domain changes?
Level 2 - Controllers
Controller
Domain
Controller Controller
Domain Domain Domain
Layout
Fragments Fragments
Layout
Fragments Fragments
Layout
Fragments Fragments
Level 2 ‘Smells’
• Large, complex Controllers
• Different scenarios driven by ‘If/Then’ logic
• Content negotiation increases complexity
further
• Many similar controller methods (not
DRY!)
• Poorly handled Transactions
?
Level 3 - Services
?
Services
Controllers
Views
Level 2-3 Refactoring
• Move domain transaction logic out of
controllers into services
• Controllers should be ‘glue’ that binds
business services to UI
• Service methods should reflect business
scenarios
• Make use of transactional capability of
services
User Story 5
“As a pomodoro partner, I would like a simple REST
API over your daily task view, so I can integrate your
data into my application.”
Considerations
• Don’t clutter your Controllers!
• REST-ful URLs
• Content negotiation?
• Custom XML/JSON formats?
RESTful URL Mappings
static mappings = {
"/task/$id?"(resource:"task")
}
static mappings = {
"/task/$id"(controller:"task") {
action = [GET:"show", PUT:"update", DELETE:"delete", POST:"save"]
}
}
static mappings = {
"/task/$id"(controller:"task", parseRequest:true) {
action = [GET:"show", PUT:"update", DELETE:"delete", POST:"save"]
}
}
Content Negotiation
class InventoryController {
def inventory
def list = {
this.inventory = Inventory.list()
withFormat {
html inventoryList:inventory
json { render inventory as JSON }
xml { render inventory as XML }
}
}
}
Custom Formats?
def listAsXML = {
def inventory = Inventory.get(params.id)
def tasks = inventory.tasks
render(contentType:"text/xml") {
inventory(name:inventory.name) {
tasks {
for(t in tasks) {
task(title:t.title)
! }
}!
}
}
}
User Story 6
“As a pomodoro fan, I’d like to be able to add
unplanned and urgent tasks to the bottom of my daily
list, so that I can track and manage interruptions.”
Level 3 - Services
Controller
Domain
Controller Controller
Domain Domain Domain
Services
Layout
Fragments Fragments
Layout
Fragments Fragments
Layout
Fragments Fragments
Level 3 ‘Smells’
• Large, complex Services
• Services acting as proxies for domain
behaviour
• ‘Cut-and-paste’ methods
?
Level 4 - Libraries
Libraries
Services
Controllers
Views
Level 3-4 Refactoring
• Move common code out of services into
POGO’s (or POJO’s)
• Enrich our domain model to simplify services:
• Named Queries
• Derived Properties
• Criteria: Conjunctions, Disjunctions,
Projections, Restrictions
User Story 7
“As a pomodoro fan, I would like to be able to search
for tasks on my inventory through a simple interface, so
I can find and modify them easily.”
Level 4 - Libraries
Controller
Domain
Controller Controller
Domain Domain Domain
Services
Libraries Libraries
Layout
Fragments Fragments
Layout
Fragments Fragments
Layout
Fragments Fragments
Level 4 ‘Smells’
• Large, monolithic application
• Increased cognitive overhead
• New starters struggle
• Components ‘cut and pasted’ into similar
projects
Plugins
Level 5 - Plugins
Libraries
Services
Controllers
Views
Level 4-5 Refactoring
• Componentise the application into plugins
• Construct applications by combining plugins
• Could your application itself be constructed
as a plugin for an organisation’s product
suite?
• Writing plugins that modify the Grails/Spring
context is beyond the scope of this
workshop!
User Story 8
“As a pomodoro fan, I would like a simplified version of
my Inventory, so I can view it on my iPhone.”
Useful Commands
• grails create-plugin <plugin>
• grails package-plugin
• grails install-plugin /path/to/plugin/grails-
example-0.1.zip
Layouts and Fragments
<g:include action="show" id="1" />
<g:include action="show" id="${currentTask.id}" />
<g:include controller="task" />
<g:include controller="task" action="list" />
<g:include action="list" params="[sort:'title', order:'asc'] />
<html>
<head>
<title><g:layoutTitle default="An example decorator" /></title>
<g:layoutHead />
</head>
<body>
<div class="menu"><!--my common menu goes here--></menu>
<div class="body">
<g:layoutBody />
</div>
</div>
</body>
</html>
Layout Options
• In your views:
<meta name="layout" content="main"></meta>
• In your controller:
static layout = 'task'
static layout = 'custom/task'
• By Convention:
grails-app/views/layouts/task.gsp
grails-app/views/layouts/task/list.gsp
• Inline:
<g:applyLayout name="myLayout" template="taskTemplate" collection="${tasks}" />
<g:applyLayout name="myLayout" url="http://www.google.com" />
<g:applyLayout name="myLayout">The content to apply a layout to</g:applyLayout>
Level 5 - Plugins
Controller
Domain
Controller
Domain Domain
Plugins
Page
View
Page
View
Controller
Services
Libraries / Plugins
Domain Domain
Layout
Fragments Fragments
Layout
Fragments Fragments
Services
Libraries Libraries
Plugins
Libraries
Services
Controllers
The Full Picture
Views
Phew!
Well Done.

Más contenido relacionado

La actualidad más candente

What is SharePoint Development??
What is SharePoint Development??What is SharePoint Development??
What is SharePoint Development??
Mark Rackley
 
Flash Platformアップデート
Flash PlatformアップデートFlash Platformアップデート
Flash Platformアップデート
Mariko Nishimura
 
Alexander Zeng
Alexander ZengAlexander Zeng
Alexander Zeng
Alex Zeng
 
Developing Mobile HTML5 Apps with Grails
Developing Mobile HTML5 Apps with GrailsDeveloping Mobile HTML5 Apps with Grails
Developing Mobile HTML5 Apps with Grails
GR8Conf
 
Moving to the Client - JavaFX and HTML5
Moving to the Client - JavaFX and HTML5Moving to the Client - JavaFX and HTML5
Moving to the Client - JavaFX and HTML5
Stephen Chin
 

La actualidad más candente (20)

What is SharePoint Development??
What is SharePoint Development??What is SharePoint Development??
What is SharePoint Development??
 
JavaFX Versus HTML5 - JavaOne 2014
JavaFX Versus HTML5 - JavaOne 2014JavaFX Versus HTML5 - JavaOne 2014
JavaFX Versus HTML5 - JavaOne 2014
 
Moving to the Client - JavaFX and HTML5
Moving to the Client - JavaFX and HTML5Moving to the Client - JavaFX and HTML5
Moving to the Client - JavaFX and HTML5
 
Java APIs- The missing manual (concurrency)
Java APIs- The missing manual (concurrency)Java APIs- The missing manual (concurrency)
Java APIs- The missing manual (concurrency)
 
Moving To The Client - JavaFX and HTML5
Moving To The Client - JavaFX and HTML5Moving To The Client - JavaFX and HTML5
Moving To The Client - JavaFX and HTML5
 
Google App Engine Java, Groovy and Gaelyk
Google App Engine Java, Groovy and GaelykGoogle App Engine Java, Groovy and Gaelyk
Google App Engine Java, Groovy and Gaelyk
 
Flash Platformアップデート
Flash PlatformアップデートFlash Platformアップデート
Flash Platformアップデート
 
Java 11 OMG
Java 11 OMGJava 11 OMG
Java 11 OMG
 
Crx 2.2 Deep-Dive
Crx 2.2 Deep-DiveCrx 2.2 Deep-Dive
Crx 2.2 Deep-Dive
 
A new tool for measuring performance in Drupal 8 - DrupalCamp London
A new tool for measuring performance in Drupal 8 - DrupalCamp LondonA new tool for measuring performance in Drupal 8 - DrupalCamp London
A new tool for measuring performance in Drupal 8 - DrupalCamp London
 
Alexander Zeng
Alexander ZengAlexander Zeng
Alexander Zeng
 
Top 8 benefits of react js
Top 8 benefits of react jsTop 8 benefits of react js
Top 8 benefits of react js
 
Java WebStart Is Dead: What Should We Do Now?
Java WebStart Is Dead: What Should We Do Now?Java WebStart Is Dead: What Should We Do Now?
Java WebStart Is Dead: What Should We Do Now?
 
Moving to the Client - JavaFX and HTML5
Moving to the Client - JavaFX and HTML5Moving to the Client - JavaFX and HTML5
Moving to the Client - JavaFX and HTML5
 
Scribe online 03 scribe online cdk and api overview
Scribe online 03   scribe online cdk and api overviewScribe online 03   scribe online cdk and api overview
Scribe online 03 scribe online cdk and api overview
 
Developing Mobile HTML5 Apps with Grails
Developing Mobile HTML5 Apps with GrailsDeveloping Mobile HTML5 Apps with Grails
Developing Mobile HTML5 Apps with Grails
 
Big ideas in small packages - How microservices helped us to scale our vision
Big ideas in small packages  - How microservices helped us to scale our visionBig ideas in small packages  - How microservices helped us to scale our vision
Big ideas in small packages - How microservices helped us to scale our vision
 
S60 3rd FP2 Widgets
S60 3rd FP2 WidgetsS60 3rd FP2 Widgets
S60 3rd FP2 Widgets
 
Moving to the Client - JavaFX and HTML5
Moving to the Client - JavaFX and HTML5Moving to the Client - JavaFX and HTML5
Moving to the Client - JavaFX and HTML5
 
Micronaut: Changing the Micro Future
Micronaut: Changing the Micro FutureMicronaut: Changing the Micro Future
Micronaut: Changing the Micro Future
 

Similar a Grails patterns and practices

5 Common Mistakes You are Making on your Website
 5 Common Mistakes You are Making on your Website 5 Common Mistakes You are Making on your Website
5 Common Mistakes You are Making on your Website
Acquia
 
SharePoint 2014: Where to save my data, for devs!
SharePoint 2014: Where to save my data, for devs!SharePoint 2014: Where to save my data, for devs!
SharePoint 2014: Where to save my data, for devs!
Ben Steinhauser
 
Designing your API Server for mobile apps
Designing your API Server for mobile appsDesigning your API Server for mobile apps
Designing your API Server for mobile apps
Mugunth Kumar
 

Similar a Grails patterns and practices (20)

5 Common Mistakes You are Making on your Website
 5 Common Mistakes You are Making on your Website 5 Common Mistakes You are Making on your Website
5 Common Mistakes You are Making on your Website
 
SharePoint 2014: Where to save my data, for devs!
SharePoint 2014: Where to save my data, for devs!SharePoint 2014: Where to save my data, for devs!
SharePoint 2014: Where to save my data, for devs!
 
Add-On Development: EE Expects that Every Developer will do his Duty
Add-On Development: EE Expects that Every Developer will do his DutyAdd-On Development: EE Expects that Every Developer will do his Duty
Add-On Development: EE Expects that Every Developer will do his Duty
 
presentation
presentationpresentation
presentation
 
presentation
presentationpresentation
presentation
 
Add-On Development: EE Expects that Every Developer will do his Duty
Add-On Development: EE Expects that Every Developer will do his DutyAdd-On Development: EE Expects that Every Developer will do his Duty
Add-On Development: EE Expects that Every Developer will do his Duty
 
JS Essence
JS EssenceJS Essence
JS Essence
 
Nsc 2013 06-17 - random rants on 2013
Nsc 2013 06-17 - random rants on 2013Nsc 2013 06-17 - random rants on 2013
Nsc 2013 06-17 - random rants on 2013
 
Creating a Documentation Portal
Creating a Documentation PortalCreating a Documentation Portal
Creating a Documentation Portal
 
Using BladeRunnerJS to Build Front-End Apps that Scale - Fluent 2014
Using BladeRunnerJS to Build Front-End Apps that Scale - Fluent 2014Using BladeRunnerJS to Build Front-End Apps that Scale - Fluent 2014
Using BladeRunnerJS to Build Front-End Apps that Scale - Fluent 2014
 
Where to save my data, for devs!
Where to save my data, for devs!Where to save my data, for devs!
Where to save my data, for devs!
 
CUST-2 New Client Configuration & Extension Points in Share
CUST-2 New Client Configuration & Extension Points in ShareCUST-2 New Client Configuration & Extension Points in Share
CUST-2 New Client Configuration & Extension Points in Share
 
Masterin Large Scale Java Script Applications
Masterin Large Scale Java Script ApplicationsMasterin Large Scale Java Script Applications
Masterin Large Scale Java Script Applications
 
CUST-1 Share Document Library Extension Points
CUST-1 Share Document Library Extension PointsCUST-1 Share Document Library Extension Points
CUST-1 Share Document Library Extension Points
 
Office 365 Saturday (Sydney) - SharePoint framework – build integrated user e...
Office 365 Saturday (Sydney) - SharePoint framework – build integrated user e...Office 365 Saturday (Sydney) - SharePoint framework – build integrated user e...
Office 365 Saturday (Sydney) - SharePoint framework – build integrated user e...
 
Advantages of Rails Framework
Advantages of Rails FrameworkAdvantages of Rails Framework
Advantages of Rails Framework
 
Git Going w/ Git
Git Going w/ GitGit Going w/ Git
Git Going w/ Git
 
Welcome to React.pptx
Welcome to React.pptxWelcome to React.pptx
Welcome to React.pptx
 
Designing your API Server for mobile apps
Designing your API Server for mobile appsDesigning your API Server for mobile apps
Designing your API Server for mobile apps
 
How to Contribute to Apache Usergrid
How to Contribute to Apache UsergridHow to Contribute to Apache Usergrid
How to Contribute to Apache Usergrid
 

Último

Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Victor Rentea
 

Último (20)

Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
Cyberprint. Dark Pink Apt Group [EN].pdf
Cyberprint. Dark Pink Apt Group [EN].pdfCyberprint. Dark Pink Apt Group [EN].pdf
Cyberprint. Dark Pink Apt Group [EN].pdf
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with Milvus
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 

Grails patterns and practices

  • 1. Grails: Patterns & Practices Paul Bowler Senior Consultant, OpenCredo
  • 3. Coding Dojo • Agile! • Teams of 2-4 people • 1 sprint = 20 minutes • Domain-Drive Design (I’m the Product Owner) • Test-Driven Development (Maybe!) • Yes, you can use the user guide and internet! • User demo at the end of each sprint • Discussion + Refactoring • Prize for best app!
  • 5. Domain-Driven Design • “Domain-driven design (DDD) is an approach to developing software for complex needs by deeply connecting the implementation to an evolving model of the core business concepts.” • The premise of domain-driven design is the following: • Placing the project's primary focus on the core domain and domain logic • Basing complex designs on a model • Initiating a creative collaboration between technical and domain experts to iteratively cut ever closer to the conceptual heart of the problem.
  • 6.
  • 7. User Story 1 “As a pomodoro fan, I would like to be able to add tasks to a uniquely named activity inventory, so that I can see what work I need to complete over the next few weeks.”
  • 8. Useful Commands • grails create-app pomodoro • grails create-domain-class <domain> • grails generate-all <domain> • grails generate-controller <domain> and add ‘static scaffold = true’ to controller
  • 9. Considerations • Associations • One-to-One • One-to-Many • Many-to-Many • Constraints &Validation • Time-Stamping? • Default values (and field values inViews?)
  • 10. You did create some tests first, right?
  • 11. Implementation class Task { Inventory inventory String description static belongsTo = [Inventory] static constraints = { description(nullable: false, blank: false) } } class Inventory { String name static hasMany = [tasks: Task] static constraints = { name(nullable: false, blank: false, unique: true) } }
  • 12. Domain Tests class InventoryTests extends GrailsUnitTestCase { void testConstraints() { def existingInventory = new Inventory(name: "Paul’s Inventory") mockForConstraintsTests(Inventory, [ existingInventory ]) ! ! // Validation should fail if both properties are null. ! ! def inventory = new Inventory() ! ! assertFalse inventory.validate() ! ! assertEquals "nullable", inventory.errors["description"] ! ! // So let's demonstrate the unique constraint. ! ! inventory = new Inventory(name: "Paul’s Inventory") ! ! assertFalse inventory.validate() ! ! assertEquals "unique", inventory.errors["name"] ! ! // Validation should pass! ! ! inventory = new Inventory(name: "John’s Inventory") ! ! assertTrue inventory.validate() ! } }
  • 13. Gotcha! • Potential performance issue with mapped collections: • Adding to the Set requires loading all instances from the database to ensure uniqueness • Likewise for mapped List • Works fine in development, but what if you have 1,000,000+ rows?
  • 14. Implementation (2) class Task { Inventory inventory String description static constraints = { description(nullable: false, blank: false) } } class Inventory { String name }
  • 15. Side-effects? • Different syntax for adding Tasks • No cascading deletes • Custom finder required to find all Tasks in an Inventory • Scaffolding breaks!
  • 16. User Story 2 “As a pomodoro fan, I would like to be able move tasks onto a ‘To Do Today’ sheet, so that I can see work to be completed today and view my work history.”
  • 17. Considerations • Does the ‘Today’ list share any common attributes with the Inventory? • How about a more intuitive URL scheme?
  • 19. Level 1 -Views Controller Model PageView PageViewPageView Model Model
  • 20. Level 1 ‘Smells’ • Logic built into pages: • Overuse of Request Parameters • If-Then tags • Inline groovy using ${...} • Poor use of layouts • Little use of tags • Domain classes as simple ‘active records’ • Page-based information architecture
  • 22. Level 1-2 Refactoring • Move logic out of pages into controllers • Reduce pages into fragments • Use layouts to construct device or stakeholder- centric views from pages and fragments • Use available tag libraries • Create your own tag libraries! • Stylesheets rule - minimise markup
  • 23. User Story 3 “As a pomodoro fan, I would like to have an optimised workflow for US2, so that I can save time and reduce input mistakes.”
  • 24. Considerations • Web Flow plugin? • Command Objects? • What changes need to be made to domain classes?
  • 25. Web Flow class InventoryController { … def inventoryFlow = { showInventory { on("done").to "saveInventory" on("continue").to "addTask" } … addTask { redirect(controller:"task", action:"create") } saveInventory() } } <g:form action="inventory"> <g:submitButton name="continue" value="Add Another"></g:submitButton> <g:submitButton name="done" value="I’m Done"></g:submitButton> </g:form>
  • 26. User Story 4 “As a pomodoro fan, I would like to be able update the number of iterations I’ve completed on each task in my ‘To Do Today’ list, so that I can keep track of my progress and improve my future estimates.”
  • 27. Considerations • Can we do this without page refreshes? • How can we test this? • Domain changes?
  • 28. Level 2 - Controllers Controller Domain Controller Controller Domain Domain Domain Layout Fragments Fragments Layout Fragments Fragments Layout Fragments Fragments
  • 29. Level 2 ‘Smells’ • Large, complex Controllers • Different scenarios driven by ‘If/Then’ logic • Content negotiation increases complexity further • Many similar controller methods (not DRY!) • Poorly handled Transactions
  • 30. ? Level 3 - Services ? Services Controllers Views
  • 31. Level 2-3 Refactoring • Move domain transaction logic out of controllers into services • Controllers should be ‘glue’ that binds business services to UI • Service methods should reflect business scenarios • Make use of transactional capability of services
  • 32. User Story 5 “As a pomodoro partner, I would like a simple REST API over your daily task view, so I can integrate your data into my application.”
  • 33. Considerations • Don’t clutter your Controllers! • REST-ful URLs • Content negotiation? • Custom XML/JSON formats?
  • 34. RESTful URL Mappings static mappings = { "/task/$id?"(resource:"task") } static mappings = { "/task/$id"(controller:"task") { action = [GET:"show", PUT:"update", DELETE:"delete", POST:"save"] } } static mappings = { "/task/$id"(controller:"task", parseRequest:true) { action = [GET:"show", PUT:"update", DELETE:"delete", POST:"save"] } }
  • 35. Content Negotiation class InventoryController { def inventory def list = { this.inventory = Inventory.list() withFormat { html inventoryList:inventory json { render inventory as JSON } xml { render inventory as XML } } } }
  • 36. Custom Formats? def listAsXML = { def inventory = Inventory.get(params.id) def tasks = inventory.tasks render(contentType:"text/xml") { inventory(name:inventory.name) { tasks { for(t in tasks) { task(title:t.title) ! } }! } } }
  • 37. User Story 6 “As a pomodoro fan, I’d like to be able to add unplanned and urgent tasks to the bottom of my daily list, so that I can track and manage interruptions.”
  • 38. Level 3 - Services Controller Domain Controller Controller Domain Domain Domain Services Layout Fragments Fragments Layout Fragments Fragments Layout Fragments Fragments
  • 39. Level 3 ‘Smells’ • Large, complex Services • Services acting as proxies for domain behaviour • ‘Cut-and-paste’ methods
  • 40. ? Level 4 - Libraries Libraries Services Controllers Views
  • 41. Level 3-4 Refactoring • Move common code out of services into POGO’s (or POJO’s) • Enrich our domain model to simplify services: • Named Queries • Derived Properties • Criteria: Conjunctions, Disjunctions, Projections, Restrictions
  • 42. User Story 7 “As a pomodoro fan, I would like to be able to search for tasks on my inventory through a simple interface, so I can find and modify them easily.”
  • 43. Level 4 - Libraries Controller Domain Controller Controller Domain Domain Domain Services Libraries Libraries Layout Fragments Fragments Layout Fragments Fragments Layout Fragments Fragments
  • 44. Level 4 ‘Smells’ • Large, monolithic application • Increased cognitive overhead • New starters struggle • Components ‘cut and pasted’ into similar projects
  • 45. Plugins Level 5 - Plugins Libraries Services Controllers Views
  • 46. Level 4-5 Refactoring • Componentise the application into plugins • Construct applications by combining plugins • Could your application itself be constructed as a plugin for an organisation’s product suite? • Writing plugins that modify the Grails/Spring context is beyond the scope of this workshop!
  • 47. User Story 8 “As a pomodoro fan, I would like a simplified version of my Inventory, so I can view it on my iPhone.”
  • 48. Useful Commands • grails create-plugin <plugin> • grails package-plugin • grails install-plugin /path/to/plugin/grails- example-0.1.zip
  • 49. Layouts and Fragments <g:include action="show" id="1" /> <g:include action="show" id="${currentTask.id}" /> <g:include controller="task" /> <g:include controller="task" action="list" /> <g:include action="list" params="[sort:'title', order:'asc'] /> <html> <head> <title><g:layoutTitle default="An example decorator" /></title> <g:layoutHead /> </head> <body> <div class="menu"><!--my common menu goes here--></menu> <div class="body"> <g:layoutBody /> </div> </div> </body> </html>
  • 50. Layout Options • In your views: <meta name="layout" content="main"></meta> • In your controller: static layout = 'task' static layout = 'custom/task' • By Convention: grails-app/views/layouts/task.gsp grails-app/views/layouts/task/list.gsp • Inline: <g:applyLayout name="myLayout" template="taskTemplate" collection="${tasks}" /> <g:applyLayout name="myLayout" url="http://www.google.com" /> <g:applyLayout name="myLayout">The content to apply a layout to</g:applyLayout>
  • 51. Level 5 - Plugins Controller Domain Controller Domain Domain Plugins Page View Page View Controller Services Libraries / Plugins Domain Domain Layout Fragments Fragments Layout Fragments Fragments Services Libraries Libraries