# APPEALS-35631: Investigate feasibility of refactoring MembershipRequest work for reusability
## Purpose
Research, document and make recommendations on the question of whether the existing `membership_requests` table in Caseflow should be refactored for more general reuse by future feature work.
## Why should we abstract requests?
It is desirable to want to reuse and optimize code as much as possible. A candidate for optimization and reuse is the`membership_request` effort by Tyler in Feb-Apr 2023.
Why take the time to consider converting `membership_request` to inherit from a more general `request` in Caseflow?
Because it is more efficient and effective.
If we are going to be instantiating and using multiple types of requests in Caseflow, as is potentially the case in upcoming VHA work, we should take steps to organize the database structure, relationships, and code for optimal clarity, efficiency, and speed.
## Background Information
### Existing request implementation
#### `membership_request` Table
```
create_table "membership_requests", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "decided_at", comment: "The date and time when the deider user made a decision about the membership request"
t.bigint "decider_id", comment: "The user who decides the status of the membership request"
t.string "note", comment: "A note that provides additional context from the requestor about their request for access to the organization"
t.bigint "organization_id", comment: "The organization that the membership request is asking to join"
t.bigint "requestor_id", comment: "The User that is requesting access to the organization"
t.string "status", default: "assigned", null: false, comment: "The status of the membership request at any given point of time"
t.datetime "updated_at", null: false
t.index ["organization_id"], name: "index_membership_requests_on_organization_id"
t.index ["requestor_id"], name: "index_membership_requests_on_requestor_id"
t.index ["status", "organization_id", "requestor_id"], name: "index_membership_requests_on_status_and_association_ids", unique: true, where: "((status)::text = 'assigned'::text)"
end
```
#### `membership_request` Model
```
class MembershipRequest < CaseflowRecord
belongs_to :organization
belongs_to :requestor, class_name: "User"
belongs_to :decider, class_name: "User", optional: true
validates :status, :organization, :requestor, presence: true
validates :status, uniqueness: { scope: [:organization_id, :requestor_id], if: :assigned? }
before_save :set_decided_at
enum status: {
assigned: "assigned",
approved: "approved",
denied: "denied",
cancelled: "cancelled"
}
...
end
```
### Existing request implementation
_(Reassign Cases to CAMO 2.0 request details...)_
## Towards a Reusable Requests Table
In the upcoming Reassign Cases to CAMO 2.0 (RACC2) work, we will be implementing modification requests of some kind in order to track and process change requests to VHA issues (e.g. . Remove issue, Withdraw Issue, Add Issue)
It seems desirable, considering the existence of membership requests with much of the same structure and fields, to abstract the functionality to a general requests functionality.
## Possible Solutions for Reuse of Requests
### Inheritance
Inheritance, in object-oriented programming, enables new objects to take on the properties of existing objects. A class that is used as the basis for inheritance is called a superclass or base class. A class that inherits from a superclass is called a subclass or derived class.
In Rails, Single Table Inheritance (STI), Polymorphic Associations, and Delegated Types are three different inheritance approaches to modeling complex relationships between database tables/entities. The choice between STI, polymorphic associations, or delegated types depends on the specific requirements and design of your application.
#### Single Table Inheritance (STI):
STI is a technique where multiple models/classes are stored in a single database table.
It allows you to have different types of objects that share common attributes and behaviors but also have their own unique attributes and behaviors.
In the database table, there is usually a "type" column that indicates the specific subclass/model associated with each record.
This approach is useful when you have a hierarchy of related models that can be represented by a single table to avoid duplicating columns/fields.
#### Polymorphic Associations:
Polymorphic associations enable a model to belong to more than one other model on a single association.
It allows a model to be associated with multiple different models through a single association.
This is achieved by using two columns in the database table: one for storing the associated model's ID and another for storing the associated model's type.
Polymorphic associations are useful when you want a model to be able to associate with different types of models without creating separate associations for each type.
#### Delegated Types:
Delegated types provide a way to define different behaviors or attributes for a single model based on its type. It allows a model to delegate its behavior or attributes to another model based on a specific condition, typically the value of a "type" column.
This is accomplished by defining conditional logic in the model's code to delegate certain methods or attributes to another model based on the type. Delegated types are useful when you want a model to exhibit different behaviors or have different attributes depending on its type without resorting to multiple models or tables.
Delegated types are a part of ActiveRecord beginning in Rails 6.1 (Available in earlier versions via a backported gem).
### Composition
Composition is a technique by which classes may achieve polymorphic behavior and code reuse by containing other classes that implement the desired functionality instead of through inheritance. Ruby uses Modules and a concept called mixins. A Module is essentially a named, organized collection of methods. Mixins are the way in which we require these Modules into our Classes as needed.
## Conclusion
Based on the scenario of implementing a general Request model and functionality in Caseflow during the upcoming Reassign Cases to CAMO 2.0 feature work, and given the specifics of the situation, Composition will probably work better than inheritance. If inheritance is chosen, however, then delegated types will probably be preferable.
The scope of the problem and future needs is not well defined at this point, and there is not yet a strong case for inheritance, we would be better served at this point by taking a more principle-based approach like composition over inheritance.
Due to the unknown nature of future request-related work (specifically the hierarchy and data structure) it is unlikely there will be a strong case for using inheritance methods.
Until the next `request` need is defined and implemented, it is likely prematureto attempt to rewrite and reorganize the existing `membership_request` code and structure to fit future, unknown needs. The extent of the modification and optimzation effort would be significant enough to merit a large block of work that will need to be factored into any development effort.
Therefore, we should continue to solution the Reassign Cases to CAMO work with an eye towards the similarity and synthesis of the `membership_request` work, but not attempt to combine or optimize them in any way during the Epic.
## Recommendations
1. Build the `request` functionality of the Reassign Cases to CAMO 2.0 (RACC2) feature work using Composition (preferred) or Inheritance (delegated types) to optimize reusability for existing (e.g. `membership_request`) and future types of requests.
2. Learn from, and factor-in, the existing `membership_request` code when designing the more reusable request implementation for RACC2.
3. Do not attempt to refactor existing `membership_request` code into the new `request` code work as part of the RACC2 work. (Decouple for risk mitigation)
4. Create a technical debt/backlog ticket for the refactoring, optimizing and integrating of pre-existing `membership_request` code to be addressed and completed at a later date (possibly in the same PI as the RACC2 work).
---
### References
- [Composition Over Inheritance](https://thoughtbot.com/ruby-science/composition-over-inheritance.html#composition-over-inheritance)
- [robertomiranda/delegated_type: Backport of ActiveRecord::DelegatedType 6.1 to AR 5.x and 6.x. Delegate types is an alternative to single-table inheritance for representing class hierarchies.](https://github.com/robertomiranda/delegated_type)