Often, there is a requirement to log mutations of a specific record type. When names of persons change, or prices of products, this can be a reason to create a specific log entry.
Over the years, we have found that the following setup works best in the composable Novulo applications.
Record setup
The first step is to ensure you have a Mutation-record for the data type. Please check first in the Novulo Application Server if there is already something existing:
Examples:
- For N_Product, there is an existing N_ProductMutation
- For N_User, there is an existing N_UserMutation
For User mutations as an example, you can have a look at M10661.
If no record exists, you need create one core record that will register the mutations that you will always want to log, in the minimum composition of your application.
If it already exists - like with Products or Users, please continue expanding this record type!
Initial mutation and âphotoâ during event
When creating change logs, there are technically two ways:
- To create an entry when a change occurs, storing the data as it was before the change
- Recommended: To create an entry when a change occurs, storing the data as it was after the change. This will require you to also store an initial mutation during the Insert of the record.
We recommend the second approach as it makes two things much easier:
- You will create data entries that allow you to easily see how data evolved in time and to create âtime-lapseâ reports without performing difficult table joins
- It is much easier during your process to store the data, especially in a composable set-up
Creating a process component to create the mutation
First, identify the record type that refers to the main record, and that registers the data you want to log to track changes. In this example, we want to track changes to username, rights profile and is-active status for a User, on a UserMutation.
In this example, the UserMutation already exists in M10661.
If you are adding mutations from a separate component, please see Best practice for change logs or mutations
Then, you create a process component to add a mutation, storing the data exactly as it is during the registration - like taking a photo.
In this process, the Update Record is a straight-forward registration. In this specific example, the current name of the Rights profile is stored next to the reference - this is to keep track easily of changing names of profiles, in this specific case.
The Lock-Update-Commit-Unlock procedure is selected to allow for easy optional consumes if ever this is required. Internally, it does exactly the same as âAdd recordâ.
Now, this process is always called in AfterInsert, to make sure you always have an initial mutation at the moment you register a new record. This is required to always find back the initial values.
Then, in the BeforeUpdate procedure, you add a Decision to check if relevant data is changed, so whether or not you need to log a mutation. In almost no case you want to log every mutation.
Add additional components to the set-up
Now, if you want to add fields from other components, make sure your Mutations-record is produced as a concept in the Reference Architecture. Also make sure your process âAdd mutationâ is registered as a Produced Process in the Reference Architecture.
Here, weâll show you how we log changes to the functionality âdisable column selectorâ (M9295) to the Mutations in a new components.
Always choose first:
- Can I require the mutations in any composition? In that case, include your mutations logging in the core component
- Canât you require the mutation records in all compositions? Create a new component to link the specific fields to the mutations.
Here, we assume itâs safe to include âUser mutationsâ in future compositions, so we continue in M9295.
First, consume your Mutation-record. In this case, the N_UserMutation. Also consume the parent record (User).
Here, register the field that you want to track.
Then, create a process in the beforeInsert of your mutation, like this. Use an âupdate recordâ and set the value on the Mutation record, based on the parent record.
Here, set the value from the main record.
So in this example, we change the User.Is_disable_column_selector, so thatâs what we store in the Mutation.
Please note user
in the expression is trigger.record.user.is_disable_columns_selector
, referring to the main record. And if you have Productmutations, youâll see trigger.record.product.name_of_field
.
The last step is to create a trigger to see if a mutation must be created. Just doing this, will not trigger mutations if you only toggle the switch on âis disable columns selectionâ.
Create a simple process like this: it will, if a User-record, thatâs in a lock (transaction) will be sent as a parameter, return True or False to say if a change must be made.
Now, produce this process so it can be consumed in another component.
Now, we go back to the first component (M10661). Here, we consume the just-produced process:
In the beforeUpdate-process, we now implement the process as an optional process. The default value (in case the process is not in the composition), is false.
And insert this value in the decision.