how to disassemble one monster app into an ecosystem of 30

86
From 1 To 30 How To Disassemble One Monster App Into An Ecosystem Of 30 Jonathan Palley , CTO/COO Guo Lei, Chief Architect © 2010 Idapted, Ltd. Beta 技术沙龙 http://club.blogbeta.com 官方twitter : @betasalon Groups : http:// groups.google.com/group/betasalon

Upload: fiyuer

Post on 18-May-2015

1.327 views

Category:

Technology


5 download

TRANSCRIPT

Page 1: How to disassemble one monster app into an ecosystem of 30

From 1 To 30How To Disassemble

One Monster App Into An Ecosystem Of 30

Jonathan Palley, CTO/COO

Guo Lei, Chief Architect

© 2010 Idapted, Ltd.

Beta 技术沙龙http://club.blogbeta.com

官方twitter : @betasalon

Groups : http:// groups.google.com/group/betasalon

Page 2: How to disassemble one monster app into an ecosystem of 30

An

Experience

Page 3: How to disassemble one monster app into an ecosystem of 30

A Tale of Two Buildings

Page 4: How to disassemble one monster app into an ecosystem of 30

2009 ShanghaiLotus Riverside Community

1909 BeijingForbidden City

Page 5: How to disassemble one monster app into an ecosystem of 30

1

30

Page 6: How to disassemble one monster app into an ecosystem of 30

What is one?

Page 7: How to disassemble one monster app into an ecosystem of 30

The entire web application/system/platform

runs as one single rails application

(We are talking about really large systems. Multiple different types of clients/functions)

Page 8: How to disassemble one monster app into an ecosystem of 30

Problems

Hard to test/extend/scale

Confused new staff

Page 9: How to disassemble one monster app into an ecosystem of 30

What is 30?

Page 10: How to disassemble one monster app into an ecosystem of 30

A ecosystem of applications

Page 11: How to disassemble one monster app into an ecosystem of 30

Independent

Page 12: How to disassemble one monster app into an ecosystem of 30

Linked and Seamless

Page 13: How to disassemble one monster app into an ecosystem of 30

Basic features of each app• Separate database• Runs independently (complete story) • Lightweight (single developer)• Tight internal cohesion and loose external coupling

Advantages

• Independent Development Cycle

• Developer autonomy

• Technology (im)maturity safety

APPEAL TO DEVELOPER LAZINESS

Page 14: How to disassemble one monster app into an ecosystem of 30

What’s the mystery of the forbidden city?

Page 15: How to disassemble one monster app into an ecosystem of 30

Consistent UI

• Shared CSS/JS/Styleguide

• Common Helpers in Shared Gem

• Safely try new things

Page 16: How to disassemble one monster app into an ecosystem of 30

All applications use the same base CSS/JS

Keep all the application the same style

<%= idp_include_js_css %>

# =><script src ="/assets/javascripts/frame.js" type="text/javascript"></script>

<link href="/assets/stylesheets/frame.css" media="screen" rel="stylesheet" type="text/css" />

interface

Page 17: How to disassemble one monster app into an ecosystem of 30

CSS Framework

interface

Page 18: How to disassemble one monster app into an ecosystem of 30

Abstract Common Helpers to Gem

Search function for models

interface

Page 19: How to disassemble one monster app into an ecosystem of 30

Common Helpers: Combo search (cont)

View:<%= search_form_for(HistoryRecord, :interaction_id, :released,[:rating, {:collection=>assess_ratings}],[:mark_spot_num,{:range=>true}], [:created_at, {:ampm=>true}]) %>

Controller:@history_records = HistoryRecord.combo_search(params)

interface

Page 20: How to disassemble one monster app into an ecosystem of 30

Common Helpers: List table

well formattedwith pagination

sortablecustomizable

interface

Page 21: How to disassemble one monster app into an ecosystem of 30

Common Helpers: List table (cont)

<%=idp_table_for(@history_records,:sortable=>true,:customize =>

"history_records") do |item, col|col.add :id, link_to(item.id, admin_history_record_path(item)),:order=>:idcol.build :duration, :waiting_time, :review_timecol.add :scenario, item.scenario_title, :order => :scenario_titlecol.add :mark_spot_num

