How do you do it?

Delegated Types and and building the ultimate Kanban Board in Rails

July 09, 2024 09:00

We're getting ready to work on our Kanban Board feature. It's exciting as we haven't started a "big" feature in a while.

As we embark on this, we began to think about how to model the database around it. We have a couple of constraints within Avo's context, which makes it a bit more challenging.

1. We don't own the records/models that sit on the board

This is the ethos of Avo; the user can have Users, Products, ToDos, and any other kind of models added to the board. We don't own those models and can't dictate the design decisions around them.

2. We don't want to enforce database restrictions (or have as few as possible)

Another big feature of Avo is that you configure it with code and not by database rows. This is powerful because all your configuration is very portable across all environments. This means we don't ship any migrations with Avo and, again, don't impose database restrictions on you.

So how do we keep track of which item is on which board/column?

3. We want multi-item-type boards

We'd love to support boards that handle multiple item types, meaning you could have a User, a Product, and a To-Do on the same board and work with all of them as if they were the same item.

4. Make it as configurable as possible

We want this to be the ultimate Kanban board feature out there. Maybe not from the initial iteration, but still be able to easily add features as we progress.

How Do We Do It?

It's an interesting challenge, I think.

Initial Iteration

We first thought about the simplest use case. We would require the user to add a column to each record type in the database to indicate which column that record is placed in.

That quickly becomes problematic if you want that record in multiple columns.

User
- id
- name
- kanban_column # add this to hold the board it is on

This starts to look like Single Table Inheritance.

You also need to hold the position on the board. Adding another database column? What if you need to add another property? It starts getting messy really quickly.

Second Iteration

Then, we thought about holding the position and board information in a different table. This is great as it doesn't impact the user's database modeling. We can "do what we want there" and not bother the user.

User
- id
- name

KanbanItem
- record_type ("User")
- record_id (55)
- column ("In progress") # which column
- position (2) # position in the column

This is already better. But what does it look like? That's right! It looks like Delegated Types.

Delegated Types are a great abstraction for holding common data for multiple types of models. They have great heuristics and are proven and tested.

It checks all the boxes:

  • We don't need to apply any updates to the user's model database structure.
  • We can have the same record sit on multiple boards.
  • This future-proofs this feature so we can customize it as we want.

We still have a few things to look into and test out, but the "meat" of the feature is there!

How would you build this feature?