tags:

views:

140

answers:

5

As I've been using Grails more and more, I find myself writing code in multiple controllers that really seems like it should be part of a domain class. Sometimes, this domain code contains a call to a service class. For example, I recently wrote a domain method that looked something like this:

class Purchase {

    // Injected
    def paymentService

    String captureTransactionId
    Boolean captured

    // ...

    def capture() {
        captureTransactionId = paymentService.capturePurchase( this )
        captured = captureTransactionId != null
    }

I don't feel outright dirty writing this code, but I haven't made a study of best design practices in Grails, so I wanted to get some opinions.

+2  A: 

I don't think that domain/model classes should be calling services. It ought to be the other way 'round.

A service can orchestrate other services to fulfill a use case. I think that's the right way to go.

duffymo
Can you please explain the rationale behind your opinion? What makes it better or worse to do it one way over the other?
Javid Jamae
I agree with duffymo; the reason is the principle of least surprise. A domain class calling a service class is surprising and a strong sign that responsibilities have not been assigned to classes properly.
ammoQ
That's the Spring way, and Spring is the basis for Grails.
duffymo
Thanks for the answers @ammoQ and @duffymo, but I'm still not quite convinced (not that I can't be). I'm sure that not everyone would agree that the "Spring way" is to create an anemic domain model (see: http://stackoverflow.com/questions/1304245/spring-and-the-anemic-domain-model ).
Javid Jamae
If you do it /right/, you are able to reuse your domain classes for another project that needs to do different things to the same database. /Wrong/ dependencies make that difficult and ugly.
ammoQ
Spring's service layer is more on the functional side, I'll agree. But whether or not your domain objects are "anemic", you should not be calling services from model objects. If that logic belongs in a model object, then it shouldn't be a service. Sounds like the design mistake was putting it in the service in the first place. One or the other, but not both. And not done by having the model call the service. Services instantiate model classes.
duffymo
Ultimately, I don't think it has to be a procedural looking service. I think that even in Grails you can follow the principles of Domain Driven Design and have a rich object model. I haven't heard a convincing argument behind the thick Transaction Script like services besides "its just right", "that's what you're supposed to do" and "its surprising". I may be going out on a limb here, but having a rich domain model isn't surprising to me, its natural.
Javid Jamae
+3  A: 

I go back and forth with stuff like this. Before Grails I had no problems with anemic domain classes and putting everything in helpers. The big reason I often ended up with anemic classes is validation. It's simple to validate nullability, length, etc. inside the class but uniqueness requires a database check and that's not relevant to a domain class (in a non-Grails app) so I'd move that to a helper. Now I've got validation in two places, so I'd consolidate in the helper and would be left with a data-only class.

But Grails replaces the need for DAOs by wiring in GORM methods into domain classes, and also replaces the need for validators by putting validation in the domain classes. So this creates issues when deciding what business logic should go in the domain class and what should be in a service or other helper - services make an excellent place to put business logic that might be needed in a domain class method or validator.

Yes it's not OO-pure, yes you create a cycle (the service calls the domain class and the domain class calls the service), no it's not "the Spring way" but a lot of Grails is not "the Spring way".

Coupling like this does make it harder to separate an app into components or plugins for reuse, but declaring services with 'def paymentService' helps a lot since you're not coupled to a package name or implementation.

Burt Beckwith
Yes, I've been using services mostly as small and focused components that talk to external systems and then call them from wherever it makes sense. I think they're decoupled enough because of the injection model that you mentioned. I like this much better than the Transaction Script type of model that @duffymo and @ammoQ seem to be suggesting.
Javid Jamae
+1  A: 

Hi, I only give my personal opinion. Since grails supports injecting services into domain classes automatically (unlike e.g. injecting services into standard groovy classes, which you have to configure yourself), I'd guess that it was intended to be used that way and therefore is not a bad practice.

Also it makes code more readable with something like "myDomainInstance.someUsefulMethod()", than "someService.someUsefulMethod(myDomainInstance)" (hopefully you know what I mean).

Lodovik
Yes, I know what you mean, and I feel the same. I find this similar to using a segregated interface in Java and having your domain code call DAO classes. That pattern seems to have inspired things like GORM where you just tell the domain class to save itself. I see this as the same thing. I tell the payment class to capture itself and it talks to the external system to take care of it. In this way, there is low coupling and I've avoided writing a very procedural looking *Transaction-Script style* service class.
Javid Jamae
A: 

I'm not sure whether it's right or wrong.

In cases like the example, what about going one way or the other: either putting the paymentService.capturePurchase() code inside the Purchase class, or you could putting all the logic in the service?

Fletch
Because I want to separate the concerns and maintain a single responsibility for my components. I want the domain code to update the domain logic, and I want the service code to interact with the external payment system.
Javid Jamae
A: 

I have a question regarding this. Suppose I create a domain class which calls a service. Via GORM the database structure in the back end is automatically created. After a while, I realize I need to modify some of the classes that make up the service. How is this modification going to affect my domain class? In other word, I understand GORM is an object model,but how do I know my modifications would not have changed the model GORM would have implemented if it did not have to use the existing model created with the original service?

Bruno
@Bruno you should probably ask this as a separate question.
Alison