end

%>

interface

Page 22: How to disassemble one monster app into an ecosystem of 30

Development Lifecycle

interface

1. Implement new View code/plugin in a second application

2. Abstract into plugin using existing “idp” helpers

3. Put it into main view gem

Page 23: How to disassemble one monster app into an ecosystem of 30

interface data

Page 24: How to disassemble one monster app into an ecosystem of 30

How do applications share data?

(remember: each app has its own data)

data

-“Read Only” Database Connections- Services- AJAX Loaded View Segments

Page 25: How to disassemble one monster app into an ecosystem of 30

Business example

user

course

purchase

learning process

data

Page 26: How to disassemble one monster app into an ecosystem of 30

Purchase App

Requirement: List course packages for user to select to purchase

The course package data is stored in the “course” application

but

data

Page 27: How to disassemble one monster app into an ecosystem of 30

Solution

readonly db connection

data

course

Page 28: How to disassemble one monster app into an ecosystem of 30

Code

Model:class CoursePackage < ActiveRecord::Base

acts_as_readonly :courseend

View:<ul><% CoursePackage.all.each do |package| %>

<li><%= package.title %> <%= package.price %></li><% end %></ul>

data

Page 29: How to disassemble one monster app into an ecosystem of 30

Why doesn’t this break the rule of loose coupling?

Model:class CoursePackage < ActiveRecord::Base

acts_as_readonly :courseend

View:<ul><% CoursePackage.all.each do |package| %>

<li><%= package.title %> <%= package.price %></li><% end %></ul>

data

Page 30: How to disassemble one monster app into an ecosystem of 30

acts_as_readonly in Depth

def acts_as_readonly(name, options = {})

config = CoreService.app(name).database

establish_connection config[Rails.env]

set_table_name(self.connection.current_database + (options[:table_name]||table_name).to_s)

end

data

Page 31: How to disassemble one monster app into an ecosystem of 30

acts_as_readonly in Depth

def acts_as_readonly(name, options = {})

config = CoreService.app(name).database

establish_connection config[Rails.env]

set_table_name(self.connection.current_database + (options[:table_name]||table_name).to_s)

end

data

Page 32: How to disassemble one monster app into an ecosystem of 30

Core service

class CoreService < ActiveResource::Base

self.site = :user

def self.app(app_name)

CoreService.find(app_name)

end

end

data

Page 33: How to disassemble one monster app into an ecosystem of 30

Centralized configuration

data

How does Core know all the configurations?

Page 34: How to disassemble one monster app into an ecosystem of 30

Each app posts its configuration to core when it is started

data

Page 35: How to disassemble one monster app into an ecosystem of 30

data

config/site_config.yml

app: course

api:

course_list: package/courses

config/initializers/idp_initializer.rb

CoreService.reset_config

Page 36: How to disassemble one monster app into an ecosystem of 30

data

core_service.rb in idp_lib

APP_CONFIG = YAML.load(Rails.root.join(“config/site_config.yml”))

def reset_config

self.post(:reset_config, :app => {

:name => APP_CONFIG["app"],

:settings => APP_CONFIG,

:database => YAML.load_file(

Rails.root.join("config/database.yml"))})

end

Page 37: How to disassemble one monster app into an ecosystem of 30

data

Model in Purchase:class CoursePackage < ActiveRecord::Base

acts_as_readonly :courseend

Page 38: How to disassemble one monster app into an ecosystem of 30

Again, implemented in gem

data

config/environment.rb

config.gem ‘idp_helpers’

config.gem ‘idp_lib’

Page 39: How to disassemble one monster app into an ecosystem of 30

data

gems

Page 40: How to disassemble one monster app into an ecosystem of 30

• Web services for “write” interactions

class CoursePackageService < ActiveSupport::Base

self.site = :course

end

data

Page 41: How to disassemble one monster app into an ecosystem of 30

example

Roadmap needs to be generated after learner pays.

data

Page 42: How to disassemble one monster app into an ecosystem of 30

codesCourse: app/controllers/roadmap_services_controller.rb

def create

Roadmap.generate(params[:user_id], params[:course_id])

end

