Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Great question, which I found myself scratching my head for quite some time. I'll give the short gist of what I've done, but in short: yes please DO separate the stuff. I've gone a bit further, but then again, I might have had some other requirements. </p> <p>Consider the following. </p> <ul> <li>An <code>Entity</code> can be anything from a Product to a Blogpost (defined by it's role. An Entity can have multiple roles) . </li> <li>An <code>Entity</code> has a lot of <code>Facts</code> . </li> <li>Everything known about an entity like the title, description, slug, hero-image, author, etc. is saved as a <code>Fact</code> to that entity. </li> <li>All write operations work on <code>Facts</code> and Facts alone. (In other words, the editing part of the app is only concerned about facts, since facts work on any type of page, my editing-code (current implemented as modals) work on any type of page) </li> <li><code>Facts</code> can be of any given type, but that type must be defined in a <code>FactSchema</code>. The CMS allows <code>Factschema</code>s to be created on the fly, thus enabling new types of facts. </li> <li>The way in which a fact can be edited (say something like openinghours with multiple dropdowns per openinghour slot) may be totally different in the way in which it is presented. Therefore I needed the possibility for transformers (between write and read). Now, this may not be needed for you, but this led me to the following: </li> <li><code>Facts</code> are never used directly for displaying (thus reading) pages. Instead they are only loaded lazily, when the fact is in edit-mode. This saves a lot of bandwidth, because a lot of stuff, like history, in progress changes, allowed editing roles, etc. may be saved with the fact</li> <li>To make <code>facts</code> available for reading each entity has a <code>.c</code> (for calculated) property. Each <code>fact</code> upon create/change is saved with the name of the fact as a key under that property (applying transformers if defined.) </li> <li>The current method for rendering is that I flatten <code>.c</code> in my object just before throwing the resulting JSON to a server-side templating engine for rendering. (Thus, all properties in <code>.c</code> become properties of the containing object) </li> </ul> <p>That's about it. Moreover, ZERO presentation logic is defined in these models, this is defined in Mustache/Hogan templates instead. (with a couple of mixed-in javascript functions if I need to extend what's possible with logic-less templates, as far is presentation logic is concerned) . </p> <p>Searching could/should be done on facts as defined in <code>.c</code>. Although I'm doing that through Elasticsearch.</p> <p>Here's those 3 schema declarations (simplified) (p.s: the schema's are in Node.js but that shouldn't bother you)</p> <pre><code> var fact= new Schema( { //_id undefined &gt; defaulting to mongoDB objectid instead //factschema is looked-up by name name: { type: String, required: true, index: { unique: false }} //value can be any type, but for a particular instance the type is restricted as set in FactSchema.valueType ,value: {type: {}, required: true} ,createddate : { type: Date, required: true, default: Date.now, select: false } } var entity= new Schema( facts: { type:[require('./fact')], select:false} ,roles: {type: [String], required: true, index: {unique: false}} ,c: {type: {}} //all calculated stuff based on facts } var factSchema= new Schema({ name: { type: String, required: true, index: { unique: true }} , valueType: { type: {}, required: true} //any type may be defined (simple types but also complex-types which have a ref to their own schema) , fact-instances are checked to adhere to the specified type in pre-save handlers. ,roles: {type: [String], required: true} //roles that are allowed to contain facts based on this factschema ,isMulti: {type: Boolean, required: true } //format to show edit-mode in. ,formFieldType: {type: String, required: true} //ACL-stuff ,directChangeRoles: {type: [String]} //i.e: [super, admin,owner] ,suggestChangeRoles: {type: [String]} //ie: [editor] } </code></pre> <p>I must say this works rather well. Hth. </p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload