My team is debating how to transition a portion of our Rails application to allow "drafts". For simplicities sake, we can imagine a Post object where we want to allow a kind of draft mode.

Our real model contains, say, roughly 20 fields and 5 has_many relationships. There are some reasonably complex business rules (i.e., content can't be changed after a Post is delivered), and a state machine (Posts are pending, sending, then delivered).

class Post
    string   content
    enum     content_type
    date     deliver_on
    enum     delivery_state

    has_many customer_group_segments

Two options being considered, but we are open to more. One is to duplicate all the existing tables (i.e., post_drafts, customer_group_segment_drafts, etc). The second is to include a kind of is_draft field on the post.

The first option would separate all the data, but we'd need to consider sharing business logic between all models. And in the case of the draft, all of the validation rules would be optional until you tried to convert a Draft to a Post. And we'd also need to create a kind of transfer mechanism, etc...

The second option would remove all constraints from the underlying tables. We'd also need to make some kind of mode system, to ensure validation only kicks in where appropriate. And we'd also need to implement various checks to ensure, for example, that we don't try and deliver a draft.

There is a similar, but older question. I'm curious of there are any new opinions. Any ideas, observations, or working examples would be great!

  • I favor the latter approach, for several reasons. – Robert Harvey Apr 19 '19 at 20:52
  • 2
    In general, I dislike "staging tables" (they sort of break the "single source of truth" principle) and the complexity they bring to the table (which you have already alluded to), but sometimes you need to do it that way, for various reasons. – Robert Harvey Apr 19 '19 at 21:03
  • 1
    Could you link to the similar, older question? What makes you think things might have changed? – jonrsharpe Apr 19 '19 at 21:44
  • 4
    Wouldn't it make sense to see "draft" as an additional state in the delivery state machine? If it is somewhat orthogonal, for example if you can have draft in pending and sending states, you could have states pending_draft and sending_draft. – Hans-Martin Mosner Apr 20 '19 at 6:00
  • 1
    This sort of integrity worth having it modelled on the application side instead of the database. DB constraints should be balanced as well. Too many constraints make our designs more rigid too. If you need different reports or queries for one or another state, you can implement views too, instead of implementing new tables. I would start little and simple and move to dup structures only if whatever I do is proven not to be enough. @Christophe gave a good pattern to start with. If you need traceability between states, the memento pattern also helps. – Laiv Apr 23 '19 at 14:03

The simplest alternative would be the is_draft property approach, since you would benefit of all the existing code, and a simpler maintenance, the day you would want to add new fields or behaviors to the record.

However, this implies that you would apply all your business rules also to draft records. And sometimes the purpose of the draft is to be more permissive than the final version (e.g. missing information). So, if your draft would not obey the same business rules and behave even slightly differently, you would need to add a lot of extra conditional clauses in a lot if places. This is what makes a separate clone of the class appealing. But the risk of painful redundant maintenance is real. So think twice before going that way.

Therefore, in the case where the simple is_draft is not sufficient, instead of doubling the structures, I would suggest to consider the use of a draft state using the state design pattern.

Note: an is_draft column in a database table is a simplified implementation of a draft state class using the single table inheritance approach

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.