Content model
SeeAMess keeps Optimizely’s useful idea: a stable identity, immutable versions, and a published pointer per language. It does not keep the typed attribute-value storage behind it.
Content and versions
Section titled “Content and versions”- A content has a UUID, a type, a parent in the tree, a sort index, and a master locale. Imported content keeps its Optimizely GUID.
- A version is one row with a JSON document of field values, keyed by field ID. Each locale of a content has its own versions, numbered
seq1, 2, 3… - A published pointer per content and locale says which version is live.
Version statuses
Section titled “Version statuses”| Status | Meaning |
|---|---|
draft |
Work in progress. The only status that can be edited. |
ready |
Marked ready for review. Read-only; editing creates a new draft. |
rejected |
Sent back by a reviewer, optionally with a comment. |
scheduled |
Will be published at its startPublish time by a job. |
published |
The live version of its locale. |
superseded |
Was published, replaced by a newer one. |
Editing published content creates a new draft. Saving sends the version you based your edit on (baseVersionId, baseRev); if someone saved in between, the save is refused with stale-version and the editor offers to compare or overwrite. Of two saves on the same base, exactly one succeeds.
Locales and shared fields
Section titled “Locales and shared fields”A field is either localized (translated per locale) or shared. Shared values are stored on the master locale and merged once, at publish time, into each locale’s projected document. On a non-master locale, shared fields are read-only in the editor.
Content can also be language-neutral (invariant: true): it has one document in its master locale and is served for every locale. Optimizely’s system folders are imported this way.
The tree
Section titled “The tree”parent_id is the truth. The projected path (/<locale>/<slug>/<slug>/…/) is derived below a site’s start page. Pages have a slug derived from the name unless you set one; blocks, media and folders have none.
Deleting content deletes its whole subtree in one transaction with one timestamp, which is how the recycle bin restores exactly what one delete removed. A subtree delete is refused when you lack Delete on any live descendant, so an access list deeper in the tree cannot be bypassed.
Reordering sends a position: the content is placed at that index among its new siblings, which are renumbered 0, 1, 2, … in one locked transaction, so concurrent moves cannot produce ties.
References
Section titled “References”Every reference, contentArea and rich text link is recorded in a reference table in the same transaction as the version. So “used by” is always current, and Delivery can expand references without searching documents.
Compared with Optimizely
Section titled “Compared with Optimizely”| Concept | Optimizely | SeeAMess |
|---|---|---|
| Identity | int pkID plus GUID |
UUID only. Imported content keeps its GUID; integer IDs are kept on the side. |
| Version | tblWorkContent row plus one row per property |
One version row with a JSONB data document |
| Published state | A copy of the property rows | A pointer per locale, plus a projected document |
| Tree | Adjacency, path string and closure table | parent_id; the path is derived |
| Shared fields | Stored on the master language, merged at every read | Stored on the master language, merged once at publish |
| Content area | HTML-like string | JSON array of items |
| References | Maintained separately | Written with the version |
| URL resolution | Walk segments on each request | One indexed lookup of (site, path) |
| Schema | Rows synced at startup | Versioned manifest, applied explicitly |