Model Overview

Model Files

Model files contain the Entity, Index and Relation definitions that you wish to generate services and database artifacts for. Jiffy determines the location of the model file(s) via the -m or -mf flags provided at the time of execution. See the Execution Options section of the documentation for details regarding the use of the -m and -mf flags.

It is possible to use more than one model-file, as long as there is no duplication of entity definitions across the files. Model files may be loaded in any order via the -mf flag, but the complete set of files must be loaded at the same time. As an example, it would be fine to create three model files; Library.json, Book.json, LibraryCard.json, but all three files should be complete and processed at the same time via the -mf flag in order to allow the correct generation of relationships etc.

Sample models.json files are installed with the application and can be found in the support/testing_models folder of the Jiffy source tree. The sample models are used as the basis for the following sections.

Making Changes to a Model

Each time the Jiffy-generated application is started, a check is performed to see if it’s models match any database artifacts. For example, if the generated application contains a model called ‘Widget’, a check is performed to see if table ‘widget’ exists in the database. If a table matching the model name is detected in the database, the application will attempt to process the model definition as an ALTER TABLE-type operation rather than CREATE TABLE. The application will add new model elements to the existing database table schema, but will not delete database artifacts if model elements are removed. During the development-cycle it is recommended to drop and recreate the database artifacts when making changes to the model. This can be done using the -dr flag option when executing your generated application. See the application startup sequence section of this document for details regarding database artifact creation and updates.

Simple Single Entity Model

The following JSON illustrates the definition of a simple single-entity model file. In this case, a model entity called ‘Person’ will be created in the generated application, along with corresponding database table ‘person’. Table ‘person’ will be created (if it does not already exist) when the application is started for the first time.

{
    "entities":  [
        {
            "typeName": "Person",
            "id_properties": {
                "start": 10000000
            },
            "properties": {
                "name": {
                    "type": "string",
                    "format": "", 
                    "required": false,
                    "unique": false,
                    "index": "nonUnique",
                    "selectable": "eq,like"
                },
                "age": {
                    "type": "uint",
                    "format": "", 
                    "required": false,
                    "unique": false,
                    "index": "",
                    "selectable": "eq,lt,gt"
                },
                "weight": {
                    "type": "float64",
                    "format": "", 
                    "required": false,
                    "unique": false,
                    "index": "",
                    "selectable": "eq,lt,le,gt,ge"
                },
                "validLicense": {
                    "type": "bool",
                    "format": "", 
                    "required": false,
                    "unique": false,
                    "index": "nonUnique",
                    "selectable": "eq,ne"
                }
            },
            "ext_points": {
                "gen_controller": true,
                "gen_model": true
            }
        }
    ]
}

The sample model file can be downloaded from the following location: simpleSingleEntityModel.json.

Annotated Simple Single Entity Model

The following block outlines and describes the fields contained in the simple model file above.

