The Anatomy of a Business Object
reviewed: 23 February 2025
We have already learned that each .cms file in the bo folder is, by definition, a business object. It was also mentioned that business objects are sometimes referred to as entities. Probably time to explain the subtle difference between these terms.
A .cms file in the bo folder contains the definition of a business object. It is however not a business object (often abbreviated to BO). The correct term for the definition is entity. A BO is an instantiation of an entity.
The file client.cms is an entity defining what a client object looks like. I can create an instance of this entity and load the data in memory for 'NewCo Ltd from Mansfield' and that would be a business object.
Each entity consists of the following components:
| Component | Usage |
|---|---|
| Any diretives | Such as inherits, cache or trace |
| The main function | The function called when an instance of this entity is created |
The @bo resource |
The definition of the entity |
The _eventAction function |
Function invoked at specific system events |
| Conditions | Conditions of this entity |
| Methods | Methods of this entity |
| Resources | Resources (other than <@bo> ) of this entity |
Some Behind the Scenes Details
It is good to have some understanding of what happens 'under the hood' when you deal with business objects.
We have seen the bo.create function to create an instance of a BO:
set( 'bo', bo.create( 'client' ) )
- CaseMaster will check whether the BO script client.cms is already in cache; if not it will try and load the file, parse the contents and store it in cache
- The main function of the BO script will be called (invoked) and this will return a
<@bo>property bag - The property bag will be used to create a so called descriptor Object
- A BO object is created (this is not yet of a specific type)
- The descriptor BO is linked to the BO object; now the BO is of type client
The @bo Resource
The <@bo> property bag is a so-called qualified property bag. This concept is explained in more details here.
The <@bo> property bag describes in great detail how a BO of this entity type should behave. Hence the term descriptor.
| Tag | Usage | Example | Optional / Mandatory |
|---|---|---|---|
| label | The label of the entity | 'Client' | Optional |
| table | The table in the database where the data is stored | 'cdClient' | Optional |
| inLineView | Query that can be used instead of a table name | 'select * from cdClient' | Optional |
| dataSource | The datasource the BO is connected to | 'primary' | Optional |
| size | Indication of number of rows in the table / view | entitySize.Small Optional | |
| primaryKey | Name of the attribute that is the primary key | 'id' | Optional |
| uniqueConstraint | Optional attribute group that must have unique value | 'client,name' | Optional |
| useOwnDataSourceForSequence | Indicates that datasource has its own sequence table | false() | Optional |
| sequence | Name of sequence in sequence table | 'cdClient' | Mandatory in case one of attributes is of type automatic |
| sequenceStep | Step to increase sequence by | 2 | Optional |
| deleteRule | Indicates whether / how delete is allowed | deleteRule.NotAllowed | Optional |
| memoryPinMode | Is BO memory pinned | memoryPinMode.None | Optional |
| auditable | BO auditing settings | auditing.None | Optional |
The following tags all are property bags themselves and will be explained later in this document:
| Tag | Usage | Optional / Mandatory |
|---|---|---|
| deleteRelations | Details on delete rules | Mandatory when deleteRule has been set to deleteRule.PerRelation |
| tags | Tags that can be linked to a descriptor | Optional |
| enhancers | Rules to enhance behavior of descriptor | Optional |
| attributes | The attributes of the descriptor | Mandatory |
| attributeGroups | Named attribute groups | Optional |
| audits | Audit types | Optional |
| indexes | Indexes | Optional |
| Security | Security rules | Optional |
Attributes
Attributes are described in more detail here.
Attribute Groups
Attribute groups are described in more detail here.
Audit Types
Audit types are described in more detail here.
Security
The security section can be used to define who can select, delete, insert or update BO's for this entity. It should be seen as a last defence. Typically you will build access control into your application (e.g. when you are not allowed to delete a client, there should be no delete button). CaseMaster will check the security rules at the very lowest level and so they can act as a last defence in case access control has not been implemented correctly elsewhere in the application.
See also here to learn more about security.
security: <@bo/security
canDelete: $inGroup( 'DEVL' )
canUpdate: true()
>
Delete Rule and Delete Relations
Delete rules are described in more detail here.
Indexes
The indexes section is not used often but can be used to describe the indexes that need to be present on the associated table as an absolute minimum.
Indexes have no impact on the workings of a descriptor or BO but are used when generating DDL.
indexes: <
<@bo/index
name: 'ixClient'
group: 'name,status'
include: 'dob'
unique: false()
>
>
The _eventAction Function
Event actions are described in more details here.
Conditions
Conditions are described in more details here.
Methods
We have already seen method in actions in previous documents. Methods are simply functions in BO scripts. Do not that the static access modifier is available for methods but not for functions of all other script types.
A static method is a function that can be called on the definition of an entity, not an instance of an entity.
set( 'c', bo.quickLoad( 'client', 10 ) ) // Load BO with PK 10
bo.invoke( [c], 'someMethod' )
bo.invoke( 'client, 'someStaticMethod' )
Tags
Tags have two purposes; you can add custom tags to a descriptor for use somewhere in your system. This can be useful when are writing generic code that supports business objects of various entities. You may want to use a specific tag to drive certain behaviour.
The following example shows a snippet of code where a specific tag is checked.
if boDesc.getTag( [BO], 'sensitive' )
// Do something special
else
// Do something else
end-if
Each tag is a property bag tag:
tags: <
sensitive: false()
>
There is a number of reserved tag names that have a specific meaning for CaseMaster. These are:
| Tag | Usage | Example |
|---|---|---|
| forceSQLUpdate | Always update database using SQL (and not a data adapter ) | true() |
| forceSQLInsert | Always insert into database using SQL (and not a data adapter ) | true() |
| qsearchEntitySize | Force the entity size for qsearch purposes | 0 |
| readNoLock | Force readNoLock on all select statements | true() |
Read here about quick search and entity size.
<End of document>