Averos Entity Workflows Commands
In order to automate entity declaration and management, averos framework offers a set of workflows to deal with all mandatory entity aspects and guidelines.
Averos provides three main entity action domain each managed through a dedicated workflow command:
- Entity area: managed by averos-entity command
- Simple member area: managed by add-simple-member command
- Composite member area: managed by add-composite-member command
1. averos-entity
Description
The workflow command averos-entity
allows to create a fully averos compliant entity along with its service controller.
The command will also register a default view layout for the newly created entity.
Command Usage
ng add @wiforge/averos:averos-entity --name=MyEntity [--sname=MyEntityService]
Input Parameters
- name: Mandatory parameter. Defines the name of the entity to be created
- sname: Optional parameter. Defines the name of the related entity service.
Output
The workflow command will bring about the following actions:
- creates a new averos entity by initializing members, metadata and decorate it with @AverosEntity()
- creates a new averos service named
[--sname]
(default=MyEntityNameService) related to the newly created entity[--name]
. - creates and initializes the entity related default view layout
2. add-simple-member
Description
The workflow command add-simple-member
allows to add a simple typed member to a given averos entity and updates the entity view layout accordingly.
Command Usage
ng g @wiforge/averos:add-simple-member --ename=MyEntity --mname=isActive --member-type=Boolean [--list-of-enum-values=value1,value2,value3]
Input Parameters
- ename: Mandatory parameter. Defines the name of the entity to which the member will be added
- mname: Mandatory parameter. Defines the name of the simple member to be added.
- member-type: Mandatory parameter. Defines the type of the simple member to be added.
Currently averos workflow supports the following types which will lead to a specific input component with respect to UI:
- enumeration: list box
- string: text input box
- number: number input box
- Boolean: list box with two values: true / false
- Date: a date input component
- Textarea: a text Area box
- Password: password input box
- Phone: averos phone input component
- list-of-enum-values: Optional parameter. Defines the list of possible member domain values if the member Type is equal to
enumeration
Output
The workflow command will add the simple member to the entity and update its view layout.
3. add-composite-member
Description
The workflow command add-composite-member
allows to add a composite member to a given averos entity.
Composite member are usually entities that are related to the parent entity by one of the following relationships: One-To-One
, One-To-Many
or Many-To-One
.
Command Usage
ng g @wiforge/averos:add-composite-member --ename=[Your Entity Name] --fename=[your composite new member entity name] --field-relation-type=[the relationship type] --member-update-strategy=[the update strategy]
Input Parameters
- ename: Mandatory parameter. Defines the name of the entity to which the member will be added
- fename: Mandatory parameter. Defines the name of the composite member entity name.
- field-relation-type: Mandatory parameter. Defines the relation type between the parent entity and the composite entity child. It could have one of the following values:
OneToOne
: defines a one to one relation between parent entity and composite childManyToOne
: defines a many to one relation between parent entity and composite childOneToMany
: defines a one to many relations between parent entity and composite child
- member-update-strategy: Optional parameter. Sets the update strategy related to the parent-childs entity relationship in a One-To-Many context. This flag could have two values:
single
multiple
Output
The command will perform the following actions:
- add a new member to the entity
- decorate the member according to its relationship with the parent entity
- update the parent entity view layout
- add a default translation key in the application default language
4. Entity Update Strategy
Given two entities P
, as the relationship owner or the Parent, and C
, as the Child, which are linked by a OneToMany
relationship; if you are willing to display child items within the parent in the context of create
, edit
or view
entity use case then you are likely requesting a navigable OneToMany
relationship into which P
is aware of its C
items.
Handling such type of relationship is tighly coupled with the design to follow in terms of update strategy.
While in real life applications there are several design patterns that were put in place in order to address this type of relations, averos has put in place the most relevant and natural update strategies that involves either a single
transaction or multiple
transactions while dealing with such relationships.
4.1. Single Transaction Strategy
A single transaction strategy means that when updating P
by adding or deleting new C
s to/from the P->C
collection relationship, the related update will happen in a single
transaction that usually relates to a simple P
entity api call.
The whole update logic is therefore managed by the back end that affords the api.
In such case, when calling an update api, the api will be in charge of updating the P
relations by updating each child separately before updating the parent. It will deal with exceptions and decide whether to cancel the update or to proceed with the partial updates.
A typical addRelation/removeRelation service implementation in the entity service will look like the example below:
deleteRelationCollection(parentId: any, relationName: string, cids: {id: string}[]): Observable<any> {
return this.httpClient.patch<any>(`${this.toDoAreaAPI}/${parentId}` , data);
}
addRelationCollection(parentId: any, relationName: string, cids: {id: string}[]): Observable<any> {
return this.httpClient.patch<any>(`${this.toDoAreaAPI}/${parentId}` , data);
}
4.2. Multiple Transaction Strategy
Multiple transaction strategy means that when updating P
by adding or deleting new C
s to/from the Pο C
collection relationship, the related update will happen in multiple
transactions that usually relate to multiple call to C
api followed by a call to P
api.
The whole update logic is therefore managed by the front end service (the web application averos service) that is triggering the update.
In such case, the front end application will be in charge of handling exceptions and decide whether to cancel the update or to proceed with the partial updates.
A typical addRelation/removeRelation service implementation in the entity service will look like the example below:
deleteRelationCollection(parentId: any, relationName: string, cids: {id: string}[]): Observable<any> {
let operations: Observable<any> | null = null;
const data: any = {};
data['updatedBy'] = this.applicationSharedService.getLoggedUser();
data['updatedAt'] = new Date();
//'inline-loading' places a loading progress indicator inside the most inner html component.
// Set it to 'false' or ommit it in order to delegate to the global application loader located
// in the root html element.
const options = {
headers: {
'inline-loading': 'true'
}
}
/**
* TODO: update the target elements in the collection by removing the parent id
* then
* update the parent's updateBy field
*/
if (relationName === 'toDoTasks'){
operations = cids.reduce((previousValue: Observable<any> | null, currentValue: {id: string}) => {
previousValue = previousValue != null
? previousValue.pipe(mergeMap(el => this.httpClient.patch<any>(`${this.toDoTaskAPI}/${currentValue.id}`, {"toDoAreaId": ""}, options)))
: this.httpClient.patch<any>(`${this.toDoTaskAPI}/${currentValue.id}`, {"toDoAreaId": ""}, options);
return previousValue;
}, operations);
}
if (operations !== null){
return (operations as Observable<any>).pipe(mergeMap(e => this.httpClient.patch<any>(`${this.toDoAreaAPI}/${parentId}` , data)));
} else {
return this.httpClient.patch<any>(`${this.toDoAreaAPI}/${parentId}` , data);
}
}
addRelationCollection(parentId: any, relationName: string, cids: {id: string}[]): Observable<any> {
let operations: Observable<any> | null = null;
const data: any = {};
data['updatedBy'] = this.applicationSharedService.getLoggedUser();
data['updatedAt'] = new Date();
//'inline-loading' places a loading progress indicator inside the most inner html component.
// Set it to 'false' or ommit it in order to delegate to the global application loader located
// in the root html element.
const options = {
headers: {
'inline-loading': 'true'
}
}
/**
* TODO: update the target elements in the collection by setting the parent id
* then
* update the parent's updateBy field
*/
if (relationName === 'toDoTasks'){
operations = cids.reduce((previousValue: Observable<any> | null, currentValue: {id: string}) => {
previousValue = previousValue != null
? previousValue.pipe(mergeMap(el => this.httpClient.patch<any>(`${this.toDoTaskAPI}/${currentValue.id}`, {"toDoAreaId": parentId}, options)))
: this.httpClient.patch<any>(`${this.toDoTaskAPI}/${currentValue.id}`, {"toDoAreaId": parentId}, options);
return previousValue;
}, operations);
}
if (operations !== null){
return (operations as Observable<any>).pipe(mergeMap(e => this.httpClient.patch<any>(`${this.toDoAreaAPI}/${parentId}` , data)));
} else {
return this.httpClient.patch<any>(`${this.toDoAreaAPI}/${parentId}` , data);
}
}
π Though
single
andmultiple
are the natural composite relationship update strategies that are made available out of the box while working with averos, it is possible to imagine and implement any other update strategy, as averos users have full control on both front end services and back ends services.