Atom is the most basic element of Cabloy, such as article, announcement, leave application, etc.

Through the combination of Atom, we can achieve any desired requirements, such as CMS, OA, CRM, ERP and so on.

It is precisely because a general concept of Atom has been abstracted from various business models, that Cabloy realizes many common features and functions for Atom, thus enabling various practical business conveniently

For more detailed concepts of Atom, see: Cabloy: Basic Concepts of Atom

Here, the basic concepts and usage are introduced mainly through the atomClass todo

Business Module Template: module-business

If business logics to be developed, it is recommended that the business module template module-business be used to create the file skeleton of the business module. This template automatically creates business-related code, greatly simplifying the workload

$ cd cabloy-demo
$ npm init cabloy src/module/test-todo --type=module-business

Declaration of AtomClass

AtomClass is the metadata information corresponding to Atom, which is also set in meta of the module

src/module/test-todo/backend/src/meta.js

const meta = {
  base: {
    atoms: {
      todo: {
        info: {
          title: 'Todo',
          tableName: 'testTodo',
        },
        actions: {
        },
        flags: {
        },
        validator: 'todo',
        search: {
          validator: 'todoSearch',
        },
      },
    },
  },
};
name description
info.title AtomClass’s title
info.tableName data table name
validator used to render form and validate form data
search.validator used to render custom search form fields

Data Table

All code related to module data architecture change is in the backend API route version/update, which facilitates the maintenance of module data version, thus achieving seamless upgrade of module

src/module/test-todo/backend/src/service/version.js

async update(options) {
  if (options.version === 1) {
    // create table: testTodo
    const sql = `
      CREATE TABLE testTodo (
        id int(11) NOT NULL AUTO_INCREMENT,
        createdAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updatedAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        deleted int(11) DEFAULT '0',
        iid int(11) DEFAULT '0',
        atomId int(11) DEFAULT '0',
        description varchar(255) DEFAULT NULL,
        PRIMARY KEY (id)
      )
    `;
    await this.ctx.model.query(sql);
  }
}

Model

Defining model objects corresponding to data tables makes it easier to manipulate data

src/module/test-todo/backend/src/model/todo.js

module.exports = app => {
  class Todo extends app.meta.Model {
    constructor(ctx) {
      super(ctx, { table: 'testTodo', options: { disableDeleted: false } });
    }
  }
  return Todo;
};
name default description
table The name of the data table or view corresponding to the model object
disableDeleted false Disable Soft Deletion feature

Form Validation

Cabloy’s Form Validation mechanism uses ajv. Suggest you have a preliminary understanding of ajv

With the Form Validation, we can automatically render the form by defining the business-related JSON Schema, and validate the form data automatically. If the form data does not meet expectations, the error messages will be displayed automatically

For more information about Form Validation, see: Cabloy: Form Validation

src/module/test-todo/backend/src/config/validation/schemas.js

module.exports = app => {
  const schemas = {};
  // todo
  schemas.todo = {
    type: 'object',
    meta: {
      custom: {
        // component: 'todoItem',
      },
    },
    properties: {
      atomName: {
        type: 'string',
        ebType: 'text',
        ebTitle: 'Name',
        notEmpty: true,
      },
      description: {
        type: 'string',
        ebType: 'text',
        ebTitle: 'Description',
      },
    },
  };
  // todo search
  schemas.todoSearch = {
    type: 'object',
    properties: {
      description: {
        type: 'string',
        ebType: 'text',
        ebTitle: 'Description',
      },
    },
  };
  return schemas;
};

Atom Action & API Route

Cabloy refers to all operations of business data as Atom Action, which fall into two main categories:

  1. Basic Action: create、read、write、delete
  2. Custom Action:business-specific operations, such as review, publish, etc.

Cabloy provides a set of basic API routes, encapsulates atom actions, so that middleware such as database transaction, validator',right` can be configured uniformly

Business module only need to provide extended API routes, which will be called by basic API routes when appropriate

Extended API Routes

src/module/test-todo/backend/src/routes.js

  // todo
  { method: 'post', path: 'todo/create', controller: todo, middlewares: 'inner' },
  { method: 'post', path: 'todo/read', controller: todo, middlewares: 'inner' },
  { method: 'post', path: 'todo/select', controller: todo, middlewares: 'inner' },
  { method: 'post', path: 'todo/write', controller: todo, middlewares: 'inner' },
  { method: 'post', path: 'todo/delete', controller: todo, middlewares: 'inner' },
  { method: 'post', path: 'todo/action', controller: todo, middlewares: 'inner' },
  { method: 'post', path: 'todo/enable', controller: todo, middlewares: 'inner' },
name description
middlewares: ‘inner’ middleware inner, indicates this API route only be allowed inner access
todo/create create an item of todo
todo/read select single item of todo
todo/select select some items of todo
todo/write update an item of todo
todo/delete delete an item of todo
todo/action custom actions of todo
todo/enable change status of todo from draft to normal

Extended Logic Codes

src/module/test-todo/backend/src/service/todo.js

module.exports = app => {

  class Todo extends app.Service {

    async create({ atomClass, key, item, user }) {
      // add todo
      const res = await this.ctx.model.todo.insert({
        atomId: key.atomId,
      });
      // return key
      return { atomId: key.atomId, itemId: res.insertId };
    }

    async read({ atomClass, key, item, user }) {
      // read
    }

    async select({ atomClass, options, items, user }) {
      // select
    }

    async write({ atomClass, key, item, validation, user }) {
      // update todo
      await this.ctx.model.todo.update({
        id: key.itemId,
        description: item.description,
      });
    }

    async delete({ atomClass, key, user }) {
      // delete todo
      await this.ctx.model.todo.delete({
        id: key.itemId,
      });
    }

    async action({ action, atomClass, key, user }) {
    }

    async enable({ atomClass, key, atom, user }) {
    }

  }

  return Todo;
};