Quantcast

controller pattern best practice

classic Classic list List threaded Threaded
11 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

controller pattern best practice

skurt
Hi,
 I just came around to blog post showing a way to build up the
controller (one with service in background). As I tested these
solutions a bit I was wondering if there are more ways to do this? Can
someone state if these are good solutions or where the pros and cons
are?
a) http://www.kromhouts.net/blog/grails/grails-service-and-controller-interface/
b) http://mrpaulwoods.wordpress.com/2011/01/23/a-pattern-to-simplify-grails-controllers/

in b) I tried to use this withDomainClassName but I get some errors.
Is this still working in grails 1.3.7?

Thanks for discussion
best Sebastian
--
Geschäftsführer (CEO)

Haiku Internet GmbH
Schlegelstraße 26
10115 Berlin

Tel.: +49 30 868702020
Fax: +49 30 57708224
Mobil: +49 179 7923088
E-Mail: [hidden email]
http://www.thechicken.com
http://www.facebook.com/pages/The-Chicken/162425640445977
http://twitter.com/the_chicken_com

Handelsregister: HRB 126354 B
Amtsgericht Berlin-Charlottenburg
Geschäftsführung:
Adrian Haß, Sebastian Kurt

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: controller pattern best practice

drosowsk
Hi Sebastian,

IMHO designing the controller and service layer is a bit of a preference question. For me, the first blog post is describing an anti pattern, overusing the service layer for trivial data fetching logic and thereby bloating the service layer (not to speak of the loss of object orientation). True, you should not leave too much business logic specific code in the controller, but why not put it in the domain class? I think its best kept there, since it belongs to a specific domain (in most cases). It is a (mainly jee) myth, that you always need a service layer for ALL kinds of business logic. Evans' "Domain driven design" shed some light on this topic for me. Also a good read is Fowlers anemic domain model anti pattern: http://martinfowler.com/bliki/AnemicDomainModel.html.

I really like the second blog post. I think I will try this in my next project :-)

Ok, leaving theoretical fuss behind, the question on how to move some of the controller logic into domain classes is still unanswered. Josh (basejump) was getting tired of having too much repetition in his service layer and came up with a great plugin (using a well known pattern, the data access object): https://github.com/basejump/grails-dao. Why not give this a try...
I did a little plugin for dealing with concurrent updates: https://github.com/drosowski/grails-concurrent-update.

I ususally keep all kinds of trivial data fetching logic inside the domain classes (perhaps as static methods) and I have a service layer for all data manipulation concerns. The controller is just for gathering data for the views and navigation purposes.

Cheers,
Daniel
Agrenon GmbH
www.agrenon.com
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: controller pattern best practice

Uday Pratap Singh
The second blog is really nice thanks for sharing. The first one which put all the params into the service is really bad code because your methods are not really well defined. Its always better to use Command Objects for any kind of data-binding whether its domain related or not. Data binding is not just limited to domain classes you can use it binding the params for example the I always use to do the pagination by creating the Command object.

Grails itself has very nice support of command object and data-binding we just need to leverage this feature, your controller will automatically become the dump routers :)

On Thu, Aug 4, 2011 at 3:00 PM, drosowsk <[hidden email]> wrote:
Hi Sebastian,

IMHO designing the controller and service layer is a bit of a preference
question. For me, the first blog post is describing an anti pattern,
overusing the service layer for trivial data fetching logic and thereby
bloating the service layer (not to speak of the loss of object orientation).
True, you should not leave too much business logic specific code in the
controller, but why not put it in the domain class? I think its best kept
there, since it belongs to a specific domain (in most cases). It is a
(mainly jee) myth, that you always need a service layer for ALL kinds of
business logic. Evans' "Domain driven design" shed some light on this topic
for me. Also a good read is Fowlers anemic domain model anti pattern:
http://martinfowler.com/bliki/AnemicDomainModel.html.

I really like the second blog post. I think I will try this in my next
project :-)

