D365FO integration using Business Events
In the modern ERP landscape, "real-time" is no longer a luxury—it’s a requirement. Dynamics 365 Finance & Operations (D365FO) meets this demand through Business Events.
Instead of external systems constantly "polling" D365FO for changes (which is resource-heavy and slow), Business Events flip the script. The system actively pushes a notification the moment a business process—like a Purchase Order confirmation or a Workflow approval—occurs.
1. The Architecture: How It Works
The Business Events framework is built for high-performance, asynchronous communication. It is designed to send lightweight payloads to external "endpoints."
Trigger: A business process completes (e.g., an invoice is posted).
Business Event: The framework captures the event and its associated data (Contract).
Outbound Queue: The event is placed in a staging table.
Batch Processing: Dedicated batch threads pick up these events and send them to the configured endpoint.
Endpoint: A listener (like Power Automate or Azure Service Bus) receives the JSON payload.
Key Configuration Parameters
In System administration > Setup > Business events > Business events parameters, you can tune the engine:
Retry count: Defaults to 3.
Bundle size: Determines how many events are grouped for a single thread.
Processing threads: Up to 4 dedicated threads per AOS.
2. Supported Endpoints
D365FO doesn't "know" what the external system is; it only knows where to send the data. Common endpoints include:
| Endpoint Type | Best For... |
| Azure Service Bus (Queue/Topic) | High-volume, reliable enterprise messaging. |
| Azure Event Grid | Highly scalable, event-driven reactive programming. |
| Power Automate (HTTPS) | Low-code automation and simple notifications (Email/Teams). |
| Azure Event Hubs | Big data streaming and telemetry. |
3. Creating a new Business Event
//Decorate the business event classBusinessEvents(classStr(VendorCreatedContract), "VendorCreated", "Vendor Created Event", ModuleAxapta::Vendor)]public final class VendorCreatedBusinessEvent extends BusinessEventsBase{private VendTable vendTable;//Create a parameter for the vend tableprivate VendTable parmVendtable(VendTable _VendTable =VendTable){VendTable = _VendTable;return VendTable;}// Create an instance of the Business Eventpublic static VendorCreatedBusinessEvent newFromVendTable(VendTable _vendTable){VendorCreatedBusinessEvent event = new VendorCreatedBusinessEvent();event.parmVendTable(_vendTable);return event;}//Initialise the Business event contract[Wrappable(true), Replaceable(true)]public BusinessEventsContract buildContract(){return VendorCreatedContract::newFromVendTable(vendTable);}}
/// <summary>/// The data contract for a VendorCreatedBusinessEvent/// </summary>[DataContract]public final class VendorCreatedContract extends BusinessEventsContract{private VendAccount vendAccount;private VendGroup groupId;private Currencycode currencycode;/// <summary>/// Creates a VendorCreatedContract from a VendTable record./// </summary>/// <param name = "_VendTable"> VendTable record</param>/// <returns>A VendorCreatedContract</returns>public static VendorCreatedContract newFromVendTable(VendTable _vendTable){var contract = new VendorCreatedContract();contract.initialize(_vendTable);return contract;}//Payloads definitionprotected void initialize(VendTable _vendTable){vendAccount = _vendTable.VendAccount;vendGroup = _vendTable.vendGroup;currencycode = _vendTable.currency;}// New instanceprivate void new(){}//Vendor account[DataMember('VendAccount'), BusinessEventsDataMember("@AccountsReceivable:VendAccount")]public VendAccount parmVendAccount(VendAccount _VendAccount= VendAccount){VendAccount = _VendAccount;return VendAccount;}//Vendor group[DataMember('VendGroup'), BusinessEventsDataMember("@AccountsReceivable:VendGroup")]public VendGroup parmVendGroup(VendGroup _VendGroup = VendGroup){VendGroup = _VendGroup;return VendGroup;}//Currency code[DataMember('CurrencyCode'), BusinessEventsDataMember("@AccountsReceivable:CurrencyCode")]public CurrencyCode parmCurrencyCode(CurrencyCode _CurrencyCode = CurrencyCode){CurrencyCode = _CurrencyCode;return CurrencyCode;}}
public static class DevVendorCreatedBusinessEventTrigger_Extension { /// <summary> ///Send the business event on vendor record creation. /// </summary> /// <param name="sender">Vendor Table</param> /// <param name="e"></param> [DataEventHandler(tableStr(VendTable), DataEventType::Inserted)] public static void VendTable_onInserted(Common sender, DataEventArgs e) { VendTable vendTable = sender; VendorCreatedBusinessEvent businessEvent = VendorCreatedBusinessEvent::newFromVendTable(vendTable); if(businessEvent) { businessEvent.send(); } } }
4. You can then call the business event trigger on:a. Azure Service Bus.b. Power automate.C. Azure logic apps
4. Pro-Tips for Implementation
Rebuild the Catalog: After deploying new X++ code, go to the Business Events Catalog and click Manage > Rebuild business events catalog to see your new event.
Keep Payloads Slim: Business events are not for bulk data transfer. If you need to send 1,000 fields, send the Primary Key via a Business Event and have the external system call an OData entity to fetch the rest or Data Management Framework.
Idempotency: The framework includes a
ControlNumberin the payload. Use this in your consuming system to ensure you don't process the same event twice.

Comments
Post a Comment