FireUX · 5 min

Not Every Create Flow Should Use the Same Architecture

Classify create flows by where irreversible work happens so simple records, drafts, uploads, onboarding and provider work stay safe.

Not Every Create Flow Should Use the Same Architecture

Many application features begin with the same verb: create.

Create a profile. Create a product. Create a workspace target. Create a record from an uploaded document. Create something in an external payment system.

The interfaces may all end with a button labelled Create, but the underlying work is not one architectural family. Treating it as one usually produces a generic composable with enough options to hide where authority, files, secrets and irreversible side effects actually live.

The question we now ask first at FireUX is:

What is being created, and where does the irreversible work happen?

That answer determines the flow.

Simple Entity creation

A client-side Entity create is appropriate when the caller already has the required identity and context, and the work is primarily one authorized data write.

The component gathers input. A domain-owned create composable validates and shapes it. The composable calls a typed Client Create target or a narrow trusted server seam. The resulting record belongs to the Entity that defines its lifecycle.

This pattern should remain boring. It should not acquire upload handling, provider secrets or multi-record transaction options merely because another feature also uses the word create.

Draft-state creation

Some objects need writing, previewing and revision before any canonical record should exist. A blog article or multi-section product form may have several editors that share one staged object.

Here, local draft state is a real product need. Write and preview surfaces use the same draft, while one submit boundary converts it into the final command. Create and publish may be separate actions.

Draft state is less useful for a tiny one-step form. If the staged object is more complicated than the record it eventually produces, the abstraction is working against the product.

Workspace onboarding

Creating an Entity that can be selected in an authorized Workspace is not just adding a root document. The trusted boundary may need to create or reuse the root and establish the caller's exact Team authority in one transaction.

These flows are often idempotent. A person may select the same real place or business more than once, and the correct outcome is to return the existing target rather than produce a duplicate.

The domain still owns the command. Workspace provides the surface and selected context; it does not become the storage owner.

Server transaction creation

Move creation to a trusted server endpoint when it involves multipart uploads, file storage, download tokens, server credentials or several writes that must succeed together.

The endpoint owns the transport boundary: read and validate the request, perform server-only work, write the resulting records and return the bounded result.

A useful example is document-driven creation. Uploading a source file and creating a doorway record is not equivalent to creating an ordinary domain-scoped record from form fields. Reusing the ordinary client composable would hide file ownership and partial-failure handling.

Provider and provisioning creation

Some flows create state in a third-party system and then store a related local representation. Payment products, checkout sessions and provider-enriched records are examples.

This family needs an explicit answer for ordering and recovery. What happens if the provider succeeds but the local write fails? Which identifier is canonical? Can a retry detect the existing provider object?

Those questions are part of the create contract, not error-handling details to add later.

Ensure and upsert

Sometimes the real intent is “make sure this exists.” If the target has a natural identifier, repeating the command should not create another record.

An ensure flow normalizes the identity, reads the current record and returns an explicit created-or-existing outcome. It is valuable for onboarding, reconciliation and membership setup, where retries are normal rather than exceptional.

Calling this an ordinary create can lead a UI or agent to assume that every successful request produced something new.

A practical classification test

Before implementing a create flow, answer these questions:

  1. Does a canonical record need to exist before the person commits?
  2. Is the target identity generated or already known?
  3. Must retries return the same result without duplication?
  4. Are files, secrets or external providers involved?
  5. Must several writes succeed atomically?
  6. Which domain owns validation and the resulting lifecycle?
  7. Is the interface creating one record, or orchestrating a larger transaction?

The answers usually reveal the family without requiring a universal create engine.

What we now do at FireUX

FireUX distinguishes client Entity create, draft-state create, Workspace onboarding, server transaction create, provider provisioning and ensure/upsert flows. Each uses the smallest transport and state model its product job needs.

Firestore mutations enter through typed Client, Server or MCP/Skill lanes. Pages, Workspaces, tools and AI agents consume those lanes; they do not receive separate raw database authority.

This classification works with our broader rule to build a second real example before extracting an abstraction and our hard-cutover approach to Entity migrations.

Future tasks may use this article as orientation, but they must still inspect current source, guards and living context before changing a create flow.

The lesson is not that creation must be complicated. It is that the same button label should not erase materially different authority and failure boundaries.