Purchase: app/models/roadmap_service.rb

class RoadmapService < ActiveSupport::Base

self.site = :course

end

Purchase: app/models/order.rb

def activate_roadmap

RoadmapService.create(self.user_id, self.course_id)

end

data

Page 43: How to disassemble one monster app into an ecosystem of 30

AJAX Loaded Composite Viewdata

<div>

<%= ajax_load(url_of(:course, :course_list)) %>

</div>

Fetched from different

applications

Ecosystem url_for

Page 44: How to disassemble one monster app into an ecosystem of 30

Open Source

http://github.com/idapted/eco_apps

data

Page 45: How to disassemble one monster app into an ecosystem of 30

interface data user

Page 46: How to disassemble one monster app into an ecosystem of 30

Features of User Service

• Registration/login

• Profile management

• Role Based Access Control

user

Page 47: How to disassemble one monster app into an ecosystem of 30

Access Control

Each Controller is one Node

user

* Posted to user service when app starts

Page 48: How to disassemble one monster app into an ecosystem of 30

Access Control

before_filter :check_access_right

def check_access_right

unless xml_request? or inner_request?

access_denied unless has_page_right?(params[:controller])

end

end

user

* Design your apps so access control can be by controller!

Page 49: How to disassemble one monster app into an ecosystem of 30

How to share?

user

Page 50: How to disassemble one monster app into an ecosystem of 30

Step 1: User Auth

SSO

Shared Session

Same Session Store

user

Page 51: How to disassemble one monster app into an ecosystem of 30

Step 1: User Auth

config/initializers/idp_initializer.rb

ActionController::Base.session_store = :active_record_store

ActiveRecord::SessionStore::Session.acts_as_remote :user,

:readonly => false

user

Page 52: How to disassemble one monster app into an ecosystem of 30

Step 2: Access Control

Tell core its controllers structure

CoreService. reset_rights

def self.reset_rights

data = load_controller_structure

self.post(:reset_rights, :data => data)

end

user

Page 53: How to disassemble one monster app into an ecosystem of 30

Step 2: Access Control

before_filter :check_access_right

def check_access_right

unless xml_request? or inner_request?

access_denied unless has_page_right?(params[:controller])

end

end

user

Page 54: How to disassemble one monster app into an ecosystem of 30

Step 2: Access Control

has_page_right?

Readonly db conn again

user

Page 55: How to disassemble one monster app into an ecosystem of 30

Step 2: Access Control

def has_page_right?(page)

roles = current_user.roles

roles_of_page = IdpRoleRight.all(:conditions => ["path = ?", page]).map(&:role_id)

(roles - (roles - roles_of_page)).size > 0

end

class IdpRoleRight < ActiveRecord::Base

acts_as_readonly :user, :table_name => "role_rights"

end

user

Page 56: How to disassemble one monster app into an ecosystem of 30

Again, gems!user

config/environment.rb

config.gem ‘idp_helpers’

config.gem ‘idp_lib’

config.gem ‘idp_core’

Page 57: How to disassemble one monster app into an ecosystem of 30

interface servicedata user

Page 58: How to disassemble one monster app into an ecosystem of 30

Support applications

• File

• Mail

• Comet service

service

Page 59: How to disassemble one monster app into an ecosystem of 30

File

class Article < ActiveRecord::Basehas_files

end

@article.files.first.url

Upload File in Background to

FileService

Store with app_name,

model_name, model_id

Use readonlymagic to easily

display

Idp_file_form

Specify Class that Has Files

Page 60: How to disassemble one monster app into an ecosystem of 30

Comet

service

class ChatRoom < ActiveRecord::Baseacts_as_realtime

end

<%= realtime_for(@chat_room, current_user.login) %>

<%= realtime_data(dom_id, :add, :top) %>

