孙悟空
孙悟空

Reputation: 1285

Rails deployment, one environment per client is that right way?

Our application is a Saas for the hospital, is deployed on one production server (same code with linux+nginx+puma) for all our clients. By default, there are three of them: test, development and production. But in our case, we have 10 environments config files point to 10 clients config file in folder 'config/environments/'. And we give hospitals name to environment file, such as

config/environments/hospital1.rb
config/environments/hospital2.rb
config/environments/hospital3.rb
...

There are several features paid is developed with engine.

Now imagine that hospital1 paid for feature1. How to active feature1 only for hospital1 ? I am currently doing this in Gemfile

group :hospital1 do
  gem 'feature1', path to ....
end

Question: Is this a good way to replace production environment to client-specific environment and be able to Enable/Disable a gem in GEMFILE ?

Any suggestion is welcome!

Upvotes: 3

Views: 162

Answers (4)

Vasfed
Vasfed

Reputation: 18444

Approach with environment per customer does not scale well. Gemfile is definitely not a place to keep client's settings. Also it's harder to test.

If you need strong separation (separate db, workers etc.) - I'd still go with one production environment:

  1. have a config file, that has settings for all your clients (enabled features etc.):
hospital1:
  name: Imaginary Medical Center
  address: Nowhere st. 0
  features:
    cool_notifications: true
    feature2: false
hospital2:
  name: ObfuscatedView Commutinu Hospital
  features:
    feature1: false

later this can evolve into an administrative database

  1. instead of environment pass an ENV variable with tenant id (like HOSPITAL_NAME=hospital1 rails server -e production), and make a singleton object that loads that config upon calling
  2. all engines can be in gemfile, but with require: false and you can only load them in application.rb (right after Bundler.require) if corresponding feature is enabled

Also have a look at apartment gem, if you can allow some resource sharing (like web workers). You'll have to add multidomain support to your app and feature toggles, but it'll be even easier to handle growing client number (and save server resources, most probably none of your clients produce high sustained load)

Upvotes: 0

Amit Patel
Amit Patel

Reputation: 15985

This is really bad approach.

Basically you are trying to do multitanancy but in very odd and uncontrolled way.

It's an architectural decision and it cannot fit in StackOverflow answer and many take a whole book to explain it.

Take a look at https://rubygarage.org/blog/three-database-architectures-for-a-multi-tenant-rails-based-saas-app to get some perspective.

Basically feature enable/disable should be done based on rules which should easily be managed via some admin panel at the least.

Upvotes: 1

inem
inem

Reputation: 116

That sounds like a bad idea.

You're "mixing oranges and sofas" here. Environments and clients have different semantics.

What you have is either different instances of the same app deployed on different servers(and then client-specific stuff should come from environment variables), or if it is a single deployment(looks like it is your case), then client-specific setting should be stored in the database.

As for limiting access to paid features, you should look into solution like pundit to do that. Gemfile is definitely not the right place for your business-logic!

Upvotes: 2

katafrakt
katafrakt

Reputation: 2488

Personally, I prefer to think of environment as technical-level thing, not application logic thing. Therefore I'd try to keep as few environments as possible. If you want to have conditional statements in Gemfile perhaps it's better to use environment variables? Something like

if ENV['FEATURE_1_ENABLED']
  gem 'feature1', path to ....
end

You could even automate managing of these variables by having a .env file per hospital and load apropriate one with Dotenv.load.

Upvotes: 1

Related Questions