Data Modeling Intermediate

How to Model a Feature So a Future Shared Library Needs No Rework

Model authorship, ownership, sharing, provenance, and assignments so a future shared library can be added without restructuring existing data.

1–2 hours Intermediate Octacer Engineering August 11, 2026
A data model with two spare columns reserved for a future shared library.

How to Model a Feature So a Future Shared Library Needs No Rework

[[IMAGE: hero | Two disjointed feature flows: on the left an author creates content in isolation on a single screen, on the right an assignment requires that same content to be shared, but the data model has no way to express ownership or sharing — the engineer is blocked mid-architecture between the two]]]

Overview

You are designing a feature where one person authors content and another person receives it as an assignment. Today, that content lives in one workspace, belongs to one user, and is delivered to one recipient. You know — or strongly suspect — that the client will soon ask for a shared library that lets multiple teams author, reuse, and assign the same content across workspaces.

The mistake most designs make is treating the current single-author, single-assignment flow as the complete data model. The model gets shaped around "who created this" and "who is this assigned to" as if both facts belonged on the same record and only one value was possible. When the shared library arrives, that model requires a migration that touches every existing record, every query, every API response, and every frontend component that reads those fields.

The alternative is to design the schema now so the future library is additive: the existing tables and queries keep working, and the shared library is a new layer that references them without restructuring them.

This guide covers three design decisions:

  1. Reuse the closest existing pattern in the system rather than inventing a new one.
  2. Add ownership and sharing columns now, even though the UI only needs a single author today.
  3. Keep authorship and assignment as separate records with separate lifecycle states.

Prerequisites

Before you begin modeling, confirm the following are available:

Tools

A database migration tool (e.g., the one already used in the project)
Access to the schema definitions for the content type you are extending
A way to represent entity relationships, either in code or in a schema diagram tool

Access

Write access to the migration files or schema definitions
The ability to run a migration against a development database
Review access to the current content model and any existing assignment or sharing models

Data inputs

The existing schema for the content type being authored (for example, documents, campaigns, templates)
A list of the fields the current authoring flow writes
A list of the fields the current assignment flow reads
Any existing pattern in the system that already handles shared resources or multi-owner records

Knowledge

Basic familiarity with the database migration workflow used by the project
Understanding of the difference between a foreign key, a join table, and an ownership column

Steps

Step 1 — Identify the closest existing sharing or ownership pattern in the system

  1. 1

    Identify existing pattern

    Before you write any schema, check whether the system already models a resource that is owned by one entity but visible to or usable by others. Common examples include:

  2. 2

    Define authorship fields

    At minimum, model:

  3. 3

    Add sharing columns

    Do not use these columns in the current UI. Leave the default values set to the authoring user and private visibility. This keeps the current feature behavior unchanged while preparing the schema.

  4. 4

    Model assignment record

    An assignment record should include:

  5. 5

    Draft library table

    library_id
    content_id — references the content record created in Step 2
    added_by
    added_at
    visibility — inherits from the content visibility column
    category or tags

  6. 6

    Validate model

    Run through the future scenarios and confirm the model handles them without rework:

  • A shared calendar where multiple users can create events
  • A team folder where files have an owner but a team has access
  • A project where tasks have an assignee but the project belongs to an organization

If a pattern exists, reuse its conventions: the same column names, the same way of representing ownership (owner ID, owner type), the same approach to access (a permissions table, an access-control list, a share record). Consistency matters more than perfect design. A future engineer reading the schema should be able to recognize the sharing pattern immediately because it already exists elsewhere.

If no such pattern exists, look for the closest structural analog: any record that has both a creator and a wider audience. Use that as the template.

Expected result: You have identified either an existing pattern to mirror or a stated reason why no pattern exists and a deliberate choice for how ownership will be represented.

Step 2 — Define the content record with authorship as its own field set

The content record (the document, template, or asset being authored) needs to store who authored it. Model authorship as fields on the content record itself, but make them fields that can later support multiple contributors without breaking existing readers.

  • author_id — the user who created the content
  • author_type — the entity type that authored it (useful if content can later be authored by a team or a system, not just a user)
  • created_at — when the content was created
  • updated_at — when the content was last modified

The key decision is to keep author_id as an owner reference, not as a comment-only field. The current UI only needs one author, so this is a single-value relationship today. But by defining it as a reference with an author_type, you leave room for the content to belong to a team without renaming columns later.

Do not conflate authorship with assignment. The content record should not contain an "assigned_to" field. Assignment is a relationship between the content and a recipient, and it changes over time. Authorship does not change.

Step 3 — Add sharing and provenance columns to the content record now

Add the columns that a shared library will need, even though the current feature does not use them. These columns cost nothing now and prevent a migration later.

Add at least:

  • visibility — controls whether the content is private, shared within a workspace, or public to the library
  • owner_id and owner_type — the entity that owns the content for sharing and permission purposes; this may be the same as the author today, but is conceptually distinct
  • source_id and source_type — provenance fields that record where content came from, so that when content is reused from a library, the original source is traceable
ALTER TABLE content
	ADD COLUMN visibility VARCHAR(20) NOT NULL DEFAULT 'private',
	ADD COLUMN owner_id UUID,
	ADD COLUMN owner_type VARCHAR(50),
	ADD COLUMN source_id UUID,
	ADD COLUMN source_type VARCHAR(50);

Set owner_id and owner_type to the author values as part of the migration backfill so existing rows have a valid owner.