Ok, leaving theoretical fuss behind, the question on how to move some of the
controller logic into domain classes is still unanswered. Josh (basejump)
was getting tired of having too much repetition in his service layer and
came up with a great plugin (using a well known pattern, the data access
object): https://github.com/basejump/grails-dao. Why not give this a try...
I did a little plugin for dealing with concurrent updates:
https://github.com/drosowski/grails-concurrent-update.

I ususally keep all kinds of trivial data fetching logic inside the domain
classes (perhaps as static methods) and I have a service layer for all data
manipulation concerns. The controller is just for gathering data for the
views and navigation purposes.

Cheers,
Daniel

-----
Agrenon GmbH
www.agrenon.com
--
View this message in context: http://grails.1312388.n4.nabble.com/controller-pattern-best-practice-tp3717900p3718112.html
Sent from the Grails - user mailing list archive at Nabble.com.

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

   http://xircles.codehaus.org/manage_email





--
Regards
##Uday Pratap Singh##
Intelligrape software (P)Ltd.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: controller pattern best practice

skurt
Hi,
  thanks for discussing. I read your lines, write a first answer [1],
thought about it, find it was "wrong" and now I am convinced there is a
way working with Command Objects does make sense. :) But I am not sure
what the pattern is. What qualifies a constraint to stay in Command
Object and not in Domain Class?

thanks
Sebastian

[1]
Command Objects are nice, but they do have some IMHO cons:
* you have to declare constraints already given in the domain again
* if the new/updated object does not validate with command objects it is
more difficult to display what is wrong.

lets compare two examples [A] and [B] in a controller. With [A] if there
is an error in the given data, it will render the form again and mark
the property that does not validate. [B] must have

[A]
def update = {
  Foo foo = Foo.get(params.id)
  bindData(foo, params)
  if (!foo.save()) {
    render "/foo/edit", model:[foo:foo]
  }
  ...
}

[B]
def update = { FooCO fooCO ->
  Foo foo = Foo.get(params.id)
  bindData(foo, fooCO)
  if (!foo.save()) {
    render "/foo/edit", model:[foo:foo]
  }
  ...
}


Am 04.08.2011 19:35, schrieb Uday Pratap Singh:

> The second blog is really nice thanks for sharing. The first one which
> put all the params into the service is really bad code because your
> methods are not really well defined. Its always better to use Command
> Objects for any kind of data-binding whether its domain related or not.
> Data binding is not just limited to domain classes you can use it
> binding the params for example the I always use to do the pagination by
> creating the Command object.
>
> Grails itself has very nice support of command object and data-binding
> we just need to leverage this feature, your controller will
> automatically become the dump routers :)
>
> On Thu, Aug 4, 2011 at 3:00 PM, drosowsk <[hidden email]
> <mailto:[hidden email]>> wrote:
>
>     Hi Sebastian,
>
>     IMHO designing the controller and service layer is a bit of a preference
>     question. For me, the first blog post is describing an anti pattern,
>     overusing the service layer for trivial data fetching logic and thereby
>     bloating the service layer (not to speak of the loss of object
>     orientation).
>     True, you should not leave too much business logic specific code in the
>     controller, but why not put it in the domain class? I think its best
>     kept
>     there, since it belongs to a specific domain (in most cases). It is a
>     (mainly jee) myth, that you always need a service layer for ALL kinds of
>     business logic. Evans' "Domain driven design" shed some light on
>     this topic
>     for me. Also a good read is Fowlers anemic domain model anti pattern:
>     http://martinfowler.com/bliki/AnemicDomainModel.html.
>
>     I really like the second blog post. I think I will try this in my next
>     project :-)
>
>     Ok, leaving theoretical fuss behind, the question on how to move
>     some of the
>     controller logic into domain classes is still unanswered. Josh
>     (basejump)
>     was getting tired of having too much repetition in his service layer and
>     came up with a great plugin (using a well known pattern, the data access
>     object): https://github.com/basejump/grails-dao. Why not give this a
>     try...
>     I did a little plugin for dealing with concurrent updates:
>     https://github.com/drosowski/grails-concurrent-update.
>
>     I ususally keep all kinds of trivial data fetching logic inside the
>     domain
>     classes (perhaps as static methods) and I have a service layer for
>     all data
>     manipulation concerns. The controller is just for gathering data for the
>     views and navigation purposes.
>
>     Cheers,
>     Daniel
>
>     -----
>     Agrenon GmbH
>     www.agrenon.com <http://www.agrenon.com>
>     --
>     View this message in context:
>     http://grails.1312388.n4.nabble.com/controller-pattern-best-practice-tp3717900p3718112.html
>     Sent from the Grails - user mailing list archive at Nabble.com.
>
>     ---------------------------------------------------------------------
>     To unsubscribe from this list, please visit:
>
>     http://xircles.codehaus.org/manage_email
>
>
>
>
>
> --
> Regards
> ##Uday Pratap Singh##
> Intelligrape software (P)Ltd.

