Message Mapper
Use the Message Mapper pattern to keep domain records independent from channel-specific message records by placing conversion logic in a dedicated mapper.
The pattern is implemented between the message boundary and the domain processing logic. Convert incoming message payloads into domain records before the flow applies business logic, and convert domain records into channel-specific message records before the flow sends them to another endpoint.
Record-to-record mapping
Use record-to-record mapping when both the domain value and the channel payload can be represented as typed records. Create separate record types for the domain model and the message format, then keep the mapping in a reusable data mapper or a dedicated mapper function. Use mapping capabilities for field connections, expressions, and custom transformation logic.
- Visual Designer
- Ballerina Code
- Define separate record types for the domain value and the channel message. See Types.
- Create a reusable data mapper with the domain record as the input and the message record as the output.
- Open the data mapper canvas and connect matching fields, such as
idtoorderId. - Use the expression editor for transformed fields, such as combining names or calculating a total. See Expression editor.
- Use array mappings when the mapper must convert item collections.
- Add a Map Data step in the flow and pass the mapped record to the next service, resource function, or connector call.
function toMessage(Order order) returns OrderMessage {
return {
orderId: order.id,
customerName: order.customer.name,
customerEmail: order.customer.email,
total: from var {unitPrice, quantity} in order.items
let decimal itemTotal = unitPrice * <decimal>quantity
collect sum(itemTotal),
items: order.items
};
}
function toDomain(OrderMessage message) returns Order {
return {
id: message.orderId,
customer: {
name: message.customerName,
email: message.customerEmail
},
items: message.items
};
}
Data-format boundary mapping
Use data-format boundary mapping when the channel sends or receives raw JSON, XML, CSV, or another serialized format. Keep parsing and serialization at the boundary, then call the typed mapper so the main flow works with records instead of raw payloads. For JSON payloads, use type-safe JSON conversion. For XML and CSV payloads, use the corresponding XML processing or CSV and flat file processing guide.
- Visual Designer
- Ballerina Code
- Define the message record type that matches the incoming raw payload.
- For a JSON boundary, add a Call Function step for
jsondata:parseAsTypeand set the result type to the message record. - Add a Map Data step that converts the message record into the domain record.
- Add the domain processing steps after the mapper.
- Before sending a raw response or outbound message, map the domain record back to the channel message record.
- Add a Call Function step for
jsondata:toJsononly when the outbound endpoint requires a raw JSON payload.
import ballerina/data.jsondata;
function readIncoming(json payload) returns Order|error {
OrderMessage message = check jsondata:parseAsType(payload);
return toDomain(message);
}
function writeOutgoing(Order order) returns json {
OrderMessage message = toMessage(order);
return jsondata:toJson(message);
}