Skip to content

guolei/eco_apps

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

eco_apps

eco_apps is a ‘client’ gem for rails applications to interact with each other in a rails application ecosystem. This is based on how we construct the Idapted platform and was first publicly presented at RailsConf (view ppt here: www.slideshare.net/jpalley/railsconf-2010-from-1-to-30-how-to-refactor-one-monolithic-application-into-an-application-ecosystem)

This gem will post an app’s configuration to the ‘master’ app (which is powered by the eco_apps_master gem) and provide functionality to power the ‘ecosystem’ (like web services, read only database connections and url helper method).

Key URLs:

Git/Issues: http://github.com/eleutian/eco_apps
Mailing List: http://groups.google.com/group/eco_apps
Blog: http://developer.idapted.com

Install

gem install eco_apps
gem install eco_apps_master

The Idea Behind the Application Ecosystem

As your business grows bigger, you just can’t stop adding new models/controllers to your original rails application – resulting in a messy, unmaintainable and difficult to deploy monolithic application. By splitting a single rails “application-system” into many independently maintainable yet interconnected applications, we’ve found a number of advantages: lower development time, greater stability and scalability and much higher developer happiness.

In this rails application ecosystem, there’s one application playing the master role. It manages the configuration info for all the applications. The ‘node’ application keeps its configuration in one file and post this to the master app when the server starts. The node app will ask the master app for another node’s info when they interact with each other. All these process are packaged in gems eco_apps_master and eco_apps.

eco_apps_master is used by master application. Any application using this gem will become the master app. eco-apps is used by all of the node applications. When an app uses this gem, it will be in the ecosystem.

Getting Started

Business logic

Suppose we are going to build an online petstore. We’ll split the features into two groups or user stories. One group is for pet info management (adding pets from an admin view and browsing/commenting/sharing/etc. from an end-user view) and the second story is ordering (monitoring orders from an admin view and actually placing the order with necessary info from an end user view). We are going to use two rails application to accomplish this: ‘pet’ and ‘order’

Create rails project

Create the two rails application and add the necessary models/controllers. For example:

pet:

rails new pet_app
cd pet_app
rails g model dog
rails g controller dogs

order:

rails new order_app
cd order_app
rails g model order
rails g controller orders

It’s easy and normal to add functions for pet application - it doesn’t need to know anything about the order application. However, we run into a problem when creating order: an order needs to know the information about the dog model (in this case) that is being ordered.

Read Only database connection

Suppose the default page of the order application lists all of the dogs available for a user to select. In this case, the order application needs the data stored in the dog table of the pet application. Thus, we use a “read only database connection”, so that the dogs info can be read from (but not write to) the dog table. It is important to understand, the one application never writes to multiple databases.

To set up this read-only db connection, the order app needs to know the db config of the pet app. This is where the ‘master’ app comes in. It knows the configuration info for all of the applications and the node applications can get this information from the ‘master’ app.

So, let’s make the pet app as our master application:

# pet/Gemfile
config.gem 'eco_apps_master', '>= 0.2.0'

When you restart server, you can see that it will create a table called “eco_apps_stores” which will store all of the apps’ info and a file “config/app_config.yml” which will store configuration of pet. And as ‘eco_apps_master’ is built on ‘eco_apps’, pet will also be one node of ecosystem.

Start pet at port 3000 in production mode and an exception will be raised, saying “master_app_url ‘production.lan’ is unreachable! Please change it in GEM_DIR/eco_apps/lib/platform_config.yml or APP_ROOT/config/app_config.yml and make sure the master app starts at this address.”.

production.lan” is the default value of master_app_url in ‘GEM_DIR/eco_apps/lib/platform_config.yml’. In this example, we should change it to “localhost:3000”.

And as pet acts as master app here, we need to add ‘in_master_app: true’.

# pet/config/app_config.yml
in_master_app: true
url: http://localhost:3000
master_app_url: http://localhost:3000

Now we can add order app into this ecosystem as a node.

# order/Gemfile
gem 'eco_apps', '>= 0.2.0'

Start order at port 3001, and edit config file.

# order/config/app_config.yml
url: http://localhost:3001
master_app_url: http://localhost:3000

