Custom Data Type (CDT) Design Guidance - Appian 22.3
Custom Data Type (CDT) Design Guidance - Appian 22.3
Overview
For most applications, Appian recommends using record types to build with your
enterprise data. When you enable data sync on your record types, you can work with a
faster, more flexible version of your data using record type relationships, custom record
fields, and other low-code data features.
However, there are some cases where you may need to use custom data types (CDTs) in
addition to your record types:
A record type that does not have data sync enabled, so you need to connect to the
source using a data store entity.
You use a data type plug-in to define a CDT as a Java object.
You create a custom document type for use with intelligent document processing.
The document's fields are represented with a CDT.
Your process model includes an Export Data Store Entity to Excel or Export Data
Store Entity to CSV smart service node. Data store entities require a CDT as part of
their configuration.
This page will help you understand how CDT relationships work and what factors to
consider when defining a data model to meet your business needs.
Data design
Data design is an important part of planning your application. Before creating your CDTs,
carefully consider how you want to write data to the database, how you want to use or
display your data, and what types of reports you need. CDT design impacts how you
query data, and by extension, how your application performs.
Data entities can have more than one level, such as an Employee who has a specific
Address. This is represented in Appian by multiple related CDTs.
https://docs.appian.com/suite/help/22.3/cdt_design_guidance.html 1/13
1/2/23, 2:57 PM Custom Data Type (CDT) Design Guidance - Appian 22.3
There are four types of CDT relationships in a data type. Depending on how you will use
your data, some CDT relationships work well in a nested design, while others are better
suited for a flat design.
The reference table below summarizes Appian's recommendations on how to design
your CDTs based on what type of CDT relationship it is.
One-to-One Nested
Many-to-One Nested
One-to-Many Flat
Many-to-Many Flat
Nested CDTs
Appian allows you to create nested designs to capture the complex structures of your
database. With a nested relationship, data lives in one table but provides context for
related table with the use of a foreign key. In Appian, nested relationships are modeled
by referencing the child CDT in the parent CDT like the diagram below.
Appian recommends using nested relationships only for one-to-one and many-to-one
relationships. One-to-many and many-to-many relationships should be flat to avoid
performance issues when querying data.
Pros:
Write one query to retrieve data from multiple tables
https://docs.appian.com/suite/help/22.3/cdt_design_guidance.html 2/13
1/2/23, 2:57 PM Custom Data Type (CDT) Design Guidance - Appian 22.3
Easy to access all fields of the children when you have a one-to-one or many-to-one
relationship
Easier for reports / charts where you want to display information across the parent
and child
Cons:
Less performant for one-to-many or many-to-many relationships
Difficult to work with lists of lists when querying multiple rows that contain one-to-
many or many-to-many relationships
It's a best practice to set up a foreign key in your CDT for nested relationships. This
is done automatically for you when you allow Appian to generate your database
tables.
Flat CDTs
In a flat design, there is no explicit relationship between CDTs in Appian. Neither the
parent or child CDT contains the other. Instead, one CDT contains a reference to the
other CDT's Primary Key (PK). Therefore, querying one table doesn't return all the fields
in the related table, it only returns the primary key.
When a related table is used infrequently, it's better to not build an explicit relationship
into your CDTs. If you include the relationship, the related table is automatically queried
every time the parent is queried.
When the application needs to return data from related tables, the designer will need to
query the related table directly, using the Primary Key values retrieved from the parent
CDTs.
Appian recommends making many-to-many and one-to-many relationships flat. If you
are currently using or designing a many-to-many or one-to-many CDT, it's best practice
to make them independent, but related. Instead of calling another CDT in a CDT Type
field, reference the primary key of the related CDT.
Pros:
https://docs.appian.com/suite/help/22.3/cdt_design_guidance.html 3/13
1/2/23, 2:57 PM Custom Data Type (CDT) Design Guidance - Appian 22.3
Generally more performant, as you can control/limit the number of queries to your
database
Easier to avoid overwriting concurrent changes since you can write to each table in
isolation
Cons:
Requires creation of additional queries
Relationships have to be maintained manually
When using any of the methods below to configure the fields that reference the related
CDT(s), your data type should have the following configurations:
The field name should include the related CDT name and "Id", such as "caseId"
The field type should be the same as the related CDT's primary key
The XSD definition for the field should use the @JoinColumn annotation, as shown in
the example below, where "case_id" is the primary key column on the case table:
https://docs.appian.com/suite/help/22.3/cdt_design_guidance.html 4/13
1/2/23, 2:57 PM Custom Data Type (CDT) Design Guidance - Appian 22.3
Master-detail relationships
Most one-to-one and one-to-many relationships are master-detail relationships, which
means the child CDT is an extension of the parent CDT. The child CDT values are usually
created or updated at the same time as the parent CDT values. Because of this, the
referenced CDT value may not exist when writing the other CDT value for the first time.
For example, when you have an employee CDT that references an address CDT, the
address for that employee doesn’t exist yet when you write an employee to the database
for the first time. This means you can’t associate the two rows if you write the employee
value first. When this happens, you need to write the referenced CDT value (in this case,
the address) to the database first before writing the other CDT (the employee).
To write both values to the database for the first time:
1. In a process model, configure the first Write to Data Store Entity smart service to
write to the referenced CDT value first.
For one-to-one and many-to-one relationships, the CDT is the child
For one-to-many relationships, the CDT is the parent
2. In the Data tab, save the Stored Values into a process variable. This will allow you
to use the id of the referenced table in step 3.
3. Add a Script Task after the Write to Data Store Entity smart service and save the id
from the stored value into the corresponding field in your other CDT.
4. Add a second Write to Data Store Entity smart service after the Script Task to save
the second CDT value.
The result should look like this:
Because multiple smart services are required to write both values for the first time,
you cannot use the Write to Data Store Entity smart service function from an
https://docs.appian.com/suite/help/22.3/cdt_design_guidance.html 6/13
1/2/23, 2:57 PM Custom Data Type (CDT) Design Guidance - Appian 22.3
interface or Web API. Instead, you must use the Start Process smart service function
in order to execute multiple smart services.
This is only necessary when writing the referenced CDT for the first time, to create the
data. If the referenced value already exists, you can use a single Write to Data Store
Entity node if only updating one of the CDTs or the Write to Multiple Data Store Entities
node if updating both CDTs.
Reference relationships
The majority of many-to-one and many-to-many relationships are "reference" or "lookup"
relationships, which means you "lookup" values from the referenced CDT. These values
are managed separately and are rarely created or updated at the same time as the
parent CDT. Because of this, the child CDT will usually already exist when you go to write
the parent CDT to the database. If for some reason the child CDT doesn't exist when
writing to the parent CDT, you'll need to create the child CDT value(s) first before writing
to the CDT that references them.
For example, an order CDT that references a product CDT will have a lookup list of
products that must exist before someone can create an order containing them.
For many-to-one relationships, you can simply save the id of the child into the parent
CDT when writing values to the database. For many-to-many relationships, you'll need to
write the parent CDT to the database before writing values to the third CDT that defines
the relationship.
To write the parent CDT to the database before writing values to a separate CDT:
1. In a process model, configure the first Write To Data Store Entity smart service to
write the parent CDT value first.
2. In the Data tab, save the Stored Values into a process variable. This allows you to
reference the id of the parent CDT in the next step.
3. Add a Script Task to generate the values for the mapping CDT. Use an expression
similar to the following to generate these values:
1 a!foreach(
2 items: pv!productsForOrder,
3 expression: type!ProductsForOrder(
4 orderId: pv!order.id,
5 productId: fv!item.id,
6 index: fv!index
7 )
8 )
9
10
4. Add a Write to Data Store Entity smart service and write the values for the
relationship CDT.
The result should look like this:
https://docs.appian.com/suite/help/22.3/cdt_design_guidance.html 7/13
1/2/23, 2:57 PM Custom Data Type (CDT) Design Guidance - Appian 22.3
Because multiple smart services are required to write both values for the first time,
you cannot use the Write to Data Store Entity smart service from an interface or
Web API. Instead, you must use the Start Process smart service in order to execute
multiple smart services.
https://docs.appian.com/suite/help/22.3/cdt_design_guidance.html 8/13
1/2/23, 2:57 PM Custom Data Type (CDT) Design Guidance - Appian 22.3
1 a!localVariables(
2 /* Query the parent */
3 local!order: a!queryEntity(
4 entity: cons!ORDER_ENTITY,
5 query: a!query(
6 filter: a!queryFilter("id", "=", ri!orderId),
7 pagingInfo: a!pagingInfo(1, 1)
8 )
9 ).data,
10 /* Query the many-to-one child */
11 local!status: a!queryEntity(
12 entity: cons!ORDER_STATUS_ENTITY,
13 query: a!query(
14 filter: a!queryFilter("id", "=", local!order.statusId),
15 pagingInfo: a!pagingInfo(1, 1)
16 )
17 ).data,
18 /* Query to get the many-to-many child ids */
19 local!productIds: index(a!queryEntity(
20 entity: cons!PRODUCTS_FOR_ORDER_ENTITY,
21 query: a!query(
22 /* You don't need all columns, just the ids */
23 selection: a!querySelection({
24 a!queryColumn("productId")
25 }),
26 filter: a!queryFilter("orderId", "=", ri!orderId),
27 pagingInfo: a!pagingInfo(1, -1, a!sortInfo("order", true))
28 )
29 ).data, "productId", {}),
30 /* Query to get the many-to-many children */
31 local!products: a!queryEntity(
32 entity: cons!PRODUCT_ENTITY,
33 query: a!query(
34 filter: a!queryFilter("id", "in", local!productIds),
35 pagingInfo: a!pagingInfo(1, -1)
36 )
37 ).data,
38
39 ...
40
41 )
42
43
https://docs.appian.com/suite/help/22.3/cdt_design_guidance.html 9/13
1/2/23, 2:57 PM Custom Data Type (CDT) Design Guidance - Appian 22.3
The expression below queries children for multiple parents using the example of orders
with a status (many-to-one) and products (many-to-many):
https://docs.appian.com/suite/help/22.3/cdt_design_guidance.html 10/13
1/2/23, 2:57 PM Custom Data Type (CDT) Design Guidance - Appian 22.3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
https://docs.appian.com/suite/help/22.3/cdt_design_guidance.html 11/13
1/2/23, 2:57 PM Custom Data Type (CDT) Design Guidance - Appian 22.3
53 a!localVariables(
54 local!pagingInfo: a!pagingInfo(1, 25),
55 /* Query the parent array */
56 local!orders: a!queryEntity(
57 entity: cons!ORDER_ENTITY,
58 query: a!query(pagingInfo: local!pagingInfo)
59 ),
60 local!uniqueStatusIds: union(index(local!orders.data, "statusId", {}), {}),
61 /* Query the many-to-one children */
62 local!statuses: a!queryEntity(
63 entity: cons!ORDER_STATUS_ENTITY,
64 query: a!query(
65 filter: a!queryFilter("id", "in", local!uniqueStatusIds),
66 pagingInfo: a!pagingInfo(1, -1)
67 )
68 ).data,
69 /* Query to get the many-to-many children relationships */
70 local!productsForOrders: a!queryEntity(
71 entity: cons!PRODUCTS_FOR_ORDER_ENTITY,
72 query: a!query(
73 filter: a!queryFilter("orderId", "in", local!orders.id),
74 pagingInfo: a!pagingInfo(1, -1, a!sortInfo("order", true))
75 )
76 ).data,
77 local!uniqueProductIds: union(index(local!productsForOrders, "productId", {}), {
78 /* Query to get the many-to-many children */
79 local!products: a!queryEntity(
80 entity: cons!PRODUCT_ENTITY,
81 query: a!query(
82 filter: a!queryFilter("id", "in", local!uniqueProductIds),
pagingInfo: a!pagingInfo(1, -1)
)
).data,
/* Match up the parent and child values */
local!ordersWithChildren: a!foreach(
items: local!orders,
expression: a!localVariables(
local!productIds: index(
local!productsForOrders.productId,
wherecontains(fv!item.id, tointeger(local!productsForOrders.orderId)),
{}
),
{
order: fv!item,
status: displayvalue(
fv!item.statusId,
local!statuses.id,
local!statuses,
null
),
/* Flatten the list of products for each order into one string for display
productNames: joinarray(
index(
https://docs.appian.com/suite/help/22.3/cdt_design_guidance.html 12/13
1/2/23, 2:57 PM Custom Data Type (CDT) Design Guidance - Appian 22.3
index(
local!products.name,
wherecontains(local!productIds, local!products.id),
{}
),
", "
)
}
)
),
a!gridField(
label: "Orders",
data: local!ordersWithChildren,
columns: {
a!gridColumn(
label: "Description",
value: reduce(fn!index(_, _, {}), fv!row, {"order", "description"}),
),
a!gridColumn(
label: "Status",
value: reduce(fn!index(_, _, {}), fv!row, {"status", "name"})
),
a!gridColumn(
label: "Products",
value: index(fv!row, "productNames", {})
)
},
validations: {}
)
)
https://docs.appian.com/suite/help/22.3/cdt_design_guidance.html 13/13