【问题标题】:Grails - controllers and tight coupling with backendGrails - 控制器和与后端的紧密耦合
【发布时间】:2012-10-22 02:08:37
【问题描述】:

当您在 grails 中生成控制器时,控制器直接调用域层上的方法 - 我完全不明白这一点,我的每一点都告诉我这是一种错误,因为您将后端与前端。我认为这属于服务层。

由于在服务层中为域对象上定义的所有方法创建一组等效的方法会非常难看,我创建了这个AbstractService 来将所有(缺失的)方法调用从服务层委托给域层:

abstract class AbstractService {
    def entityType

    /**
     * By default, this method takes the name of the service that extends this
     * class, removes the suffix 'Service' and tries to create the Class object
     * from the resulting name. Override at will.
     */
    protected Class getEntityType() {
        if (!entityType) {
            try {
                entityType = Class.forName(this.class.name[0..-8], false, Thread.currentThread().contextClassLoader)
            } catch (ClassNotFoundException e) {
                throw new ClassNotFoundException("Class ${this.class.name[0..-8]} could not be found. Please "
                                + "override AbstractService#getEntityType() for ${this.class}.")
            }
        }
        entityType
    }

    def methodMissing(String name, args) {
        try {
            if (getEntityType()?.metaClass?.getStaticMetaMethod(name)) {
                getEntityType().invokeMethod(name, args)
            } else if (args?.last()?.metaClass?.getMetaMethod(name)) {
                args.last().invokeMethod(name, args.take(args.size() - 1))
            } else {
                throw new MissingMethodException(name, this.class, args)
            }
        } catch (MissingMethodException e) {
            throw new MissingMethodException(name, this.class, args)
        }
    }
}

然后我只是扩展此服务,例如像这样:

class UserService extends AbstractService {
}

然后我的控制器可以看起来像这样:

class UserController {
    def userService

    def create() {
        userService.save(new User(params))
    }

    def list() {
        userService.list(params)
    }

    // et cetera...
}

你不觉得这样更好吗?多亏了依赖注入,我可以重写整个业务层,而无需更改控制器中的代码——这就是我们使用依赖注入的原因,不是吗?

感谢您的回答,我希望听到尽可能多的意见。

【问题讨论】:

    标签: grails


    【解决方案1】:

    此模型在 Java Web 应用程序和所有应用程序中非常常用。 Rails(以及遵循它的 Grails)社区只是试图打破这里的范式,让它变得更简单。我的意思是,如果这个实体可以简单地完成这项工作,你为什么要委托一个服务类来操纵一个实体?如果实体做这项工作是自然的,那么不要让其他人来做。这样一来,您就可以避免使用Anemic Model,因为您的对象不仅是数据持有者,而且还知道如何运营自己的业务。

    话虽如此,有时您最好使用服务类对您的实体进行操作。例如,如果它同时涉及不同类型的实体等等......所以,当它不是“自然的”(并且你必须强制使其工作)时,实体本身来处理操作,那么服务类就是要走的路。基于 Rails 的This article 提供了一些关于使用服务类的提示。

    而且您并没有将控制器与模型紧密耦合(您说的是后端和前端,但我想这就是您的意思)。控制器最终将需要使用模型,无论是实体本身还是操作它的服务类(也是模型)。

    【讨论】:

    • 是的,这一切都很好,很酷,但是如果您后来决定要从 SQL 数据库迁移到 noSQL 数据库怎么办 - 您将不得不重写前端(控制器)因为您不能指望具有 GORM 定义的方法的域类。或者你可能希望你的 webapp 同时支持 noSQL 和 SQL——你会怎么做?无论如何感谢您的回答!
    • 这是一个错误的前提。域类正在实现 GORM API。如果您想从经典 SQL 更改为 NoSQL,则将 GORM API 实现更改为适当的 NoSQL 数据库,而无需触及控制器。已经有几个可用的 GORM API 实现,只需在 Grails 插件中搜索您最喜欢的 NoSQL 数据库即可。
    • 我不认为这是一个错误的前提。我认为您可能想要重写整个后端以实现更多的可伸缩性 - 您可能会使用 EJB 或者您可能决定用 C++ 或其他方式编写后端,我认为这是可能的,在这种情况下 -保留 GORM api 可能根本不可能。
    • 是的,你可能会。再一次,解决方案是使用 GORM API 的不同实现,一种适合上下文的实现。请记住,为此使用服务类并不能解决问题,而只是添加另一层间接性。
    • 服务的好处是你可以有两个(或更多)不同的实现。我有点同意 OP,因为我认为除了基本的 GORM 方法之外的任何其他方法,如保存、查找或删除都应该使用 - 主要的 GORM 实现提供了很多经常使用的休眠特定功能(作为分离标准 API)。因此,如果您在控制器中使用除了最基本的 GORM 操作之外的其他东西,单独更改 GORM 实现将无济于事
    【解决方案2】:

    脚手架控制器代码并不真正代表理想的应用程序架构。请记住,生成的脚手架代码只是生成应用程序的 CRUD 部分的起点。

    您是对的,一般来说,您不想将大部分 GORM 查询放在控制器中,因为控制器应该用于与前端交互。您当然可以将查询/业务逻辑放入服务中,也可以将查询直接放入域类中。这就是 Grails 服务支持声明式事务处理的原因。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-24
      • 2017-05-09
      • 2018-11-19
      • 1970-01-01
      • 1970-01-01
      • 2017-12-28
      相关资源
      最近更新 更多