Solid Grails apps with verbose Groovy

Post on 02-Jul-2015

1,582 views 7 download

description

Codemotion 2014 Video de la charla: https://www.youtube.com/watch?v=YwT1ccikv3s Tras 5 años programando con Grails, me he dado cuenta de que lo he estado haciendo mal. Por programar rápido y hacer un código minúsculo, he perdido muchas cosas por el camino que si tenía cuando programaba aplicaciones web con Java. Voy a contar mi experiencia personal de cómo he evolucionado mis desarrollos para que sean fáciles de mantener y de testear. Vuelven los POJOS y el código estático, pero solo para ayudar a poner un poco de oden. Aplicando al extremo la "S" de SOLID y aplicando patrones de diseño, hablaré de una manera (más) de diseñar aplicaciones web con Grails usando Commands, organizando el código de los controllers y clasificando los servicios y sus respuestas con pequeñas clases.

transcript

MADRID · NOV 21-22 · 2014

Solid Grails Apps

with verbose Groovy

Alberto Vilches

Consultant at Virtual Software

@albertovilches

Greach Conference Organizer

http://greachconf.com

MADRID · NOV 21-22 · 2014

Disclaimer

1. I’m not a guru, I’m not trying to teach you

nothing.

2. This is not a talk about something specific,

this is my personal opinion, I can be

wrong.

3. It’s just my way to do the things…. Now.

Tomorrow I can change my mind, again.

MADRID · NOV 21-22 · 2014

About me

16 years developing, last 5

years with Groovy/Grails

Madrid GUG usual suspect

Greachconf organizer

2 kids, Maclover, gamer and skeptical

Now working at Virtual Software

MADRID · NOV 21-22 · 2014

About me

And my code sucks!… and probably yours too.

I can’t be the best

but I can be better than yesterday.

This is a talk about my problems, but I’ll show

you solutions.

MADRID · NOV 21-22 · 2014

IntroductionPART 0/3

MADRID · NOV 21-22 · 2014

I want to tell you something…

MADRID · NOV 21-22 · 2014

I want to tell you about…

The

Grails trap

MADRID · NOV 21-22 · 2014

A trap? Are you sure?

It’s not a trap for everybody

Some people are invulnerable

MADRID · NOV 21-22 · 2014

A trap? Are you sure?

It’s not a trap for everybody

Some people are invulnerable

It depends how much

you have suffered with

j2Ee

MADRID · NOV 21-22 · 2014

Please, kill me

I was lost

in the f*cking J2EE World

Three tiers application servers, EJB, RMI, CORBA, JMS, JDBC direct access,

really slooow source java code generation, JNDI, huge monolitic applications

full of jar, war and ear, fighting against DTOs, VO, Facade and Service

Locators, and of course XML, XML, XML, I told you about XML? Yes XML!

MADRID · NOV 21-22 · 2014

And then…

MADRID · NOV 21-22 · 2014

And then…

MADRID · NOV 21-22 · 2014

And then…

MADRID · NOV 21-22 · 2014

And then…

Groovy and Grails

The same shit but you ride them with a magic

flying unicorn with rocket launcher

0 boilerplate!

Working app from scratch in minutes!

MADRID · NOV 21-22 · 2014

Grails: the trap explained

Really new and young

Not much documentation

Not real complex examples (no petstore!)

MADRID · NOV 21-22 · 2014

Grails: the trap explained

“With great power comes great responsibility”Spiderman movie

“La potencia sin control no sirve de nada”Some old TV ad

“Ten cuidao no corras tanto que te vas a dar”My mother

MADRID · NOV 21-22 · 2014

Grails: the trap explained

Software architecture?

Design patterns?

MADRID · NOV 21-22 · 2014

Grails: the trap explained

Software architecture?

Design patterns?

WHO CARES! WE HAVE

ARTIFACTS(remember? no packages at begining!)

MADRID · NOV 21-22 · 2014

Grails: the trap explained

No boilerplate and CoC becomes the

NEW TRUTH

MADRID · NOV 21-22 · 2014

Grails: the trap explained

No boilerplate and CoC becomes the

NEW TRUTH

the world needs to know

it

MADRID · NOV 21-22 · 2014

“Look, my code is really cool!”