{
    "entities": [
    // The 'entities' block contains an array of entities belonging to the application
    // model.  Each entity relates directly to a database table (or view).  Entities
    // contain information that the application generator uses to create and update
    // database artifacts such as tables, indexes, sequences and foreign-keys, as well
    // as information informing the application runtime of the member field properties.
    // This is a mandatory model element.
    {
        "typeName": "Person"
        // Field 'typeName' refers to the name of an entity.  It should be capitalized
        // and written in CamelCase.
        // An Entity given a typeName of "Person" will result in an internal model
        // object of type Person and a database table called 'person'.
        // This is a mandatory model element.

        "id_properties": {
        // The 'id_properties' block contains a single entry for now, and is used to
        // provide guidance to the application generator regarding the setup of the
        // entity's ID field.
        // This is an optional model element.

          "start": 10000000,
          // Field 'start' can be used to provide a starting point for an entity's
          // ID field.
          // This is a mandatory model element if the 'id_properties' block has
          // been included in the model.
        },

        "properties": {
        // The 'properties' block contains 1:n entity member field definitions.
        // Member fields should be defined in camelCase and can start with a lower
        // or upper-case character.  In the context of the "entity" with a 'typeName'
        // of 'Person', 'properties' refer to the data fields of the generated
        // 'Person' model structure.  'properties' are a collection of free-form
        // name-tags, each with a child-block containing the 'property' attributes.
        // This is a mandatory model element.

        The "name" property block is described below:

            "name": {
                "type": "string",
                // Field 'type' in a 'properties'->'name' JSON-block refers to the go
                // data-type associated with the current 'property'.
                // 'type' is a mandatory field in an "entity" 'property' block.

                "dbtype": "varchar(100)",
                // Field 'dbtype' can be used to specify a native db-field-type for
                // the property.  This feature can be useful if for example, the
                // developer is confident that a string will never exceed 100
                // characters in length.  Care should be taken to ensure that the
                // specified DB-Type is consistent with the go-type that will be
                // generated in the application's model.<Entity> defintion.  Consider
                // also that making use of this field potentially limits the backend
                // portability of the generated code.  For example, not all database
                // systems have a TINYINT data-type, so specifying a 'db_type' of
                // TINYINT could be problematic if multiple database systems are
                // being used for testing.
                // This is an optional field.

                "no_db":
                // Field 'no_db' can be used to instruct the generator to create the
                // field as a member in the enitity struture, but to prevent the
                // field from being persisted in the backend database.  Non-persistent
                // fields are not created in the database table schemas, and values
                // passed into the application are wiped from their respective
                // internal structures following use.  'no_db' fields can also be used
                // to pass calculated values back to the caller.
                // This is an optional field.

                "format": "",
                // Field 'format' is not currently used, but is intended to deal with
                // field conversion from strings / floats to timestamp formats etc.
                // This is an optional field.

                "required": false,
                // Field 'required' is used to instruct the generator to set a
                // 'NOT NULL' database constraint on the column related to the
                // property.  
                // Allowed values include {true, false}.
                // This is a mandatory field.

                "unique": false,
                // Field 'unique' is used to instruct the database not to accept
                // duplicate values in the database column related to the property.
                // Setting this field to true will cause a 'UNIQUE' constraint to
                // be applied to the related database column.
                // Allowed values include {true, false}.
                // This is a mandatory field.

                "index": "nonUnique",
                // Field 'index' is used to instruct the database to create an index
                // on the db table-column related to the property.  See the 'indexes'
                // element in the type definition for the creation of compound
                // indices.
                // Allowed values include {"unique", "nonUnique", ""}.
                // This is an optional field.

                "selectable": "eq,like"
                // Field 'selectable' can be used to instruct the code-generator to
                // create simple REST query accessor routes for the current 'property'.
                // The generator creates routes to permit GET operations that can be
                // called based on the entity 'typeName' and 'property' values.
                // Allowed values include:
                // {"EQ", "eq", "LT", "lt", "GT", "gt", "GE", "ge", "LIKE", "like", "NE", "ne", ""}
                // Additional restrictions are imposed based on the 'type' field value.
                // For example, a bool type need not support LT or GT operators.
                // Sample URLs for Person->Name selection with "eq,like" are shown:

                //    https://localhost:<port>/persons/name(EQ '<sel_string>')
                //    https://localhost:<port>/persons/name(LIKE '<sel_string>')

                // Note that this is not the same as filtering insofar as setting the
                // selectable options results in the creation of parameterized static
                // routes in the application mux.

            },
            "age": {
                "type": "uint",
                "format": "",
                "required": false,
                "unique": false,
                "index": "",
                "selectable": "eq,lt,le,gt,ge,ne"
            },
            ...
            ...
        },
        "ext_points": {
        // The 'ext_points' block contains two elements which Jiffy uses to determine
        // whether or not to create extension-points in the entity.
        // This is an optional block.
            "gen_controller": true,
            // Element 'gen_controller' is used by Jiffy to determine whether or not
            // to insert extension-points into the entity's controller.  Setting the
            // value of this element to 'true' will cause Jiffy to insert extension-
            // points into the entity's controller and generate an empty implementation
            // of the extension-point interface for the entity in the ./controllers/ext
            // folder.  A generated controller extension-point implementation for
            // entity 'Person' would take the form of: 'personc_ext.go'.
            // This is an optional element.
            "gen_model": true
            // Element 'gen_model' is used by Jiffy to determine whether or not to
            // insert extension-points into the entity's model file.  Setting the
            // value of this element to 'true' will cause Jiffy to insert extension-
            // points into the entity's model file and generate an empty implementation
            // of the extension-point interface for the entity in the ./models folder.
            // A generated model extension-point implementation for entity 'Person'
            // would take the form of: 'personm_ext.go'.
            // This is an optional element.
        }  
    },
    {
        ... next entity definition
    }
    ]
}

Entity ID

The ID field is visibly absent from the preceding entity declarations. The original intent was to support any name for the primary key / resource identifier of an entity. While it is possible to do this, ID uint values are the universal ‘standard non-standard’ way of representing object identifiers in systems. As a result, ID is injected into the model definition of every entity as a uint64 field and is marked as the primary-key in the database table. By default, ID is created as an auto-incrementing column in the DBMS, but this functionality can be suppressed (future - the ORM supports this, but Jiffy has yet to be updated). The ability to allow a specific starting point for the ID key range is supported via the entity header-level “start” value.

If the ID field really needs to be known as something else - CustomerNumber for example, the generated code can be edited in a few locations to support the change. It is worth mentioning that the number of edits required to rename ID increases in direct relation to the number and complexity of entity relations (both to and from).

As an alternative to renaming ID, it is also conceivable that it can be ignored. Ignoring ID means that the generated CRUD controller/model/routes are not as useful as they could be, but they offer a great starting point for your own coding. Entities can be defined with column constraints that mimic those of DBMS primary / complex keys, then the generated CRUD artifacts based on ID can be ignored, copied then ignored, or modified to accommodate the modeled entities.

It is also possible to go completely custom and write your own models and controllers from scratch using a generated model as a reference template. In addition to exposing a generic internal CRUD interface to the backend, the more interesting go/sql calls are exposed internally along with some lightly wrapped and super useful calls from jmoirons widely used sqlx package. Although Jiffy eschews non-standard lib packages wherever possible, sqlx is really great.