Topic: To inherit or not?

I'm having some difficulties choosing a good design for a fairly simple simple use case. The use case is as follows:

A sales agent writes a proposal for a client for let's say some kind of pension savings plan. The pension savings plan is one of the products offered by a certain company or a just a bank. As soon as the compony or bank approves the proposal, then it becomes a policy with a unique number. Finally once the client has payed, the proposal becomes an official contract.

My first thought was just to use three classes in AR like this:

Proposal < AR::Base
Policy < AR::Base
Contract < AR:Base

I don't like the fact that a proposal will have have also a dynamically created read attribute methode for the column "policy_number" that belongs only to Policy and Contract. The same logic applies for the column "payment_date" for the date of the first payment.

It becomes really problematic when a contract can become a policy again because the client didn't pay his monthly fee. You would not want to copy almost all the attributes of a Contract object to a Policy object, destroy the Contract object and create the new Policy object.

Another way to implement the use case without using inheritance at all, would be to let Policy be an :has_one :policy association of Proposal and similar Contract be a :has_one :contract association of Policy.

I would solve this kind of problem normally with using the Decorator of State pattern from the GoF. With the Decorator pattern you can add and remove functionality as you like with changing other objects. The State pattern will look like if a Policy object can change class to an Contract object.

But mixing one of these two patterns with persistence, makes my head spin. So does anyone have some suggestions for how to implement this?

Best regards,

Mark Noten

Re: To inherit or not?

How different are the three models? Does the behavior/attributes change drastically when it changes state?

Another question, do you need to keep track of when the state changed, who changed the state, etc? If the state reverts back to Policy, then back to Contract, do you need to keep track of this change so you can look back on the history?

Lastly, are you planning on doing a RESTful design? This problem reminds me of something DHH mentioned in his keynote (near the end of section 05). Watch it with the slides.

Railscasts - Free Ruby on Rails Screencasts

Re: To inherit or not?

First of all, I'm doing this project for a friend that helped in the past and to get some feeling with ROR. I will tell you some more about the use case to get a better insight. So please bare with me...

This friends acts like a mediator between sales agents and a bank or insurance company. For every product that he offers, he has a tree where each node is a sales agent. The tree is not that big but I still use the better nested set plugin from Krishna Dole with a scope on the column product_id wich is a FK. Ofcourse a sales agent can write more than one proposal. As mentioned earlier, there are three different stages that have to completed before a sales agent can get a commission (simplified):

1) He writes a proposal of a product for a client
2) The mediator sends the proposal to the bank or insurance company and they approve it by issuing a policy for the proposal with a number and date
3) The client pays the initial fee in a certain month and the policy becomes a real contract. For example: the client is now insured against fire for his appartment. The mediator gets a commission and he distributes the bigger par to the sales agent who wrote the initial proposal and a small part to the ancestors up the three.

This little project is all about generating a monthly sales report for each sales agent.

The three models Proposal, Policy and Contract are not that different for now because I want to keep things as simple as possible to start. But it is important that a contract can be revoked because the client stopped payment and effectively changes back to a policy. This is important because the bank or insurance company will ask their money back. The mediator ofcourse has to ask a part of the commission back from some of his sales agents. This doesn't happen that often but it's important to have a good model that can keep track of this. So a contract can go back to a policy and a policy can become a contract again.

For now, I have a table proposals that looks like this (for now, this is sufficient):

CREATE TABLE  `proposals` (
  `id` mediumint(8) unsigned NOT NULL auto_increment,
  `client_surname` varchar(64) NOT NULL,
  `client_firstname` varchar(64) NOT NULL,
  `street` varchar(128),
  `number` varchar(5),
  `box` varchar(5),
  `postal_code` NOT NULL,
  `city` varchar(128)NOT NULL,
  `agent_id` mediumint(8) unsigned NOT NULL,
  `written_on` date NOT NULL,
  `units` smallint(4) unsigned NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `FK_AGENT` (`agent_id`),
  CONSTRAINT `FK_AGENT` FOREIGN KEY (`agent_id`) REFERENCES `agents` (`id`) ON DELETE SET NULL ON UPDATE SET NULL
);

If I would use STI for this simple case, then I would just add the following columns to represent a Policy and a Contract:

type varchar(32) NOT NULL, <- STI type
policy_number mediumint(8) unsigned,
policy_approved_on date,
first_payment_on date <-- Contract

The behavior of the three classes is not really important.  It's just important to display proposals, policies or contracts seperately in the view together with the extra attributes for a proposal or contract.

But it's all about the state changes between a Contract and a Proposal. Because if it happened then it will show up in the final sales report.

I'm thinking about adding a FK to a table called states for the current state and a FK in the states table back to the proposals table to have a history. But then again, how can I keep the current state consistent with the class? Here I'm lost again because different states have different attributes. This would mean that I would also have a STI for the states table.

I would be happy for now if I just can make the transitions from proposal to policy and then contract. Should I hack the type column to avoid destroying a proposal and creating a policy with the same attributes plus the two extra ones?

I've went through the slides and I must say that for now I don't do use the RESTAPI yet.

Thanks for your reply,

Mark

Re: To inherit or not?

marknoten wrote:

I'm thinking about adding a FK to a table called states for the current state and a FK in the states table back to the proposals table to have a history. But then again, how can I keep the current state consistent with the class? Here I'm lost again because different states have different attributes. This would mean that I would also have a STI for the states table.

I think you are really close here. Here's what I'm thinking.

class Proposal < ActiveRecord::Base
  belongs_to :state
  has_many :states
end

class State < ActiveRecord::Base
  belongs_to :proposal
end

class Policy < State
end

class Contract < State
end


Here the proposals table would have a state_id column representing the current state. The states table would have type, proposal_id and any other columns the subclasses need. When the state changes, a new State record is created of the proper type. This way you can see a history in proposal.states, or you can simply fetch the current state with proposal.state. Any logic which is dependent upon the current state can just be passed to the state object. I suggest placing a created_at column in the states table to keep track of when the state changed.

The only thing I don't like about this approach is the duplication of the policy number, etc. You may want to keep this in the proposal table if a given proposal will always only have one.

If you end up with a lot of different attributes which aren't shared between the different states, you may want to use polymorphic association instead of STI. I don't think it would be as convenient as STI in this instance though.

You may want to rename the Proposal model to something else (like Negotiation) and make Proposal a State as well.

Railscasts - Free Ruby on Rails Screencasts

Re: To inherit or not?

Ryan,

first of all thanks for your quick reply. I'm dealing now with some kind of chicken-egg situation here.

When I first create a Proposal, I want to associate it with a initial state. I'm using one of the callback method "before_validation_on_create" in ActiveRecord:

class Proposal < ActiveRecord::Base
  belongs_to :state
  has_many :states
 
  validates_presence_of :state_id
  before_validation_on_create :init_state

  private
    def init_state
      change_state(Negotiation.new)
    end
 
    def change_state(state)
      Proposal.transaction(self) do
        self.state = state
        self.states << state
      end
    end
end


In the State parent class I'm using someting similar to initiliaze the creation on date:
class State < ActiveRecord::Base
  belongs_to :proposal
 
  validates_presence_of :proposal_id
  before_validation_on_create :record_creation
 
  private
    def record_creation
      self.created_on = Date.today
    end
end

But I'm having too many FKs in the database for this to work. Because of the current state and the history of states, their is some circular relation between the two. It will give an FK violation with a message like:

Mysql::Error: Cannot add or update a child row: a foreign key constraint fails (`delatool_development/states`, CONSTRAINT `FK_PROPOSAL` FOREIGN KEY (`proposal_id`) REFERENCES `proposals` (`id`)): INSERT INTO states (`created_on`, `type`, `proposal_id`) VALUES('2007-01-07', 'Negotiation', 0)

I want to create a initial state Negotiation for a Proposal but the state itself needs a reference to a Proposal because it has a FK. This ofcourse will never work...

Maybe the easy thing to do is to drop the FK constraint in the states table. So first create a Negotation state with an invalid value "proposal_id" column. After the creation of the Proposal, using the call_back method "after_create" can add the Negotation state to the states history by doing a self.states << state on the Proposal instance.

Maybe somebody has some better idea for it!

Best regards,

Mark

Re: To inherit or not?

The created_on column is sort of a magic name. Rails will automatically set it to the current date so you don't need to do that.

As for the Foreign Key constraint problem, I'm not sure what to tell you. Someone else may have a better suggestion. I prefer not to set constraints in the database and just set them through the Rails app. Of course if you plan to access the database through other means you may need these constraints.

Railscasts - Free Ruby on Rails Screencasts

Re: To inherit or not?

For now I dropped the FK constraint on the proposal_id column in the states table and it works fine. I already have a private method change_state with an transaction that will set the current state and at the same time add it to the history of states.

def change_state(state)
  Proposal.transaction(self) do
    self.state = state
    self.states << state
  end
end

This will ensure the consistency between the states and proposals table.

Thanks again,

Mark