02 - The Problem
03 - The Solution
04 - DAMP BNLs
05 - Multiple Contexts
The Problem
To show the value of Business Natural Languages we will start with an existing ruby application and then introduce the Business Natural Language. This approach should provide a good example of how to include a Business Natural Language in an application.
For the sample application imagine you've been contracted to replace a bonus calculation system for a pharmacy. The pharmacy chose to custom develop the software for calculating bonuses because of the varying bonus packages. They have given you profiles of 3 employees and the business rules required to generate bonus compensation.
Profiles:
John Jones
bonuses: $10,000 if the company posts a profit of 1 million
$20,000 if the company posts a profit of 2 million
$30,000 if the company posts a profit of 3 million or more
5% of toothbrush profits
bonus pay month: January
Jackie Johnson
bonuses: 1% of profits
2% of drug profits
bonus pay month: February
Joe Noone
bonuses: 1% of profits or $10,000, whichever is greater
bonus pay month: March
The application need only be run monthly; therefore it is okay if the process is executed manually. The existing application is executed as a rake task. It also has every employee's information hard-coded as rules in the application. Last years profit information is available in the data warehouse. For our purposes lets add some dummy warehouse data. This migration should give you the all the data you will need:
class Profit < ActiveRecord::BaseThe rake task that executes the current payroll process is pretty straightforward:
end
class WarehouseData < ActiveRecord::Migration
def self.up
create_table :profits do |table|
table.column :toothbrush_in_cents, :integer
table.column :drug_in_cents, :integer
end
Profit.new(:toothbrush_in_cents=>125050000, :drug_in_cents=>410040050).save
end
def self.down
drop_table :profits
end
end
File: lib/tasks/payroll.rake
namespace :payroll doEach employee ruby file is also fairly straightforward. They contain the logic to calculate a bonus, expressed as ruby.
desc "run payroll"
task :run => :environment do
output_file = "#{File.expand_path(File.dirname(__FILE__))}/../../tmp/#{Time.now.year}-#{Time.now.month}-bonus.log"
File.open(output_file,'w') {}
[JohnJones, JackieJohnson, JoeNoone].each do |employee|
employee.append_bonus_to(output_file)
end
end
end
File: app/models/john_jones.rb
class JohnJonesFile: app/models/jackie_johnson.rb
def self.append_bonus_to(bonus_log)
if Time.now.month == 7
profit = Profit.find(:first)
bonus = profit.total > 100000000 ? 1000000 : 0
bonus += 1000000 if profit.total > 200000000
bonus += 1000000 if profit.total > 300000000
bonus += profit.toothbrush_in_cents * 0.05
File.open(bonus_log,'a') do |file|
file << "Jim Jones bonus: #{bonus.round} cents\n"
end
end
end
end
class JackieJohnsonFile: app/models/joe_noone.rb
def self.append_bonus_to(bonus_log)
if Time.now.month == 7
profit = Profit.find(:first)
bonus = profit.total * 0.01
bonus += profit.drug_in_cents * 0.02
File.open(bonus_log,'a') do |file|
file << "Jackie Johnson bonus: #{bonus.round} cents\n"
end
end
end
end
class JoeNooneProfit is a simple ActiveRecord::Base inheritor with total being it's only behavior
def self.append_bonus_to(bonus_log)
if Time.now.month == 7
profit = Profit.find(:first)
bonus = (profit.total * 0.01).round
File.open(bonus_log,'a') do |file|
file << "Joe Noone bonus: #{bonus > 1000000 ? bonus : 1000000} cents\n"
end
end
end
end
File: app/models/profit.rb
class Profit < ActiveRecord::BaseThe existing application works; however, the level of maintainability is low. Unfortunately, employees are required to re-negotiate their bonus structure every year. Following these negotiations a programmer must be involved to change the employees bonus logic. Also, any time a new employee is hired, a new file must be introduced into the system to calculate that employee's bonus.
def total
toothbrush_in_cents + drug_in_cents
end
end
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.