AL and X++ side by side — reading the target language from day one
A genuine side-by-side comparison of AL and X++ for Business Central developers: table extensions, event subscribers versus Chain of Command, codeunit procedures versus object-oriented classes, Rec/xRec versus table buffers, trigger patterns versus attributes, error handling, transactions, Commit semantics, and data-access filtering — shown in real code both ways so a BC developer can read D365 code on day one.
What you will be able to do
Read and understand D365 X++ code on the first day of working with the target platform
Translate AL table extension patterns into X++ table extensions with Chain of Command
Distinguish when to use Chain of Command versus a delegate-based event handler in D365
Map Rec and xRec usage to X++ table buffer patterns and understand the lifecycle differences
Convert AL filtering patterns (SetRange, SetFilter) to X++ select statements and query objects
Explain the difference between AL Commit and X++ ttscommit reference-counted transactions
Introduction
This chapter exists so that a Business Central developer can open a D365 X++ source file and read it on day one. It is not a tutorial — it assumes you know AL well and have already read chapter 1's structural overview.
```xpp [ExtensionOf(tableStr(SalesTable))] final class SalesTable_CreditCheck_Extension { public boolean validateField(FieldId _fieldId) { boolean ret = next validateField(_fieldId);
Codeunit versus class
```al codeunit 50102 "Inventory Allocation" { procedure AllocateStock(ItemNo: Code[20]; LocationCode: Code[10]; Qty: Decimal): Boolean var Item: Record Item; AvailableQty: Decimal; begin Item.Get(ItemNo); AvailableQty := Item.CalcAvailableQty(LocationCode); if AvailableQty >= Qty then begin CreateAllocation(ItemNo, LocationCode, Qty);
Rec/xRec versus the X++ table buffer
Rec — the current record state (after the change) xRec — the previous record state (before the change)
Error handling: Error() versus throw/try-catch
AL error handling operates on a boundary model: Error() unwinds to the last Commit or Codeunit.Run boundary if Codeunit.Run(...) catches any error raised inside the codeunit There is no try/catch in the traditional sense (AL introduced TryFunction attributes later)
Transactions: Commit versus ttsbegin/ttscommit
```al procedure PostInvoice(var SalesHeader: Record "Sales Header") begin // Implicit transaction starts ValidateHeader(SalesHeader); CreateLedgerEntries(SalesHeader); Commit(); // <-- Hard commit. Everything above is now persisted.
To tie everything together, here is a complete example showing how a BC posting extension translates to D365.
Knowledge check
Summary
AL and X++ solve the same problem — extending a standard ERP without breaking it — through different language paradigms. The BC developer arrives with the right instincts (extend, don't modify; validate before posting; think in transactions) but must learn different syntax and, in places, genuinely different semantics.