Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Grails - the use of service layer

I have problems in organizing my code when I want to update a non-simple domain object. The problem is to separate responsibilities for the controller and the service layer.

More explicitly, assume that we have a domain class Client that depends on other domain classes like Address and so on.

In the view there is a gsp for editing some of the Clients properties including some of the nested properties like street on the Address.

When I would like to update those fields I call the update method on a Controller (in this case the ClientController).

I like the feature coming from the errors of a domain class when validated. Like if I in the Controller write

Client client = Client.get(params.id)
client.properties = params
client.validate()

If the client now has errors it is very easy to display them in the edit view.

But, I thought that updating, saving and getting the client from the database (Client.get(theId)) should be handled by the service layer. In my case I have to update or create other domain objects (like the Address) before I update the Client.

So one of my questions are how should the API look like for the service layer?

public ... updateClient(…)

In the literature they have the trivial example of updating the age of a person. So, their API consists of the id of the person and the new age. But, in my case I have about ten params from the view and they are just a subset of all properties of a client and I don't know which one of these that have changed.

  1. I would like to have a Client in the controller that I can validate and resend to the edit view if it has validation errors.
  2. I would like to handle the database interactions and transactions from the service layer.

How can I combine these? What responsibilities should the different layers have regarding update? How should the API of the service layer look like regarding update?

If there is a good reference implementation somewhere I would be happy to study it. Many times the service layer is unfortunately totally or partially ignored.

like image 617
Per-Henrik Avatar asked Sep 12 '14 08:09

Per-Henrik


People also ask

What is Grails domain class?

A domain class fulfills the M in the Model View Controller (MVC) pattern and represents a persistent entity that is mapped onto an underlying database table. In Grails a domain is a class that lives in the grails-app/domain directory.


1 Answers

The missing piece of this puzzle is command objects. These classes represent the contract for your API into your services and provide you the ability to have a concrete class for your views and for validation. Let's look at an example.

Given a domain class of Client which as an Address and several Phone instances your Service layer might look like this:

...
class ClientService {
  def updateClient(ClientUpdateCommand cmd) {
    ..
  }
}
...

While the ClientUpdateCommand looks something like this:

@grails.validation.Validateable
class ClientUpdateCommand {
  Long id
  String name
  List<PhoneUpdateCommand> phoneNumbers = []
  AddressUpdateCommand address = new AddressUpdateCommand()
  ...
  static constraints {
    id(nullable: false)
    name(nullable: false, blank: false, size:1..50)
    ...
  }
  ...
}

You will note that this command object is composed of other command objects, and has constraints for validation. It may seem like you are replicating your domain classes here, but I have found that the more complex your application then more differences will appear between the domain classes and your command objects.

Next up is the use of the command object within your controller and your views. A controller method may look something like this:

Class ClientController {
  ...
  def clientService
  ...
  def edit(ClientUpdateCommand cmd) {
    ...
    cmd = clientService.load(cmd.id)
    Map model = [client: cmd]
    render(view: 'edit', model: model)
  }
  def update(ClientUpdateCommand cmd) {
    Map model = [client: cmd]
    if (cmd.hasErrors() {
      render(view: 'edit', model: model]
      return
    } else {
      clientService.update(cmd)
      ...
    }
    ...
  }
}

I have left a lot out of the controller as I didn't want to bore you with the details but rather show how a command object replaces the domain instance. In some ways it's a little bit more work but it moves you completely away from manipulating the domain classes and delegates that to the service you have created. You will also notice that the command object replaces the domain class instance for your model for your views. I won't bother giving you any examples of the GSPs since they really don't change much when using command objects like this.

I'm sure there could be entire chapters of books written on the subject, but hopefully this gives you some insight and you can see that the answer to your question(s) is: Command objects.

like image 114
Joshua Moore Avatar answered Oct 28 '22 01:10

Joshua Moore