Implement domains
External entities
The external entity script contains 3 parts:
Constructor
The constructor is used to construct a local instance of the external entity and initialize the associated properties with certain pre-specified values that are provided as input properties.
//**
* constructor method of external entity reference
*/
public create(): void {
const log = this.util.log;
log.debug('Constructor.execute()');
// Initialize the value of the associated properties to the input value store in the construc-tor parameter and to a predefined value
this.instance.associatedProperty1 = this.input.constructorProperty1;
this.instance.associatedProperty1 = value1;
}
Loader
The loader is used to load the external instance. This is normally done by calling a REST service of the integration namespace.
When calling a service (REST or normal), it is important to check whether the output exists, and it is not void. Due to type safety, if the check does not occur, then it is not possible to access the properties of the returned object. This is to prevent accessing properties that may not exist. This check is necessary for calling functions that are built to return two different types such as services that return either void or their output entity.
/**
* load method of external entity reference - loads external entity from external system
*/
public async load(): Promise<void> {
const log = this.util.log;
log.debug('Load.execute()');
// Initialize the input of the REST integration service
const input = this.factory.entity.cust. ServiceIdentifier_Input();
// Initialize the input properties to their values
input.property1 = “value1”; // or this.instance.(…)
// Call the REST service and pass as input the the input entity created above
const serviceOutput = await this.services.intnsacrnm.ServiceIdentifier (input);
// Check if the output of the service is not void
if(serviceOutput) {
// Initialize the output of the loader of the external entity
this.output = this.factory.nsacrnm.ExternalEntityIdentifier_Output()
// Set the values of the output properties of the output
this.output.property1 = serviceOutput.property1;
this.output.property2 = serviceOutput.property2;
}
}
Validator
The validator gives information regarding the existence of a specific instance of an external entity. It usually
returns true
if the external instance exists and false
if it does not exist. The validator can also be used for
updating the local instance of an external entity if the update flag is set to true
.
/**
* validate method of external entity reference - checks if external entity still exists in external system
* @param update indicates whether local properties should be updated after validating entity on external system
* @returns boolean indicating whether external entity still exists in external system
*/
public async validate(update: boolean): Promise<boolean> {
const log = this.util.log;
log.debug('Validate.execute()');
// trigger load function of external entity in order to validate whether the entity still exists
const existence = await this.instance.load();
// If it exists return true. Otherwise, return false.
if(existence) {
return true;
} else {
return false;
}
}
Factory commands
The main purpose of a factory command is to create a new instance of a root entity with initial values and then persist those changes to the database. Each factory command provides input data depending on what has been modelled in the Solution Designer before. As a return value, the factory command will automatically return the instance id of the created instance.
Here you can see an example implementation of a factory command, creating a new instance of root entity Order:
/**
* Factory command execution
*/
public async execute(): Promise<void> {
// Read input properties
const { inputProperty1, inputProperty2 } = this.input;
// create a new instance of the Root entity
this.instance = this.factory.entity.Order();
// fill properties of the instance using the values from the input
this.instance.property1 = inputProperty1;
this.instance.property2 = inputProperty2;
// Set the instance property to an initial value
this.instance.property3 = 'value3';
// Save changes to the database
await this.instance.persist();
}
Instance commands
Instance commands usually hold logic to modify the state of a root entity instance and persist the updated state in the database. The instance that can be modified as well as the input of the command is provided automatically.
Here you can see an example implementation of an instance command, modifying an existing instance of root entity Order and throwing a business error if some special conditino is met:
/**
* Instance command execution
*/
public async execute(): Promise<void> {
// get value from input
const { newPropertyValue } = this.input;
// Check current state of instance
if (this.instance.property1 === 'SomeSpecialState'){
// throw business error
throw this.factory.error.nsacrnm.MyBusinessError();
} else {
// update property with new value
this.instance.property1 = newPropertyValue;
}
// Save changes to the database
await this.instance.persist();
}
The deletion of a root entity should be also handled within an instance command:
/**
* Instance command deleting execution
*/
public async execute(): Promise<void> {
// Delete instance
await this.instance.delete();
}
Events
An event using a schema from the schema registry as payload is published by using the structure of the following code.
Custom headers, and custom message key can be set for events (optional).
import { schemaRegistry } from 'solution-framework';
/**
* Event publishing in Command or Service
*/
public async execute(): Promise<void> {
// create a new event instance
const event = this.factory.event.domainNs.OrderAccepted();
// Create payload object using the type defined in schemaRegistry
const payload: schemaRegistry.SchemaName = {
property1: 'val'
}
event.payload = payload;
// set message key (optional)
event.messageKey = 'myMessageKey';
// set message headers (optional)
event.messageHeaders = {
'myHeaderKey': 'my Header value',
}
// Publish the event
await event.publish();
}
An event using an entity as payload is published by using the structure of the following code, however this is deprecated and it is recommended to use schemas from the schema registry instead.
Custom headers can be set for events (optional).
/**
* Event publishing in Command or Service
*/
public async execute(): Promise<void> {
// create a new event instance
const event = this.factory.event.domainNs.OrderAccepted();
// Create payload entity for event and set values
event.payload = this.factory.entity.domainNs.OrderAccepted_Payload();
payload.property1 = value1;
// set message headers (optional)
event.messageHeaders = {
'myHeaderKey': 'my Header value',
}
// Publish the event
await event.publish();
}
Agents
A published event will automatically trigger the agents where the event is assigned as trigger event. The payload provided in the event will be available as input in the agent. Besides that, any kind of logic can be implemented in an agent (e. g. calling another service).
Within the agent implementation, the message key, the message headers and the message timestamp are available and can be used. Both custom headers as well as commonly used headers can be accessed here.
/**
* Agent execution
*/
public async execute(): Promise<void> {
const log = this.util.log;
// Get the payload from the event that triggered the agent
const { property1 } = this.input;
// log message key
log.info(`Message key: ${this.messageKey}`);
// log value of my custom header, wich has a string as value
const strHeaderValue = this.messageHeaders['customHeader1'].toString();
log.info(`Custom header "customHeader1" has value: ${strHeaderValue}`);
// log value of another header, which has Buffer as value
const bufHeaderValue = this.messageHeaders['customHeader2'].toString();
log.info(`Custom header "customHeader2" has value: ${bufHeaderValue}`);
// log value of message timestamp in the format of an ISO String
const msgTime = new Date(this.messageTimestamp);
log.info(`Message timestamp: ${msgTime.toISOString()}`)
// Initialize input for calling another service
const input = this.factory.entity.order.ServiceIdentifier_Input();
// Set the value from the event payload as input value
input.property1 = property1;
// Call service and pass as input the input entity created above
await this.services.order.ServiceIdentifier(input);
}
Domain services
Within a domain service implementation, you can access the modelled input properties and process any logic. Typical logic in domain services would be e. g. loading of data from the repo and processing the data or calling instance and factory commands. Additionally it is also possible to define the output of the service. Both input and output is restricted to what has been modelled in the Solution Designer.
/**
* Domain service execution
*/
public async execute(): Promise<void> {
// Get the input properties
const { property1, property2, id } = this.input;
// Get an instance by ID
const myInstance = await this.repo.nsacrnm.EntityIdentifier.findById(id);
// trigger instanceCommand
await myInstance.MyCommand();
// fill output of service with some data
this.output = this.factory.entity.order.ServiceIdentifier_Output();
this.output.prop1 = property1;
this.output.prop2 = property2;
}