grails patterns and practices

53
Grails: Patterns & Practices Paul Bowler Senior Consultant, OpenCredo

Post on 18-Oct-2014

369 views

Category:

Technology


4 download

DESCRIPTION

Slides from Grails coding Dodo (OpenCredo 2011)

TRANSCRIPT

Page 1: Grails patterns and practices

Grails: Patterns & Practices

Paul BowlerSenior Consultant, OpenCredo

Page 2: Grails patterns and practices

Who are you?

Page 3: Grails patterns and practices

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!

Page 4: Grails patterns and practices

?

5 ‘Maturity’ Levels

?

?

?

?

Page 5: Grails patterns and practices

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.

Page 6: Grails patterns and practices
Page 7: Grails patterns and practices

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.”

Page 8: Grails patterns and practices

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

Page 9: Grails patterns and practices

Considerations

• Associations

• One-to-One

• One-to-Many

• Many-to-Many

• Constraints & Validation

• Time-Stamping?

• Default values (and field values in Views?)

Page 10: Grails patterns and practices

You did create some tests first, right?

Page 11: Grails patterns and practices

Implementation

class Task {Inventory inventoryString description

static belongsTo = [Inventory]

static constraints = {description(nullable: false, blank: false)

}}

class Inventory {String namestatic hasMany = [tasks: Task]

static constraints = {name(nullable: false, blank: false, unique: true)

}}

Page 12: Grails patterns and practices

Domain Testsclass 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() ! } }

Page 13: Grails patterns and practices

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?

Page 14: Grails patterns and practices

Implementation (2)

class Task {Inventory inventoryString description

static constraints = {description(nullable: false, blank: false)

}}

class Inventory {String name

}

Page 15: Grails patterns and practices

Side-effects?

• Different syntax for adding Tasks

• No cascading deletes

• Custom finder required to find all Tasks in an Inventory

• Scaffolding breaks!

Page 16: Grails patterns and practices

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.”

Page 17: Grails patterns and practices

Considerations

• Does the ‘Today’ list share any common attributes with the Inventory?

• How about a more intuitive URL scheme?

Page 18: Grails patterns and practices

?

Level 1

?

?

?

Views

Page 19: Grails patterns and practices

Level 1 - Views

Controller

Model

Page View Page ViewPage View

Model Model

Page 20: Grails patterns and practices

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

Page 21: Grails patterns and practices

?

Level 2

?

?

Controllers

Views

Page 22: Grails patterns and practices

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

Page 23: Grails patterns and practices

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.”

Page 24: Grails patterns and practices

Considerations

• Web Flow plugin?

• Command Objects?

• What changes need to be made to domain classes?

Page 25: Grails patterns and practices

Web Flowclass 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>

Page 26: Grails patterns and practices

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.”

Page 27: Grails patterns and practices

Considerations

• Can we do this without page refreshes?

• How can we test this?

• Domain changes?

Page 28: Grails patterns and practices

Level 2 - Controllers

Controller

Domain

Controller Controller

Domain Domain Domain

Layout

Fragments Fragments

Layout

Fragments Fragments

Layout

Fragments Fragments

Page 29: Grails patterns and practices

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

Page 30: Grails patterns and practices

?

Level 3 - Services

?

Services

Controllers

Views

Page 31: Grails patterns and practices

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

Page 32: Grails patterns and practices

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.”

Page 33: Grails patterns and practices

Considerations

• Don’t clutter your Controllers!

• REST-ful URLs

• Content negotiation?

• Custom XML/JSON formats?

Page 34: Grails patterns and practices

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"]}

}

Page 35: Grails patterns and practices

Content Negotiationclass InventoryController { def inventory def list = { this.inventory = Inventory.list() withFormat { html inventoryList:inventory json { render inventory as JSON } xml { render inventory as XML } } }}

Page 36: Grails patterns and practices

Custom Formats?def listAsXML = {def inventory = Inventory.get(params.id)def tasks = inventory.tasksrender(contentType:"text/xml") {inventory(name:inventory.name) {tasks {for(t in tasks) {task(title:t.title)

! }}!

}}

}

Page 37: Grails patterns and practices

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.”

Page 38: Grails patterns and practices

Level 3 - Services

Controller

Domain

Controller Controller

Domain Domain Domain

Services

Layout

Fragments Fragments

Layout

Fragments Fragments

Layout

Fragments Fragments

Page 39: Grails patterns and practices

Level 3 ‘Smells’

• Large, complex Services

• Services acting as proxies for domain behaviour

• ‘Cut-and-paste’ methods

Page 40: Grails patterns and practices

?

Level 4 - Libraries

Libraries

Services

Controllers

Views

Page 41: Grails patterns and practices

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

Page 42: Grails patterns and practices

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.”

Page 43: Grails patterns and practices

Level 4 - Libraries

Controller

Domain

Controller Controller

Domain Domain Domain

Services

Libraries Libraries

Layout

Fragments Fragments

Layout

Fragments Fragments

Layout

Fragments Fragments

Page 44: Grails patterns and practices

Level 4 ‘Smells’

• Large, monolithic application

• Increased cognitive overhead

• New starters struggle

• Components ‘cut and pasted’ into similar projects

Page 45: Grails patterns and practices

Plugins

Level 5 - Plugins

Libraries

Services

Controllers

Views

Page 46: Grails patterns and practices

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!

Page 47: Grails patterns and practices

User Story 8“As a pomodoro fan, I would like a simplified version of

my Inventory, so I can view it on my iPhone.”

Page 48: Grails patterns and practices

Useful Commands

• grails create-plugin <plugin>

• grails package-plugin

• grails install-plugin /path/to/plugin/grails-example-0.1.zip

Page 49: Grails patterns and practices

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>

Page 50: Grails patterns and practices

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.gspgrails-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>

Page 51: Grails patterns and practices

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

Page 52: Grails patterns and practices

Plugins

Libraries

Services

Controllers

The Full Picture

Views

Page 53: Grails patterns and practices

Phew!Well Done.