SHARD-WP-0003 T8. Classic relational-DB-backed wiki: Catalyst/DBIx::Class app, pages + path tree + full history in SQL tables, Markdown body in a column, no file store and no content API. Anchors the direct-DB-read binding (map schema -> page model + journal); DB version rows = a third history source beside git commits and RCS files. UC-81. Enriched UC-02/40/36/34. Marks T8 done. Feeds SHARD-WP-0002 T14/T13/T11. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6.9 KiB
MojoMojo — deep dive (findings)
Date: 2026-06-14 · Source: SHARD-WP-0003 T8 · Subject: MojoMojo, a Perl Catalyst wiki/CMS.
Why this dive
The file-store classics (TWiki, Foswiki, Oddmuse, UseMod) keep pages as files; the modern SaaS keep them behind APIs. MojoMojo is the classic relational-DB-backed wiki — a Catalyst MVC app over DBIx::Class with pages and their history in SQL tables, and no file store and no first-class content API. It anchors the "attach by reading the database directly" hard case the adapter contract must account for (T13/T14).
1. Architecture
- Stack: Perl Catalyst (MVC web framework) + DBIx::Class (ORM) over a relational DB (SQLite / PostgreSQL / MySQL). Templating via Template Toolkit.
- Content: Markdown (Text::MultiMarkdown) is the page markup — so the body is Markdown, but it lives in a DB column, not a file.
- Pages are hierarchical: a path tree (
/parent/child) modeled as rows with parent/lineage relations — structure is relational, not directory-based. - Versioning: each page edit creates a new version row (a
page_version-style table) — full revision history lives in DB version tables, with author/timestamp. - Features: inline AJAX editing, attachments (stored as DB rows / blobs + metadata), diffs, RSS feeds, full-text search, per-page permissions.
2. The attach problem — DB or nothing
MojoMojo exposes its content through the web app (HTML) and the database; there is no clean REST/GraphQL content API and no file store. So a shard adapter has two realistic paths:
- Direct relational read (preferred): read the
page+page_version(+content,attachment) tables via DBIx::Class schema — pages, the path tree, and full history are all there, importable to the coordination journal (UC-41-style history import, but from DB version rows rather than RCS/git). - HTML scrape (fallback): parse rendered pages — lossy, last resort.
This makes MojoMojo the direct-DB-read binding archetype: the canonical store is a relational schema, and the adapter's job is to map that schema to the wiki page model + journal.
3. Capability profile
| Dimension (synthesis spectrum) | MojoMojo |
|---|---|
| Attachment mode | direct DB read (relational); HTML-scrape fallback; no file store, no API |
| Addressing granularity | page (row); path tree via lineage rows |
| Content identity | DB page id; path as human key |
| Identity vs placement | row id vs path lineage (separable) |
| Structure | relational: page rows + parent/lineage; attachments as rows |
| History | DB version tables (per-edit version rows, author/timestamp) |
| Merge model | app-level last-writer; DB transactions |
| Native query | SQL over the schema (not a wiki query language) |
| Translation | Markdown body in a DB column — minimal translation, but extraction needed |
| Write granularity | page (row) per save |
| Operational envelope | a Perl app + its DB; direct DB access needs credentials |
| Access grant | per-page permissions in DB; app auth |
| Content opacity | transparent if you can read the DB |
| Provenance | author/timestamp on version rows |
4. INTENT mapping
Reinforcements
- Backend-neutral page model: the body is Markdown — once extracted from the DB column it maps directly; the adapter's work is schema→page-model, not format translation.
- History portability (T13): DB version rows are a third history-source shape beside git commits and RCS files — importable to the journal as discrete revisions with author/timestamp.
- Graceful degradation: even with only DB read (no API), MojoMojo is a usable read/projection/backup shard; with DB write it could be write-through, but carefully (app invariants).
Divergences (boundaries / notes)
- No file store, no API ⇒ the direct-DB-read binding is a first-class attach mode the contract must name (alongside file-store, in-engine host, external-API, CRDT, P2P) — or a sub-mode of "external store" where the medium is a relational schema (T14). Reading a third-party app's DB is coupling to its schema (versioned, may drift across MojoMojo versions) — a stated risk (UC-43 backend-swap analogue at the schema level).
- Writing by direct DB risks violating app invariants (lineage, version counters, search index) — default to read/projection/overlay; write-through only with the app's cooperation.
What to keep
- Direct-DB-read as a named binding for DB-backed engines with no file/API (UC-81), mapping a relational schema → wiki page model + journal.
- DB version rows as a history source for the journal (T13), beside git and RCS.
- Schema-coupling caution — treat the schema as a versioned interface that can drift (relates UC-43).
5. UC seed
| # | Seed | Disposition |
|---|---|---|
| UC-81 | Attach a DB-backed wiki with no file store / no API (MojoMojo) by reading its relational store directly (page + version tables), mapping schema → page model and importing DB-resident history to the journal | new |
| — | DB attach vs file attach | enrich UC-02 / UC-40 |
| — | DB version-table history import | enrich UC-36 |
| — | relational page rows / lineage as structure | enrich UC-34 |
6. Architecture notes for SHARD-WP-0002
- T14 (binding): add direct relational read as a binding (or external-store sub-mode whose medium is a SQL schema) for DB-backed engines lacking a file store or API; HTML scrape is the lossy fallback. Schema is a versioned coupling (drift risk, UC-43).
- T13 (history portability): DB version rows = a history source alongside git commits and RCS revisions — import as discrete journal entries (author/timestamp).
- T11 (capability): "has-file-store" / "has-API" are absent here; "has-readable-DB" is the capability — a sparse profile relying on schema knowledge.
7. Open questions
- Does shard-wiki sanction direct third-party DB reads as a binding, or restrict them (schema coupling/drift) to a documented best-effort mode? How is schema drift across MojoMojo versions handled (UC-43)?
- Is write-through by direct DB ever allowed (risking app invariants), or are DB-backed no-API engines read/projection/overlay/backup only?
8. Sources
- MojoMojo — github.com/mojomojo/mojomojo; metacpan MojoMojo (Catalyst app, DBIx::Class schema: Page / PageVersion / Content / Attachment)
- Catalyst + DBIx::Class framework docs (architecture context)
- prior:
research/260613-twiki-deep-dive/(file-store classic contrast, UC-40/41)
9. Traceability
New UC UC-81 carries the marker ⊙ in the wikiengines column of
spec/UseCaseCatalog.md. Enriched: UC-02, UC-40, UC-36, UC-34. Architecture cross-refs:
SHARD-WP-0002 T14 (direct-DB binding), T13 (DB version-row history), T11.