// article
Events are the primary way to extend Business Central code flows without modifying Microsoft's base application. That makes event subscribers central to upgrade-safe AL development—but a subscriber can still create hidden coupling, slow every transaction, or change behavior in ways that are difficult to diagnose.
The goal is not to subscribe wherever an event exists. The goal is to create the smallest, most explicit extension boundary that preserves the publisher's business invariants.
Business and integration events are published intentionally from AL objects. Trigger events are emitted around table and page triggers. Prefer an event whose name and parameters directly represent the business point you need. A late, generic trigger event may force the subscriber to reconstruct context that an earlier business event already provides.
Read the publisher and surrounding code. Determine whether the event runs before or after validation, inside a transaction, once per document, or once per line. An event name alone does not describe all of those semantics.
Microsoft recommends using an object name in the EventSubscriber attribute. For table events, use Database:: rather than Table::.
// AL
codeunit 50110 "BA Sales Subscribers"
{
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Sales-Post",
'OnAfterPostSalesDoc', '', false, false)]
local procedure OnAfterPostSalesDoc(
var SalesHeader: Record "Sales Header";
var GenJnlPostLine: Codeunit "Gen. Jnl.-Post Line";
SalesShptHdrNo: Code[20];
RetRcpHdrNo: Code[20];
SalesInvHdrNo: Code[20];
SalesCrMemoHdrNo: Code[20];
CommitIsSuppressed: Boolean;
InvtPickPutaway: Boolean;
var CustLedgerEntry: Record "Cust. Ledger Entry";
WhseShip: Boolean;
WhseReceiv: Boolean;
PreviewMode: Boolean)
begin
if PreviewMode then
exit;
QueueFollowUp(SalesHeader.SystemId, SalesInvHdrNo);
end;
local procedure QueueFollowUp(SourceSystemId: Guid; PostedInvoiceNo: Code[20])
begin
// Delegate to the app's durable, idempotent queue implementation.
end;
}
The published signature can change between versions, so use the AL editor's event-subscriber insertion support against the symbols for the target version rather than copying an old signature from a blog. The example illustrates structure and the use of SystemId; it must be compiled against the intended Business Central symbols during review.
A subscriber should usually check applicability and delegate to a named codeunit. This makes the condition visible, allows direct tests of the business behavior, and prevents the event layer from becoming an unstructured collection of transaction logic.
Avoid user interface calls in subscribers that might run from APIs, job queues, or background sessions. Avoid synchronous external HTTP requests in posting flows. Do not add Commit() unless the publisher's contract explicitly requires and permits it; an unexpected commit can break transaction atomicity.
A subscriber can run again because a process is retried, an integration replays work, or another code path publishes the same business event. If it creates a queue entry, notification, or integration record, protect that side effect with a durable key or existence check. Use a record identifier such as SystemId plus the operation type rather than a display number that may be changed or reused.
The final booleans in EventSubscriber control what happens when the session lacks the subscriber codeunit's license or permission. When false, execution stops with an error; when true, Business Central skips the subscriber and continues. Do not set both to true merely to avoid deployment errors. Skipping a critical compliance or accounting rule could be worse than stopping the transaction.
Some events expose an IsHandled pattern that lets a subscriber replace standard behavior. It is powerful but creates a one-subscriber-wins contract and can conflict with other extensions. Prefer additive events when possible. When replacement is necessary, document which standard behavior is bypassed, test coexistence with installed apps, and fail clearly when assumptions are not met.
Add telemetry around the delegated feature, not around every event call. Operations teams need to know whether the feature succeeded, failed, or was intentionally not applicable without collecting customer content.
Bitta Apps reviews inherited subscribers, replaces fragile modifications through AL extension development, and adds the operational controls needed for release-wave support and reliable integrations.