MADRID · NOV 21-22 · 2014

“Look, my code is really cool!”

MADRID · NOV 21-22 · 2014

“Look, my code is really cool!”

(In fact, it really really really sucks)

MADRID · NOV 21-22 · 2014

Grails: the trap explained

no architectural patterns needed, just artifacts

MADRID · NOV 21-22 · 2014

Grails: the trap explained

(no architectural patterns needed, just artifacts

+ really easy data access with gorm)

MADRID · NOV 21-22 · 2014

Grails: the trap explained

(no architectural patterns needed, just artifacts

+ really easy data access with gorm)

x (avoiding the “anemic domain model”

results in more business logic in controllers,

services and domain objects)

MADRID · NOV 21-22 · 2014

Grails: the trap explained

(no architectural patterns needed, just artifacts

+ really easy data access with gorm)

x (avoiding the “anemic domain model”

results in more business logic in controllers,

services and domain objects)

^ (Grails infrastructure everywhere)

MADRID · NOV 21-22 · 2014

Grails: the trap explained

(no architectural patterns needed, just artifacts

+ really easy data access with gorm)

x (avoiding the “anemic domain model”

results in more business logic in controllers,

services and domain objects)

^ (Grails infrastructure everywhere)

===================================

Shit, now i’m lost with grails

too

MADRID · NOV 21-22 · 2014

My new mess (Grails version)

MADRID · NOV 21-22 · 2014

My new mess (Grails version)

MADRID · NOV 21-22 · 2014

PART 1/3The theoric and long part,

with patterns and other stuff.

MADRID · NOV 21-22 · 2014

How I’m solving it

PROBLEM #1

No control about where the business logic lives

Grails hides Spring and Hibernate, offering to

you new infrastructure you spread in artifacts

MADRID · NOV 21-22 · 2014

How I’m solving it

PROBLEM #1

No control about where the business logic lives

Create a taxonomy and rules

Grails hides Spring and Hibernate, offering to

you new infrastructure you spread in artifacts

Isolate it

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

My house, my rules:

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

My house, my rules:

1) Only use services

2) Return rich responses (and use enums!)

3) Too much services? create your own

taxonomy

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

1) Only use services

2) Return rich responses (use enums!)

Controllers just call services methods.

To avoid logic in controllers, services have to

return rich object responses.

Controllers and views only have to check

responses status calling short methods with

meaningful names (clean code!).

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

The good parts:

You can add more info into response

classes without change all the app.

Easy to test and mocks.

Meaningful actions.

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

The good parts:

You can add more info into response

classes without change all the app.

Easy to test and mocks.

Meaningful actions.

The bad parts:

Verbose.

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

The good parts:

You can add more info into response

classes without change all the app.

Easy to test and mocks.

Meaningful actions.

The bad parts:

Verbose.

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

What about to put logic in domain classes?

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

What about to put logic in domain classes?

IT’S

A

TRAP!

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

What about to put logic in domain classes?

“Create methods

in your domain classes,

but delegate calls to

service methods.”

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

3) Too much services? create your own

taxonomy

My house, my rules:

“Repository” services

“External” services

“Business” services

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

“Repository” services

One responsability: access data

Gorm/HQL/JDBC, grailsApplication.config,

fileSystem

No transactional!

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

“Repository” services, the good parts:

Isolate Grails infrastructure.

Easy to refactor: better rename (only one

place to modify) or change data source.

Easy to mantain: only one place to look.

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

“Repository” services, the good parts:

Isolate Grails infrastructure.

Easy to refactor: better rename (only one

place to modify) or change data source.

Easy to mantain: only one place to look.

The bad parts:

Verbose, specially the wraping service of

grailsApplication.config

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

“Repository” services, the good parts:

Isolate Grails infrastructure.

Easy to refactor: better rename (only one

place to modify) or change data source.

Easy to mantain: only one place to look.

The bad parts:

Verbose, specially the wraping service of

grailsApplication.config

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

“External” services

One responsability

access to remote servers

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

“External” services

One responsability

access to remote servers

Web Services (rest/soap), apis, mail servers, …

FTP, SNMP, JMX, XMPP, …

Amazon S3, Facebook, Google, Twitter, …

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

“External” services, the good parts