@chat_room.realtime_channel.broadcast(“hi world", current_user.login)

Page 61: How to disassemble one monster app into an ecosystem of 30

mail

Mail services

MailService.send(“[email protected]”, :welcome,

:user => “test”)

service

Page 62: How to disassemble one monster app into an ecosystem of 30

Host all in one domain

Load each rails app into a subdir, we use Unicorn

unicorn_rails --path /userunicorn_rails --path /studycenterunicorn_rails --path /scenario

Page 63: How to disassemble one monster app into an ecosystem of 30

Host all in one domain

use Nginx as a reverse proxy

location /user {proxy_pass http://rails_app_user;

}

location /studycenter {proxy_pass http://rails_app_studycenter;

}

Page 64: How to disassemble one monster app into an ecosystem of 30

Host all in one domain

All you see is a uniform URL

www.eqenglish.com/user

www.eqenglish.com/studycenterwww.eqenglish.com/scenario

Page 65: How to disassemble one monster app into an ecosystem of 30
Page 66: How to disassemble one monster app into an ecosystem of 30

Pair-deploy

Page 67: How to disassemble one monster app into an ecosystem of 30

How to split one into many?

Page 68: How to disassemble one monster app into an ecosystem of 30

By Story

Each App is one group of similar features.

By DataEach App writes to the same data

Page 69: How to disassemble one monster app into an ecosystem of 30

Example

• User Management

• Course package

• Purchase

• Learning process

• …

Page 70: How to disassemble one monster app into an ecosystem of 30

Iteration

Page 71: How to disassemble one monster app into an ecosystem of 30

Be adventurous at the beginning.

Split one into as many as you think is sensitive

Page 72: How to disassemble one monster app into an ecosystem of 30

Then you may find

• Some applications interact with each other frequently.

• Lots of messy and low efficiency code to deal with interacting.

Page 73: How to disassemble one monster app into an ecosystem of 30

Merge them into one.

Page 74: How to disassemble one monster app into an ecosystem of 30

Measurement

• Critical and core task of single app should not call services of others.

• One doesn’t need to know much about others’ business to do one task (or develop).

• Independent Stories

Page 75: How to disassemble one monster app into an ecosystem of 30

Pitfalls

• Applications need to be on the

same intranet.

• No “right place” for certain cases.

Page 76: How to disassemble one monster app into an ecosystem of 30

Results: Higher Productivity

-Faster build to deploy-More developer autonomy-Safer- Scalable-Easier to “jump in”- Greater Happiness

Page 77: How to disassemble one monster app into an ecosystem of 30

Support Tech

• FreeSWITCH

• VoIP

• http://www.freeswitch.org.cn

• Flex

• Erlang

• concurrent tasks

• Titanium

• mobile

Page 78: How to disassemble one monster app into an ecosystem of 30

Full Stack of Us

Rails/FlexCTO

VoIP UI SystemUE

Page 79: How to disassemble one monster app into an ecosystem of 30

Full Stack of Us

Develop environment:

• 4 mbp + 4 black apples + 1 linux

• textmate & netbeans

Servers:• test: 2 physical pc with Xen serving 10

virtual servers

• production: 2 (China) + 5(US)

Page 80: How to disassemble one monster app into an ecosystem of 30

Full Stack of Us

Web Server:

• nginx + unicorn

Rails:

• Rails version 2.3.4

• Ruby version: 1.8.7

Page 81: How to disassemble one monster app into an ecosystem of 30

Full Stack of Us

Plugins:

• exception_notification

• will_paginate

• in_place_editor_plus

• acts_as_list

• open_flash_chart

• Spawn + workling

Page 82: How to disassemble one monster app into an ecosystem of 30

Full Stack of Us

Gems:

• rspec

• factory_girl

• thinking-sphinx

DB:

• mysql

Cache:

• memcache

Page 83: How to disassemble one monster app into an ecosystem of 30

Full Stack of Us

Test:

• rspec + factory_girl

• Autospec

Deploy:

• webistrano

Page 84: How to disassemble one monster app into an ecosystem of 30

Full Stack of Us

Team Build:

• board

• code review

• tea break

• retreat

Page 85: How to disassemble one monster app into an ecosystem of 30

Join Us

If you are:

• smart

• creative

• willing to do great things with great people

• happen to know JS/Flex/Rails

Send whatever can prove your ability to:

[email protected]

Page 86: How to disassemble one monster app into an ecosystem of 30

© 2010 Idapted, Ltd.

Thank you!

Q&A

http://developer.idapted.com

[email protected] (@jpalley)

[email protected] (@fiyuer)