grails patterns and practices

Post on 18-Oct-2014

370 Views

Category:

Technology

4 Downloads

Preview:

Click to see full reader

DESCRIPTION

Slides from Grails coding Dodo (OpenCredo 2011)

TRANSCRIPT

Grails: Patterns & Practices

Paul BowlerSenior 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 in Views?)

You did create some tests first, right?

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)

}}

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

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 inventoryString 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

Page View Page ViewPage View

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

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 Negotiationclass 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.tasksrender(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.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>

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.

top related