Video: Add events to your apps (and doing some refactoring)

In this video, I’ll start refactoring the USPS app and discuss and show how to add events to your app in order to be a good app-citizen.

I’ll also show some good ideas for refactoring your code to make it more extendable and easier to maintain.


In this episode, Erik refactors the US Postal Service Business Central app’s management codeunit and adds integration events to make the app extensible. The focus is on breaking large functions into smaller, more focused procedures and ensuring that other developers can extend the app by subscribing to well-placed events throughout the data flow.

Why Refactor and Add Events?

The app has been taking shape over previous episodes — including a thorough pass with code analyzers — but the code still has room for improvement. Beyond clean code, there’s the concept of being a good app citizen. This means building an app that other developers can extend. Since this app already extends the base app, it’s important to consider: what if someone installs this app and wants to extend it? Perhaps they need to use an additional field from the US Postal Service response, or they have a custom address field on the customer or vendor table.

The answer is to add events at strategic points throughout the codeunit and to refactor the code into smaller, more focused functions — which also makes future testing much easier.

Breaking Down the Apply Address Function

The first target is the ApplyAddress function, which takes a compare record (the result from US Postal Service) and applies fields to customer or vendor records. It contains a case statement that branches for customers and vendors — a classic place where code tends to bloat.

Erik references an old NASA programming guideline: if a function is longer than a page (about 25 lines on old terminals), it should probably be split into smaller functions. If you can’t read it easily in one glance, it can likely be trimmed down.

Using the AL Code Actions extension in Visual Studio Code, the customer-specific code is selected and extracted into a new sub-procedure called ApplyToCustomerRec, and the vendor-specific code is extracted into ApplyToVendorRec. After removing unnecessary begin/end blocks and unused variables, the original function shrinks from a full page down to about seven lines.

One nice detail: the AL Code Actions extension intelligently inserts extracted procedures before any event declarations at the bottom of the codeunit, showing a clear understanding of typical codeunit structure.

Refactoring the Web Service Call

The original PrepareXML function was doing far more than its name suggested — it prepared the XML, called the web service, and handled the result. Erik renames the outer function to VerifyAddressWithUSPostalService and extracts the actual XML preparation back into a dedicated PrepareXML procedure. The result handling is extracted into a ProcessResults procedure. The raw web service call becomes its own CallWebService function.

This gives a clear separation of concerns:

  • VerifyAddressWithUSPostalService — the main orchestrator
  • PrepareXML — builds the XML request from the compare record
  • CallWebService — makes the technical HTTP call
  • ProcessResults — handles the XML response

Integration Events vs. Business Events

Before adding events, Erik explains the difference between the two types of custom events in AL:

  • Business Events — Define a formal contract with an implicit promise not to change in future releases. These are the expectation for solutions published to AppSource, including Microsoft’s own.
  • Integration Events — Also custom events raised by AL code, but they do not carry the same promise of stability, nor do they have restrictions against exposing implementation details.

Since the app might still change, Erik chooses integration events as the more appropriate option.

The IncludeSender Parameter

Integration events have an IncludeSender parameter. Setting this to true passes the codeunit instance to event subscribers, which is useful when the codeunit has global state that subscribers need to access. Since this codeunit has no global variables (and shouldn’t — Erik strongly advocates against global variables in codeunits), IncludeSender is set to false.

Adding Events Throughout the Data Flow

Events are added at four strategic points to cover the complete round-trip of data:

1. Before Modifying Customer/Vendor Records

In the ApplyToCustomerRec and ApplyToVendorRec procedures, events are added so that subscribers can set additional fields on the record before it’s modified:

[IntegrationEvent(false, false)]
local procedure OnBeforeModifyCustomer(var Customer: Record Customer; var AddressVerify: Record "US Postal Service Address Verify")
begin
end;

[IntegrationEvent(false, false)]
local procedure OnBeforeModifyVendor(var Vendor: Record Vendor; var AddressVerify: Record "US Postal Service Address Verify")
begin
end;

This allows a subscriber to do something like:

Customer."Some Field" := AddressVerify."Some Other Field";

2. After Applying Customer/Vendor Data to the Address Verify Record

When preparing the request, events are added so subscribers can populate additional fields on the compare record from customer or vendor data:

[IntegrationEvent(false, false)]
local procedure OnAfterApplyingCustomerToAddressVerify(var AddressVerify: Record "US Postal Service Address Verify"; Customer: Record Customer)
begin
end;

[IntegrationEvent(false, false)]
local procedure OnAfterApplyingVendorToAddressVerify(var AddressVerify: Record "US Postal Service Address Verify"; Vendor: Record Vendor)
begin
end;

3. After Adding Fields to the XML Request

In the PrepareXML procedure, an event allows subscribers to add custom XML elements to the request being sent to the US Postal Service:

[IntegrationEvent(false, false)]
local procedure OnAfterAddingFieldsToXML(var AddressElement: XmlElement; AddressVerify: Record "US Postal Service Address Verify")
begin
end;

4. After Processing the Response

In the ProcessResults procedure, an event allows subscribers to extract additional data from the US Postal Service response:

[IntegrationEvent(false, false)]
local procedure OnAfterGettingDataFromUSPostalService(var AddressVerify: Record "US Postal Service Address Verify"; ResultDocument: XmlDocument)
begin
end;

The Complete Extension Story

With these four event points, a developer extending this app can:

  1. Add custom fields to the customer/vendor tables and the address verify table via table extensions
  2. Populate the address verify record with their custom data from the customer/vendor (event 2)
  3. Include that data in the XML request to the US Postal Service (event 3)
  4. Extract custom response data from the US Postal Service result (event 4)
  5. Apply that data back to the customer/vendor record (event 1)

Summary

This episode covered two complementary improvements to the US Postal Service app. First, large functions were broken down into smaller, single-purpose procedures using the AL Code Actions extension’s extract sub-procedure feature. Second, integration events were added at key points in the data flow — when preparing requests, building XML, processing results, and applying changes — to make the app fully extensible. These smaller functions also lay the groundwork for easier unit testing in a future episode. The goal throughout is to be a good app citizen: write clean, focused code and give other developers the hooks they need to extend your work without modifying it.