ruby-on-rails - Rails 应用程序的领域驱动设计 : Implementing a service in a basic example

标签 ruby-on-rails ruby design-patterns domain-driven-design component-based

两个模型:一个所有者和一个:

owner.rb

class Owner < ActiveRecord::Base
  has_one :dog
end

dog.rb

class Dog < ActiveRecord::Base
    belongs_to :owner
end

这是架构:

schema.rb

ActiveRecord::Schema.define(version: 123) do

  create_table "dogs", force: true do |t|
    t.string   "name"
    t.integer  "energy"
    t.integer  "owner_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  add_index "dogs", ["owner_id"], name: "index_dogs_on_owner_id"

  create_table "owners", force: true do |t|
    t.string   "name"
    t.string   "energy"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

end

非常简单的设置。

我想要一个主人带他的去散步。当散步结束时,主人的能量会下降 5,狗的能量会下降 20。

很明显,这个walk_the_dog Action /方法,无论它在哪里,都会影响两个对象:一个owner 对象和一个dog 对象(当然,这个狗对象恰好与这个所有者相关联)。

我不知道把这段代码放在哪里。我知道我可以简单地在 owners_controller.rb 中创建一个 Action ,但这似乎是个坏主意。它看起来像这样:

owners_controller.rb

class OwnersController < ApplicationController
    def walk_the_dog
        @owner = Owner.find(params[:id])
        @owner.energy -= 5
        @owner.dog.energy -= 20   # this line in particular seems like bad OO design
        @owner.save
        @owner.dog.save
    end
    ...
 end

据我了解,对象应该只改变自己的状态,不应该改变其他对象的状态。所以这似乎是个坏主意,因为在所有者 Controller 中,我们不仅要更改 owner 对象的状态,还要更改关联的 dog 对象的状态。

我已阅读有关服务的内容。 walk_the_dog 似乎是服务的绝佳案例,因为据我所知,服务允许对象之间的交互以及多个对象的状态更改。我只是不知道如何去做/实现它。

是否应该有一个名为walk_the_dog 的服务对象?它们是否应该只是服务目录中的一个文件,其中包含一堆服务方法——其中一个称为 walk_the_dogowners_controller.rb只是在它的 Controller 中使用这个方法?我不知道下一步是什么。

注意:我看到有人说“谁在乎这是否破坏了 OO 设计。就去做吧,如果它有效,它就有效。”不幸的是,这不是一种选择。我现在正在开发的应用程序遵循了这种思维方式。应用程序变得非常大,现在维护起来非常困难。我想在应用程序的重大重新设计中解决这种情况。

最佳答案

如果重构这段代码,我会做以下几件事:

在你的代码中写数字是一件坏事,要么你已经将它们定义为常量,如 ENERGY_PER_WALK_FOR_DOG = 20或者更好的方法是在 Dog 的表中定义一个字段模型。这样,可以更好地管理和分配这些值。

add_column :dogs, energy_per_walk, :integer, default: 20
add_column :owners, energy_per_walk, :integer, default: 5

我会在 ApplicationController 中创建一个方法类:

def walk(resources = [])
  resources.each do |resource|
    resource.lose_walk_energy # you can refine it more!
  end
end

在文件夹 app/models/concerns 中,我将编写以下模块:

module Walkable
  extend ActiveSupport::Concern


  # subtract energy_per_walk form the energy saved in db
  def lose_walk_energy
    self.energy -= self.energy_per_walk
    save
  end

end

现在,您的方法简化为以下方法:

def walk_the_dog
  @owner = Owner.find(params[:id])
  walk([@owner, @owner.dog])
end

关于ruby-on-rails - Rails 应用程序的领域驱动设计 : Implementing a service in a basic example,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30785782/

相关文章:

ruby-on-rails - 无效的单表继承类型 :

ruby-on-rails - 为什么我不能访问 ruby​​ 中的实例变量?

ruby - 如何打开相对于主目录的文件

arrays - 过滤二维数组没有副作用

c# - 在应用 Mode-View-ViewModel 设计模式时包括部分 View

ruby-on-rails - Ruby group_by 具有不同键的哈希数组(键不固定)

ruby-on-rails - 正则表达式 : How do I grab a block of text using regex?(在 ruby​​ 中)

oop - 如何理解 DCI 模式

ruby-on-rails - ruby on rails 设计模式

ruby-on-rails - Rails 3,设置i18n语言环境不起作用