--
Geschäftsführer (CEO)

Shopotainment by
Haiku Internet GmbH
Schlegelstraße 26
10115 Berlin

Tel.: +49 30 868702020
Fax: +49 30 57708224
Mobil: +49 179 7923088
E-Mail: [hidden email]
http://www.shopotainment.de
http://facebook.com/shopotainment
http://twitter.com/shopotainment

Handelsregister: HRB 126354 B
Amtsgericht Berlin-Charlottenburg
Geschäftsführung:
Adrian Haß, Sebastian Kurt

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: controller pattern best practice

drosowsk
Hi,

skurt wrote
Hi,
  thanks for discussing. I read your lines, write a first answer [1],
thought about it, find it was "wrong" and now I am convinced there is a
way working with Command Objects does make sense. :) But I am not sure
what the pattern is. What qualifies a constraint to stay in Command
Object and not in Domain Class?

thanks
Sebastian
Well, being two different concepts (CO not persisted, domain classes persisted) you should first make up your mind about that. If you need the data persisted, use a domain class (and naturally all constraints stay in this domain class). If you need an object solely for transfering and validating user input but don't need the user input persisted (e.g. a searchrequest), use a CO. In this case all the validation logic stays in the CO.

Cheers,
Daniel
Agrenon GmbH
www.agrenon.com
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: controller pattern best practice

John Fletcher-3
http://mrpaulwoods.wordpress.com/2011/01/23/a-pattern-to-simplify-grails-controllers/ creates one of those "why didn't I think of that?" moments. Definitely should be abstracted into a plugin if not core.
 
John
2011/8/9 drosowsk <[hidden email]>
Hi,


skurt wrote:
>
> Hi,
>   thanks for discussing. I read your lines, write a first answer [1],
> thought about it, find it was "wrong" and now I am convinced there is a
> way working with Command Objects does make sense. :) But I am not sure
> what the pattern is. What qualifies a constraint to stay in Command
> Object and not in Domain Class?
>
> thanks
> Sebastian
>

Well, being two different concepts (CO not persisted, domain classes
persisted) you should first make up your mind about that. If you need the
data persisted, use a domain class (and naturally all constraints stay in
this domain class). If you need an object solely for transfering and
validating user input but don't need the user input persisted (e.g. a
searchrequest), use a CO. In this case all the validation logic stays in the
CO.

Cheers,
Daniel

-----
Agrenon GmbH
www.agrenon.com
--
View this message in context: http://grails.1312388.n4.nabble.com/controller-pattern-best-practice-tp3717900p3729339.html
Sent from the Grails - user mailing list archive at Nabble.com.

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

   http://xircles.codehaus.org/manage_email



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: controller pattern best practice

virtualeyes
Absolutely, excellent thread, certainly cleans up the crud so to speak, and dovetails nicely with REST.

I broke out into an abstract base class (in 2.0 no need for controller base class to be a controller itself), so given this src/groovy class:

abstract class Crud {