Step 4 — Model assignment as a separate record

Create an assignment record that is distinct from the content record. The assignment record captures the relationship between content and a recipient, along with assignment-specific state.

  • id
  • content_id — the content being assigned
  • assignee_id — the recipient
  • assigned_at
  • due_at
  • status — for example, pending, accepted, completed

This separation matters for two reasons. First, the same content can be assigned to multiple recipients, which is a natural requirement once a shared library exists — one library item can be assigned to many teams. Second, the assignment lifecycle is independent of the content lifecycle. Content can be revised while assignments referencing it remain valid.

Because authorship and assignment live on separate records, adding the shared library later requires only a new table that references content — not changes to the assignment table.

Step 5 — Define the future shared library table without creating it

Design the shared library table on paper, but do not create it yet. The purpose of this step is to verify that the schema you have designed supports the library additively.

A shared library record would likely look like:

The critical check: every field in this table references existing records or stands alone. Nothing in the library table requires you to modify the content table, the assignment table, or any existing query. The library is a pure addition.

Expected result: You can write the schema for the library table using only references to columns that already exist in your design.

Step 6 — Validate the model against the future requirement

  • A user authors content, and it remains private. The content record has visibility = private, author_id set, and owner_id set to the same user.
  • The user shares content to the library. A library record is created referencing the content. No content fields change.
  • Another team assigns library content to their members. Assignment records are created referencing the content ID. The original content record is untouched.
  • The content is reused in a new workspace. The new workspace creates a new content record with source_id pointing to the original content. Provenance is preserved.

If any of these scenarios require a column rename, a new required field on an existing table, or a change to existing queries, the model needs adjustment. Walk back through Steps 2 through 4 until all scenarios are additive.

Configuration

Three configuration decisions materially affect how well this model holds up when the shared library arrives.

Owner type

The owner_type column should default to user but be able to hold team or organization. The cost of adding this column now is one string field. The cost of adding it later is a migration across all existing content records, plus updates to every query that filters content by owner. Set the default to the authoring user so that the current behavior is unchanged, and leave the column unused until the library exists.

Visibility defaults

Default visibility = private. The current feature only ever reads content authored by the current user, so private is the correct default. When the library arrives, the visibility field will control which content appears in the shared context. Starting private and making sharing an explicit act is the safer path, because it means no existing content is exposed accidentally when the library feature is released.

Status transitions

The assignment record has a status field. The current UI may only need pending and completed. Do not build a state machine with transitions, validations, and side effects until the assignment behavior requires it. The column exists; the behavior around it can grow later. Premature state-machine logic will need rework when real assignment workflows emerge.

Verification

Verify the model by testing that the future library requirement is met without touching existing structures.

Functional check

Create a content record with visibility = private and the current user as both author_id and owner_id.
Confirm the existing authoring flow can read and update this record without reference to the new columns.
Simulate a future library operation by inserting a record into a temporary table that references content_id and points to the content record created in step 1.
Confirm the content record remains readable by the assignment flow after the library insert.

Data check

Query the content table for all rows where visibility = 'private'.
Confirm the query returns only content where the current user is the owner_id.
Confirm the source_id and source_type columns are null for originally authored content.

Failure check

Attempt to create an assignment referencing a content ID that does not exist.
Confirm the foreign key constraint rejects the insert.
Confirm the content table has no assignment-related columns, proving the separation holds.

Repeatability check

Run the content creation flow twice.
Confirm two distinct content records exist, each with its own author and owner.
Confirm no shared or global state was mutated by either run.

Troubleshooting

The existing assignment flow reads a field from the content record that I moved

If you moved an assignment-related field onto the content record in an earlier version of the schema, the assignment flow will break when you separate the models. Check all queries and views that reference the content table for fields like assigned_to, due_date, or status. These belong on the assignment record. Migrate the data to the assignment table and update the queries to join on content_id.

The content record has multiple authors but author_id is a single value

The model assumes single authorship today and multi-owner content later. Do not turn author_id into a list. If multiple authors become necessary, the correct extension is a join table between content and users, or the use of owner_type to reference a team. Changing author_id to an array or a comma-separated value will break every existing query and index.

The future library requires a field I did not anticipate

This is the normal situation. The design goal is not to predict every field. It is to ensure that adding a field to the library table, or adding a new table, does not require modifying existing tables. If the new requirement fits inside a new table or a new column on the library table, the model is holding up. If it forces a change to the content or assignment tables, return to Step 3 and reconsider what belongs on the shared core record.

Production checklist

  • [ ] visibility defaults to private for all new content.
  • [ ] owner_id and owner_type are backfilled to the author for all existing content records.
  • [ ] source_id and source_type are nullable and null for originally authored content.
  • [ ] Assignment data lives only in the assignment table, with no assignment fields on the content table.
  • [ ] The existing authoring and assignment queries run unchanged against the migrated schema.
  • [ ] A foreign key enforces that assignments reference valid content records.
  • [ ] No code path writes to visibility, owner_id, owner_type, source_id, or source_type from the current feature.
  • [ ] The schema for the future library table has been drafted and references only existing tables and columns.
  • [ ] A rollback migration exists that drops the new columns without data loss for existing records.
  • [ ] The team has agreed on the naming conventions for ownership and provenance fields so the library feature uses the same vocabulary.

Ready to Implement This Guide?

Our team can implement these strategies for you, tailored to your specific business needs.

Schedule Consultation