我们有两个 Rails 项目,共用一个数据库 一个是 主站系统 一个是 运维系统 我们希望两个项目共用一个套 Migration 代码。
额外说明,在发布的时候 我们会通过配置 database.yml 让主站系统 和 运维系统 连接到 同一个数据库。
我们正在研究,在开发和测试阶段,怎么做最合适?
我们希望在开发和测试阶段,两个项目都可以独立的创建数据库。
rake db:migrate
我们考虑有以下解决方案:
两个项目放在一个 Git 中,将 Migration 放在一个项目中,另一个项目通过 database.yml 连接到数据库。
两个项目放在两个 Git 中,将 Migration 放在两个项目中,复制 Migration 代码。
两个项目放在两个 Git 中,将 Migration 放在一个项目中,另一个项目通过 Linux File Link 或 Ruby Require 引用。
两个项目放在两个 Git 中,将 Migration 放在一个项目中,另一个项目通过 database.yml 连接到数据库。
三个项目放在三个 Git 中,将 Migration 放到一个 Gem 中。
三个项目放在三个 Git 中,将 Migration 放到一个独立的 Git 中,两个项目通过 Git Submodule 引用。
希望,有相关经验的高人, 能够分享他们的解决方案, 非常感谢!
相关资料
http://stackoverflow.com/questions/2690546/multiple-rails-app-single-mysql-database http://stackoverflow.com/questions/2599920/multiple-applications-with-ruby-on-rails http://stackoverflow.com/questions/10802793/ruby-on-rails-multiple-applications-with-same-model-and-db
没有试过,两套 migration 的脚本如果 version id 不一样应该相互不影响吧,虽然只有一张 schema_migrations 的表。
#10 楼 @ery 我前几天看的一个 cms:
rake -T
rake refinery_authentication:install:migrations # Copy migrations from refinery_authentication to application
rake refinery_images:install:migrations # Copy migrations from refinery_images to application
rake refinery_pages:install:migrations # Copy migrations from refinery_pages to application
rake refinery_products:install:migrations # Copy migrations from refinery_products to application
rake refinery_resources:install:migrations # Copy migrations from refinery_resources to application
refinery_products 等都是这个 cms 的插件
# Specify additional Refinery CMS Extensions here (all optional):
gem 'refinerycms-i18n', '~> 2.0.0'
gem 'refinerycms-blog', '~> 2.0.0'
gem 'refinerycms-inquiries', '~> 2.0.0'
# gem 'refinerycms-search', '~> 2.0.0'
# gem 'refinerycms-page-images', '~> 2.0.0'
https://github.com/refinery/refinerycms
然后rake db:migrate:status
看哪些还没迁移就再迁移一遍。不知到你的情况和这种一样不。
你这种情况是否可以把迁移文件做成单独的 gem,所有系统引入这个 gem。就解决所有问题了。
我以前一个公司把 asset 单独做成了一个 gem,所有其他系统引入这个 gem。应该和你的情况类似。
当然这两种我都没实践过,可以考虑试一下,建个 demo 项目~
之前我们有个项目三个系统,1) 后台管理,2) 主系统,3)API,三套系统独立运行,但数据库是连接同一个,代码是放在不同的 git 中的,但是 db 目录是共享的,也就是@hooopo 说的同步 migration
其实我最没理解的是这句话:我们希望在开发和测试阶段,两个项目都可以独立的创建数据库。
既然你说两个项目没有依赖,完全可以独立运行。难道还不能独立的创建数据库?
我理解的没有依赖应该是这样的:
project A's database.yml:
development:
database: development_A
production:
database: production_C
-
project B's database.yml:
development:
database: development_B
production:
database: production_C
然后在开发环境分别做迁移都可以正确运行。
#7 楼 @ery 我理解你所说的场景下有两个 project,一个是前台,一个是管理后台。 解决方案要根据规模来定,小规模的时候可以由一个 project 来负责 migration,另外一个仅仅使用,不负责维护数据结构的升级,具体哪个维护 migration 取决于哪个 project 的变更更频繁。
同步 migration 的做法我没接触过,不过感觉是朝着错误的方向上深入下去了
应该提醒的是,此时要注意分拆 model 的业务逻辑,一个常见的误解是前后台共用一套模型,这个很多时候都是错误的,因为前后台对同一个东西的看法可能是截然不同的,如果共用模型,容易人为造成陷阱。实在重复的代码,可以做成对 model 的增强 gem,然后共用(@hooopo 的方案)
当然,上面说的是小规模,当业务变大的时候就必须朝服务化的方向转移了,所以关键还是要把业务分开
#26 楼 @ery 把一个项目的ActiveRecord::Migrator.migrations_path
指向另一个项目的db/migrate
目录
看这里的代码,做成 engine 就会自动去加载 engine 里的 migrations。手动设置ActiveRecord::Migrator.migrations_path
估计也会起到 engine 的效果。
task :load_config do
ActiveRecord::Base.configurations = Rails.application.config.database_configuration
ActiveRecord::Migrator.migrations_paths = Rails.application.paths['db/migrate'].to_a
if defined?(ENGINE_PATH) && engine = Rails::Engine.find(ENGINE_PATH)
if engine.paths['db/migrate'].existent
ActiveRecord::Migrator.migrations_paths += engine.paths['db/migrate'].to_a
end
end
end
然后只在一个项目里加 migration 文件。
企业级应用里面对这个的成熟解决方案是针对数据层的 SOA - 除非是玩具项目或者 prototype, 不建议重新发明轮子
两个 model 层对数据库理解完全不同的应用共用一个数据库非常容易造成数据质量问题,除非一个应用是只读,比如是报表这一类。这种情况可以考虑自动镜像 生产环境的数据库到 sandbox 供本地开发使用。同步 migration 的问题是把对数据的依赖转化为对开发流程的依赖,在多团队协作的时候或者开发员经验不足时会太乱。
#29 楼 @knwang SOA 不适合我们的情况, 如果要采用 SOA 的模式的话话, 我们需要建立第三个项目, 如果今后我们开发 IOS 客户端的话,我们会用 SOA。
因为两个项目的业务逻辑完全不同, 就像你提到的报表, 举个例子, 针对表 1,项目 A 执行读写操作,项目 B 执行只读操作。 针对表 2,项目 B 执行读写操作,项目 A 执行只读操作。 所以说两个项目对 Model 层的理解是完全不同的。
至于镜像生产环境的数据库, 由于我们的项目尚未发布, 所以木有生产环境的数据库。
所以开发和测试阶段, 同步 Migration,是比较适合我们的解决方案。 所以,我们在集中精力思考如何通过 Migration。 Git? File Link? Gem? Copy?
最后,非常感谢你的建议!
#31 楼 @ery 同步 migration 对开发流程非常制约,举个用 git / gem 的例子:
team 1 creates a migration A, runs it as part of development, but not commit to git / gem yet team 2 creates a migration B, runs it, commit to git / gem team 1 now commits migration A to git / gem after finishing a feature
now team 2 gets a migration A that it hasn't run with stamstamp before migration B that it has run.
如果两个团队都用 git branching 就更复杂了。退一大步说,没有这些复杂的情况,migration 的建立和运行也都是线性的,这种情况需要两边有更新 migration 的时候要立刻向对方大吼要立刻更新,并且 roll back 本地还没有 commit 的 migration。这种互相制约的情况很不利与项目的快速开发。向我上面说的,这是把数据库结构的依赖转化为对开发流程的依赖。
#33 楼 @knwang 我明白你的意思啦,非常感谢你细心的说明。 我们团队的开发流程,和你所描述的情况有些不同, 以下是我们的开发流程:
团队 A 开发项目 A,新建一个 Git Branch A,根据他们的需求随意改动 Migration 团队 B 开发项目 B,新建一个 Git Branch B,根据他们的需求随意改动 Migration 任何一个团队开发结束后, 直接提交代码到对应的 Git Branch 上(A 或 B), 周期一般一周左右。 团队 A 和团队 B 互不影响。
这个时候,团队 C 登场, 团队 C 逐个审查 Branch A 或 B 的代码, 做 Code Review, 审查并重构后, 再逐个提交到 Master Branch 上。
团队 C 最痛苦, 他们也是整个项目的瓶颈。 他们负责整合所有的 Branch。