Vertical design.

Each integration has one implementation.

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

“External” services, the good parts

Vertical design.

Each integration has one implementation.

The bad parts

Too simple: keep in mind some integrations

could need design patterns to avoid huge

service classes full of spaghetti code.

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

External services, good parts

Vertical design.

Each integration has one implementation.

The bad parts

Too simple: keep in mind some integrations

could need design patterns to avoid huge

service classes full of spaghetti code.

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

“Business” services

One responsability: your business logic

Can be transactional

Orchest all other services

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

“Business” services, the good parts

Isolate business logic without infrastructure.

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

“Business” services, the good parts

Isolate business logic without infrastructure.

The bad parts

Verbose: you always have to pass through

them.

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

“Business” services, the good parts

Isolate business logic without infrastructure.

The bad parts

Verbose: you always have to pass through

them.

Hey, you can break your own rules and call

directly to repositoy services.

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

“Business” services, the good parts

Isolate business logic without infrastructure.

The bad parts

Verbose: you always have to pass through

them.

Hey, you can break your own rules and call

directly to repositoy services.

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

My rules

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

My rules

Controllers call to business services only

Business services call to any kind of service

Repositories can call other repositories only

External services can’t call other services

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

Create a taxonomy with your own rules

MADRID · NOV 21-22 · 2014

#1 No control about where the

business logic lives

Create a taxonomy with your own rules

But ensure all the

team respect them

MADRID · NOV 21-22 · 2014

PART 2/3About the views.

Sweet and short!

MADRID · NOV 21-22 · 2014

How I’m solving it

PROBLEM #2:

Controllers have too much duplicated and

dynamic code with map arguments (buggy!)

MADRID · NOV 21-22 · 2014

How I’m solving it

PROBLEM #2:

Controllers have too much duplicated and

dynamic code with map arguments (buggy!)

Clean code: hide infrastructure in single

methods (or classes)

MADRID · NOV 21-22 · 2014

#2 Controllers have too much

duplicated and dynamic code

MADRID · NOV 21-22 · 2014

#2 Controllers have too much

duplicated and dynamic code

MADRID · NOV 21-22 · 2014

#2 Controllers have too much

duplicated and dynamic code

MADRID · NOV 21-22 · 2014

#2 Controllers have too much

duplicated and dynamic code

MADRID · NOV 21-22 · 2014

#2 Controllers have too much

duplicated and dynamic code

The good parts

Meaningful actions, easy reading, reusable

Easy to rename model values or view names

Better testingYou don’t have to assert model or view, just assert method calls

Static code, if you like

MADRID · NOV 21-22 · 2014

#2 Controllers have too much

duplicated and dynamic code

The good parts

Meaningful actions, easy reading, reusable

Easy to rename model values or view names

Better testingYou don’t have to assert model or view, just assert method calls

Static code, if you like

The bad parts

Less flexible than using dynamic methods.

MADRID · NOV 21-22 · 2014

PART 3/3Final part. A lot of

technical knowledge about data binding.

BONUS TRACK: 10 TRICKS!

MADRID · NOV 21-22 · 2014

PART 3/3Final part. A lot of

technical knowledge about data binding.

Shut up and keep your eyes open!

MADRID · NOV 21-22 · 2014

How I’m solving it

PROBLEM #3:

What about the input?

Divide input data in minimal commands

Use new Grails 2.3 binding events

And let me show you some tricks :-)

MADRID · NOV 21-22 · 2014

#3 How Commands works

A new command instance is created, then:

1 Data is bound from request (json/params),

before and after events are called.

2 Dependency Injection.

3 command.validate() is called.

Finally, your action is executed.

MADRID · NOV 21-22 · 2014

#3 How Commands works

ControllersApi.class

(org.codehaus.groovy.grails.plugins.web.api)

initializeCommandObject method.

DataBindingUtils.class

(org.codehaus.groovy.grails.web.binding)

ValidationSupport.class

(org.codehaus.groovy.grails.web.plugins.support)

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #0

Never ever use domain classes as

commands in your actions.

It’s a really bad practice, you are exposing your

datamodel to the world!

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #1

You musn’t validate commands in your

actions.

Grails did that for you already!

Maybe database access will be executed twice.

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #2

