Your SlideShare is downloading. ×
0
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Grails patterns and practices
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Grails patterns and practices

403

Published on

Slides from Grails coding Dodo (OpenCredo 2011)

Slides from Grails coding Dodo (OpenCredo 2011)

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

  • Be the first to like this

No Downloads
Views
Total Views
403
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
17
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Grails: Patterns & Practices Paul Bowler Senior Consultant, OpenCredo
  • 2. Who are you?
  • 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!
  • 4. ? 5 ‘Maturity’ Levels ? ? ? ?
  • 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. 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.”
  • 7. 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
  • 8. Considerations • Associations • One-to-One • One-to-Many • Many-to-Many • Constraints &Validation • Time-Stamping? • Default values (and field values inViews?)
  • 9. You did create some tests first, right?
  • 10. 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) } }
  • 11. 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() ! } }
  • 12. 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?
  • 13. Implementation (2) class Task { Inventory inventory String description static constraints = { description(nullable: false, blank: false) } } class Inventory { String name }
  • 14. Side-effects? • Different syntax for adding Tasks • No cascading deletes • Custom finder required to find all Tasks in an Inventory • Scaffolding breaks!
  • 15. 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.”
  • 16. Considerations • Does the ‘Today’ list share any common attributes with the Inventory? • How about a more intuitive URL scheme?
  • 17. ? Level 1 ? ? ? Views
  • 18. Level 1 -Views Controller Model PageView PageViewPageView Model Model
  • 19. 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
  • 20. ? Level 2 ? ? Controllers Views
  • 21. 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
  • 22. 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.”
  • 23. Considerations • Web Flow plugin? • Command Objects? • What changes need to be made to domain classes?
  • 24. 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>
  • 25. 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.”
  • 26. Considerations • Can we do this without page refreshes? • How can we test this? • Domain changes?
  • 27. Level 2 - Controllers Controller Domain Controller Controller Domain Domain Domain Layout Fragments Fragments Layout Fragments Fragments Layout Fragments Fragments
  • 28. 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
  • 29. ? Level 3 - Services ? Services Controllers Views
  • 30. 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
  • 31. 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.”
  • 32. Considerations • Don’t clutter your Controllers! • REST-ful URLs • Content negotiation? • Custom XML/JSON formats?
  • 33. 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"] } }
  • 34. 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 } } } }
  • 35. 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) ! } }! } } }
  • 36. 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.”
  • 37. Level 3 - Services Controller Domain Controller Controller Domain Domain Domain Services Layout Fragments Fragments Layout Fragments Fragments Layout Fragments Fragments
  • 38. Level 3 ‘Smells’ • Large, complex Services • Services acting as proxies for domain behaviour • ‘Cut-and-paste’ methods
  • 39. ? Level 4 - Libraries Libraries Services Controllers Views
  • 40. 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
  • 41. 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.”
  • 42. Level 4 - Libraries Controller Domain Controller Controller Domain Domain Domain Services Libraries Libraries Layout Fragments Fragments Layout Fragments Fragments Layout Fragments Fragments
  • 43. Level 4 ‘Smells’ • Large, monolithic application • Increased cognitive overhead • New starters struggle • Components ‘cut and pasted’ into similar projects
  • 44. Plugins Level 5 - Plugins Libraries Services Controllers Views
  • 45. 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!
  • 46. User Story 8 “As a pomodoro fan, I would like a simplified version of my Inventory, so I can view it on my iPhone.”
  • 47. Useful Commands • grails create-plugin <plugin> • grails package-plugin • grails install-plugin /path/to/plugin/grails- example-0.1.zip
  • 48. 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>
  • 49. 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>
  • 50. 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
  • 51. Plugins Libraries Services Controllers The Full Picture Views
  • 52. Phew! Well Done.

×