NOTE: If master_app_url is set in ‘GEM_DIR/eco_apps/lib/platform_config.yml’, it will works for all node apps.

Start pet first, and then order, you can see that order is posting its info to master(pet here) app.

NOTE: To make the requests respond faster in development mode, the app’s config info will be posted to master app only in production mode. As a result, you need to start one app in production mode at least 1 time to make it available to others.

Now we can make use of the gem’s magic to make it easy to setup a readonly db connection.

# order/app/models/dog.rb
class Dog < ActiveRecord::Base
  acts_as_readonly :pet
end

class Order < ActiveRecord::Base
  belongs_to :dog
end

That’s it. Now you can use the dog model just as normal active_record object - the only difference being that it reads the data from the pet’s database and it can not modify records. Note that acts_as_readonly relies on ActiveRecord::Base.connection.current_database, so the databases like sqlite3 not implementing this method are not supported.

Navigation Between Apps

It’s common that users will need to jump from one application or user story to another. In our petstore example, consider a functionality that links an order page to a detailed info page on that dog being ordered.

It’s easy to hard code the page url in order app, like

<%= link_to "view detail", "http://localhost:3001/dogs/#{dog.id}" %>

However, this will cause tight coupling between apps and become painful if URL’s change.

To solve this problem, applications can publicly expose URLs to other apps. Consider this configuration in the pet app:

# pet/config/app_config.yml
api:
  url:
    dog_detail: dogs/:id

And this url_of code in the orders app:

# order/orders/index.html.erb
<%= link_to "view detail", url_of(:pet, :dog_detail, :id => dog.id) %>

Web Services

Sometimes you cannot avoid one app needed to update the information in another application’s database. This usually goes along with some business logic. We use active resource to achieve this and again, the configuration is automatic:

# order/models/dog_service.rb
class DogService < ActiveRecourse::Base
  self.site = :pet
end

# pet/controllers/dog_services_controller.rb
class DogServicesController < ActionController::Base
  ip_limited_access # optional, only can be accessed by intranet ip (defined in GEM_DIR/eco_apps/lib/platform_config.yml)
end

Q&A

How to test readonly models?

Readonly models are not readonly in test mode. You can test them just as normal models. The difference is that they are in other databases.

If you would like to keep all the tests in the same database, you can define the columns of the readonly model in app_config.yml, then the tables will be created accordingly.

# order/config/app_config.yml

readonly_for_test:
  dogs:
    string: breed, name

How to set master_app_url?

There’s two ways to set master_app_url. One is to set it in each app like this:

# config/app_config.yml
master_app_url: http://internal_path_to_master.lan

As the master_app_url for all your apps in the same ecosystem (and usually server) is the same, it is often easier to set this configuration on the gem level:

# GEM_DIR/eco_apps/lib/platform_config.yml
master_app_url: http://internal_path_to_master.lan

How to keep different config for different rails modes?

It is necessary to keep different url configuration for production and development mode, you can do this as following:

# APP_ROOT/config/app_config.yml
url:
  development: http://example.dev
  production: http://example.production

# GEM_DIR/eco_apps/lib/platform_config.yml
master_app_url:
  development: http://example.dev
  production: http://example.production

How to make app using another’s configuration that is different from what stored in the master app?

Sometimes it’s necessary for one application to use another’s configuration that is different from what stored in the master app. This may be because you need to use a different configuration on your own machine rather than on the test server, or even because the app doesn’t exist yet.

You can mock another’s configuration in development mode in this way:

# APP_ROOT/config/app_config.yml
# other configuration

cached_config:
  order: # This is another app's name
    url: # set url
    api: # The same rule with config in app_config.yml

In addition to this, the config of other apps will be cached in this way. If config of one app is changed, you need to delete it from ‘app_config.yml’ manually to keep it up-to-date.

How do you setup your team to develop in this way?

We have a staging server and staging “master” app. This staging server holds a stable version of each app with real data. When we are working on an application on your local system, the ecosystem it connects to is this “stable staging” environment.

What about security?

In the platform_config.yml file of the eco_apps gem you will find an intranet_ip setting. You can use this setting to define which intranet addresses are allowed to access the eco_app_master services. Set your firewall accordingly!

Releases

No releases published

Packages

No packages published