Yes, you can use more than one command

in your actions.

All of them will be bound using the same data

input.

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #3

Pay attention when using new data binding

annotations.

The annotations are bound to the command

class at compile time forever. And maybe you

want a different behavior.

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #3

Pay attention when using new data binding

annotations.

Are you sure to use that? What happens if you want to use the

command for importing data from a file using other format date?

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #4 The @BindUsing is tricky!

Use the same field name!

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #4 The @BindUsing is tricky

How it works? QUIZZ TIME!

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #4 The @BindUsing is tricky

email emailOther value bound

crew@greachconf.com empty ?

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #4 The @BindUsing is tricky

email emailOther value bound

crew@greachconf.com empty empty!

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #4 The @BindUsing is tricky

email emailOther value bound

crew@greachconf.com empty empty!

empty VILCHES@GMAIL.COM ?

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #4 The @BindUsing is tricky

email emailOther value bound

crew@greachconf.com empty empty!

empty VILCHES@GMAIL.COM empty too!

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #4 The @BindUsing is tricky

email emailOther value bound

crew@greachconf.com empty empty!

empty VILCHES@GMAIL.COM empty too!

crew@greachconf.com VILCHES@GMAIL.COM ?

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #4 The @BindUsing is tricky

email emailOther value bound

crew@greachconf.com empty empty!

empty VILCHES@GMAIL.COM empty too!

crew@greachconf.com VILCHES@GMAIL.COM vilches@gmail.com

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #4 The @BindUsing is tricky

So, be careful…

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #5 Default value converters are tricky

Locale.default is used if no request bound to

current thread.

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #5 Default value converters are tricky

You have to create another implementation if

you want to use commands without request.

(i.e.: importing from file in a Quartz job)

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #6

Use the binding events

Especially, afterBinding event allows you to do

some ad-hoc binding and validations.

Best part: these events are executed before

validate.

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #7

Delegate binding events to the commands.

Instead to create a listener for every command,

create a generic listener and delegate the event

call to your command.

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #7

Delegate binding events to the commands.

It’s just an idea, you can do it better :-)

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #8

Don’t let Grails to load entities for you.

Only you can access to database in your

business logic, not Grails!

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #8

Don’t let Grails to load entities for you.

controller/action?productType.id=25

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #8

Don’t let Grails to load entities for you.

controller/action?productType.id=25

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #8

Don’t let Grails to load entities for you.

Doing that, you have to create a constraint validator to

ensure the ownership of the entity.

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #8

Don’t let Grails to load entities for you.

I don’t like because it needs up to 4 db selects to

get the info.

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #8

Don’t let Grails to load entities for you.

Alternative way to do it using afterBinding event. With

only 1 select against db!

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #9

You can bind whatever you want!

Just call to

DataBindingUtils.bindObjectToInstance()

It will call to all of your events too!

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #9

You can bind whatever you want!

All my commands inherits from this class (kind of…)

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #10 Use commands as input in your

services

Ensure they don’t

have errors!

MADRID · NOV 21-22 · 2014

THE END?You wanna more tricks?

2 MORE TRICKS!

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #11 Dependency injection starts

AFTER all the events (even the

beforeBinding)

So, if you need services or any other artifact

injected in your commands, just use:

Holders.applicationContext.autowireCapableBeanFactory.

autowireBeanProperties(command,

AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, false)

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #12 Create your own annotations!

Just create a listener and read the command

annotation in the beforeBinding or afterBinding

event.

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #12 Create your own annotations!

Annotation example. It just take a Closure as

the only argument.

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #12 Create your own annotations!

How to read the annotation and call the

command from a binding listener:

MADRID · NOV 21-22 · 2014

#3 How Commands works

TRICK #12 Create your own annotations!

Using the annotation from a command. Real

example!

MADRID · NOV 21-22 · 2014

No more tricks, no more rules!

New Grails 2.3 Data Binding is very powerful!

Use at your own risk and read documentation!

Divide input data in minimal commands.

Combine with previous patterns.

Create your own rules and respect it.

MADRID · NOV 21-22 · 2014

Questions? Thank you!

@albertovilches

http://albertovilches.com

@greachconf

http://greachconf.com

10-11 April

2015