        Class domain
        Integer   entityID // usually sequence ID, can def if you use assigned string keys
        String     pkey // id, orderID, etc.

        abstract def setup()
        Crud() {
                setup()
        }

        def self(Closure c) {
                def entity = domain.get(entityID)
                if(!entity) {
                        i18n this.pkey, 'not found', true
                        withFormat {
                                html { redirect action:"list" }
                                json { response.status = 404 }
                                xml  { response.status = 404 }
                        }
                }
                else {
                        entity.properties = params
                        return c.call(entity)
                }
        }

        def withRest(LinkedHashMap<String, Object> entityMap) {
                entityMap?.each{ name, entity->
                        withFormat {
                                html { return ["$name":entity] }
                                json { render entity as JSON }
                                xml  { render entity as XML }
                        }
                }
        }
}

You can do this in your crud enabled controllers with 2 lines (extends and setup()):

class NodeController extends Crud {
       
        def setup() { domain = Node } // or whatever domain you'd like to use

        def show() {
                self({ Node entity-> withRest node:entity }) // parens omitted if you prefer
        }

       def create() {
                withRest node:new Node(params)
        }

        def update() {
                self({ Node entity->
                        if(!entity.hasErrors() && entity.save(flush:true)) {
                                withFormat {
                                        i18n pkey, 'updated'
                                        html { redirect action:"show", id:entity."${pkey}" }
                                        json { response.status = 200; render entity as JSON }
                                        xml  { response.status = 200; render entity as XML }
                                }
                        }
                        else {
                                withFormat {
                                        i18n pkey, 'not updated'
                                        html { render view:"edit", model:[node: entity] }
                                        json { response.status = 409; render entity.errors as JSON }
                                        xml  { response.status = 409; render entity.errors as XML }
                                }
                        }
                })
        }
}
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: controller pattern best practice

skurt
Wow! this looks as "conventional" as grails is coding by convention.
It probably saves a lot of unneccessary we write it again and agian
lines of code. If grails would be developed via github you should
commit this and add a pull request!
thanks
Sebastian

2011/8/19 virtualeyes <[hidden email]>:

> Absolutely, excellent thread, certainly cleans up the crud so to speak, and
> dovetails nicely with REST.
>
> I broke out into an abstract base class (in 2.0 no need for controller base
> class to be a controller itself), so given this src/groovy class:
>
> abstract class Crud {
>
>        Class   domain
>        Integer   entityID // usually sequence ID, can def if you use
> assigned string keys
>        String     pkey // id, orderID, etc.
>
>        abstract def setup()
>        Crud() {
>                setup()
>        }
>
>        def self(Closure c) {
>                def entity = domain.get(entityID)
>                if(!entity) {
>                        i18n this.pkey, 'not found', true
>                        withFormat {
>                                html { redirect action:"list" }
>                                json { response.status = 404 }
>                                xml  { response.status = 404 }
>                        }
>                }
>                else {
>                        entity.properties = params
>                        return c.call(entity)
>                }
>        }
>
>        def withRest(LinkedHashMap<String, Object> entityMap) {
>                entityMap?.each{ name, entity->
>                        withFormat {
>                                html { return ["$name":entity] }
>                                json { render entity as JSON }
>                                xml  { render entity as XML }
>                        }
>                }
>        }
> }
>
> You can do this in your crud enabled controllers with 2 lines (extends and
> setup()):
>
> class NodeController extends Crud {
>
>        def setup() { domain = Node } // or whatever domain you'd like to use
>
>        def show() {
>                self({ Node entity-> withRest node:entity }) // parens omitted if you
> prefer
>        }
>
>       def create() {
>                withRest node:new Node(params)
>        }
>
>        def update() {
>                self({ Node entity->
>                        if(!entity.hasErrors() && entity.save(flush:true)) {
>                                withFormat {
>                                        i18n pkey, 'updated'
>                                        html { redirect action:"show", id:entity."${pkey}" }
>                                        json { response.status = 200; render entity as JSON }
>                                        xml  { response.status = 200; render entity as XML }
>                                }
>                        }
>                        else {
>                                withFormat {
>                                        i18n pkey, 'not updated'
>                                        html { render view:"edit", model:[node: entity] }
>                                        json { response.status = 409; render entity.errors as JSON }
>                                        xml  { response.status = 409; render entity.errors as XML }
>                                }
>                        }
>                })
>        }
> }
>
> --
> View this message in context: http://grails.1312388.n4.nabble.com/controller-pattern-best-practice-tp3717900p3754977.html
> Sent from the Grails - user mailing list archive at Nabble.com.
>
> ---------------------------------------------------------------------
> To unsubscribe from this list, please visit:
>
>    http://xircles.codehaus.org/manage_email
>
>
>



