Grails - 使用服务层

时间:2014-09-12 08:08:00

标签: grails service layer

当我想要更新非简单域对象时,我在组织代码时遇到问题。问题是要分离控制器和服务层的职责。

更明确地说,假设我们有一个域类Client,它依赖于其他域类,如Address等。

在视图中有一个用于编辑某些Clients属性的gsp,包括一些嵌套属性,例如Address上的street。

当我想更新这些字段时,我在Controller上调用update方法(在本例中是ClientController)。

我喜欢在验证时来自域类错误的功能。就像我在控制器中写道

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

如果客户端现在有错误,很容易在编辑视图中显示它们。

但是,我认为更新,保存和从数据库中获取客户端(Client.get(theId))应该由服务层处理。在我的情况下,我必须在更新客户端之前更新或创建其他域对象(如地址)。

所以我的一个问题是服务层的API应该如何?

public ... updateClient(…)

在文献中,他们有一个简单的例子来更新一个人的年龄。因此,他们的API由人的身份和新时代组成。但是,在我的情况下,我从视图中看到大约十个参数,它们只是客户端所有属性的一个子集,我不知道哪一个已经改变了。

  1. 我想在控制器中有一个客户端,如果它有验证错误,我可以验证并重新发送到编辑视图。
  2. 我想处理来自服务层的数据库交互和事务。
  3. 我如何结合这些?不同层面对更新有哪些责任?关于更新,服务层的API应该如何?

    如果在某个地方有一个很好的参考实现,我会很乐意研究它。很多时候,服务层不幸被完全或部分忽略。

1 个答案:

答案 0 :(得分:5)

这个难题的缺失部分是command objects。这些类表示API与服务的合约,并使您能够为视图和验证创建具体的类。让我们来看一个例子。

鉴于Client的域类,Address和几个Phone个实例,您的服务层可能如下所示:

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

虽然ClientUpdateCommand看起来像这样:

@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)
    ...
  }
  ...
}

您将注意到此命令对象由其他命令对象组成,并具有验证约束。您似乎在这里复制域类,但我发现应用程序越复杂,域类和命令对象之间就会出现更多差异。

接下来是在控制器和视图中使用命令对象。控制器方法可能如下所示:

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)
      ...
    }
    ...
  }
}

我从控制器中留下了很多东西,因为我不想让你了解细节,而是展示命令对象如何替换域实例。在某些方面,它需要更多的工作,但它会使您完全远离操纵域类并将其委托给您创建的服务。您还会注意到命令对象替换了视图模型的域类实例。我不打算给你任何关于GSP的例子,因为在使用这样的命令对象时他们真的没什么变化。

我确信可能会有关于这个主题的书籍的整个章节,但希望这会给你一些见解,你可以看到你的问题的答案是:命令对象。