--
Geschäftsführer (CEO)

Haiku Internet GmbH
Schlegelstraße 26
10115 Berlin

Tel.: +49 30 868702020
Fax: +49 30 57708224
Mobil: +49 179 7923088
E-Mail: [hidden email]
http://www.thechicken.com
http://www.facebook.com/pages/The-Chicken/162425640445977
http://twitter.com/the_chicken_com

Handelsregister: HRB 126354 B
Amtsgericht Berlin-Charlottenburg
Geschäftsführung:
Adrian Haß, Sebastian Kurt

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: controller pattern best practice

virtualeyes
Thanks Skurt, but proceed with caution, my background is LAMP stack ;-)

You'll probably want to change a couple of things to suit your needs (for example, using a beforeInterceptor {} instead of abstract class' ctor in order to access controller scoped vars like controllerName), not to mention implementing some flavor of the i18n method:

def i18n(String key, String msgCode = 'default.not.found.message', Boolean renderMsg = false) {

                // convert abbreviated code like 'updated' or 'not found' to 'default.updated.message'
                if(msgCode.split('\\.').size() == 1) {
                        msgCode = {
                                def(a,b,c) = ['default',null,'message']
                                b = msgCode.contains('not ')? msgCode.split(' ').join('.'): msgCode
                                [a,b,c].join('.')
                        }()
                }
                Map msgArgs = [
                        code: msgCode,
                        args: [message(code:"${entityName.lower()}.label", default:"$entityName"), entityID]
                ]
                withFormat {
                        html { flash.message = message(msgArgs) }
                        json { if(renderMsg) render message(msgArgs) }
                        xml  { if(renderMsg) render message(msgArgs) }
                }
        }

Would be nice if Groovy supported named args a la Python -- so boolean renderMsg arg could be called like:
i18n pkey, 'not found', renderMsg:true (instead of just "true" which reveals little about the arg's purpose)
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: controller pattern best practice

Ian Roberts
On 19/08/2011 19:37, virtualeyes wrote:
> Would be nice if Groovy supported named args a la Python -- so boolean
> renderMsg arg could be called like:
> i18n pkey, 'not found', renderMsg:true (instead of just "true" which reveals
> little about the arg's purpose)

The Groovy approach is to make the method take a Map parameter

i18n(key:pkey, code:'not found', renderMessage:true)


def i18n(Map args) {
  String key = args.key?.toString()
  String msgCode = args.code ?: 'default.not.found.message'
  def renderMsg = args.renderMessage ?: false
  //...
}

Ian

--
Ian Roberts               | Department of Computer Science
[hidden email]  | University of Sheffield, UK

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: controller pattern best practice

virtualeyes
Ian, right, interesting.

Of course, now looking at method signature you know nothing about the intent of Map args, not to mention being unable to enforce args.foo Type. On the other hand, method calls are explicit, no mystery at all as to what the intent is.

You can mix and match named & non-named method params, but I believe the named param will always be called first, so your first arg and any other to-be-named arg must be defined before non-named args in method signature.

I'd like my cake and eat it too, however, so typed method signature with option to use named params where desired would be ideal -- perhaps in Groovy 2.0.
Loading...