In webinar #85 of the Areopa Academy series, Patrick Schiefer — Microsoft MVP — presents a practical tour of object-oriented design patterns applied to Microsoft AL, the language behind Dynamics 365 Business Central. Moderator Luc van Vugt facilitated the session, which was originally delivered at Directions EMEA 2023 and later shared with the broader AL community.
The session covers the foundational concepts that make OO thinking possible in AL, then works through seven concrete patterns with live code demonstrations in VS Code and Business Central.
AL Is Not Object-Oriented — But .NET Is
Patrick opens with four key facts about AL that every developer should understand before reaching for design patterns:
- AL itself is not object-oriented, but every AL codeunit is compiled to C# in the background, so the runtime is fully OO.
- AL distinguishes between value types (Integer, Text, Boolean, Decimal, Char) and reference types (codeunits, interfaces, and any type that exposes procedures).
- Copying a value type copies the value; copying a reference type copies the reference — both variables now point to the same in-memory object.
- This reference behaviour is critical to understand before working with interfaces or any of the patterns below.
Patrick demonstrated the reference behaviour concretely: assigning a codeunit variable to a second variable means both point to the same instance. Changing a field through one variable is immediately visible through the other — a common source of bugs for developers used to procedural C/AL.
📖 Docs: Interfaces in AL — official reference covering interface syntax, the implements keyword, and polymorphic method dispatch, introduced in Business Central 2020 release wave 1.
Interfaces: The Foundation for OO in AL
An interface defines a contract — a set of procedure signatures — that any implementing codeunit must honour. The interface itself contains no code, only signatures.
Key points from the demo:
- A single codeunit can implement multiple interfaces simultaneously using a comma-separated
implementslist. - Variables declared as an interface type can be assigned any codeunit that implements that interface, enabling polymorphism.
- Because codeunits are reference types, an interface variable and the underlying codeunit variable share the same instance — changes through one are visible through all.
- Microsoft’s preferred pattern ties interfaces to extensible enums: each enum value maps to a specific interface implementation, letting ISVs add new implementations by simply extending the enum.
Patrick also noted that codeunit variables assigned to interface variables all reference the same underlying instance. In his demo, three variables — the codeunit itself, and two interface-typed variables pointing at it — all reflected the same value when any one of them was updated.
📖 Docs: Extensible Enums — how to declare enums with Extensible = true and map enum values to interface implementations, the pattern Microsoft uses throughout the System Application.
Design Patterns: Three Categories
Patrick followed the Gang of Four (GoF) taxonomy, grouping patterns into three families:
- Creational — how objects are created (Builder, Factory, Singleton, Lazy Initialisation). Singleton is handled automatically by AL’s
SingleInstanceproperty on codeunits. - Structural — how code and objects are composed (Facade, Adapter, Proxy, Template Method).
- Behavioral — how objects interact and responsibilities are distributed (Command, Command Queue, State, Observer).
The session then worked through seven patterns in detail.
Pattern 1: Template Method (Structural)
Use this when multiple codeunits solve the same kind of problem with different specifics. The pattern extracts the common algorithm into a shared “template” codeunit and delegates only the varying parts to concrete implementations via an interface.
Patrick’s example: two export codeunits — one for sales lines, one for purchase lines — had nearly identical looping, line-reading, and output logic. Only the SetDocument call differed. The refactoring:
- Define an
IExporterinterface with a singleExport()procedure. - Have both concrete codeunits implement
IExporter. - Move the shared loop logic into an
ExportTemplatecodeunit that accepts anIExporterparameter and callsExport()on it.
The benefit: a colleague adding a third export type (say, for service lines) only needs to create a new codeunit that implements IExporter. The template handles the rest, and the new implementation is automatically consistent with all existing exporters.
interface IExporter
{
procedure Export();
}
codeunit 50100 ExportTemplate
{
procedure RunExport(Exporter: Interface IExporter)
begin
// shared loop and output logic
Exporter.Export();
end;
}
codeunit 50101 SalesExporter implements IExporter
{
procedure Export()
begin
// sales-specific logic
end;
}
Pattern 2: Facade (Structural)
The Facade pattern wraps a complex subsystem behind a simple, stable surface. Microsoft uses this extensively in the System Application — the old Language codeunit, for example, is now a thin facade. The real implementation sits in a separate internal codeunit that Microsoft can refactor freely without breaking partner code, as long as the facade’s public signatures remain unchanged.
Patrick’s recommendation: if you build a library or ISV app, put a facade codeunit in front of your internal logic. Mark internal codeunits with Access = Internal. Consumers can only reach the facade; you own the internals.
Pattern 3: Proxy (Structural/Behavioral)
A proxy sits between a caller and a target object, intercepting calls to perform additional checks before delegating to the real implementation. Patrick’s example added a validation layer to a calculator codeunit:
- The real
Calculatecodeunit is markedAccess = Internal. - Both the real codeunit and a proxy codeunit implement the same
ICalculateinterface. - The proxy checks that both operands are positive before forwarding the call; otherwise it raises an error.
A practical BC use case: when integrating with multiple external systems, each system gets its own proxy codeunit that validates headers and authentication before passing data to the shared HTTP call logic.
Pattern 4: Command (Behavioral)
The Command pattern encapsulates an action as an object. This is similar to events, but gives the caller explicit, readable control over what will happen — there is no hidden subscriber list.
The three required elements:
- An
ICommandinterface with a singleExecute()procedure. - Concrete command codeunits that implement
ICommand(e.g.,PrintCommand,EDIExportCommand). - A caller that creates the appropriate command and passes it to the processing codeunit.
In the posting example:
interface ICommand
{
procedure Execute();
}
codeunit 50110 PostDocumentHandler
{
procedure Post(SalesHeader: Record "Sales Header"; Command: Interface ICommand)
begin
// post logic
Command.Execute();
end;
}
codeunit 50111 PrintCommand implements ICommand
{
procedure Execute()
begin
// print the document
end;
}
Adding a new action (e.g., “post and send to web service”) requires only a new command codeunit — the PostDocumentHandler never needs to change.
Pattern 5: Command Queue (Behavioral)
The Command Queue extends the Command pattern to support executing multiple commands in sequence. A queue is a first-in, first-out (FIFO) collection.
Patrick implemented the queue from scratch (because AL collections don’t natively support variant-typed entries) using a QueueEntry codeunit that holds an ICommand and a Next reference. The queue itself tracks First and Last pointers and exposes Push, Pop, GetSize, and ProcessQueue procedures.
ProcessQueue is a simple loop:
procedure ProcessQueue()
begin
while GetSize() > 0 do
Pop().Execute();
end;
Patrick demonstrated a batch-posting use case: the user selects multiple sales orders, adds each to the queue via a “Add to Batch Post” action, then triggers “Execute Batch Post” — all orders are processed in sequence without the user manually posting each one. He pointed out this approach can also be used to implement a simple macro recorder: capture user actions as command objects, store them in a queue, and replay on demand.
📖 Docs: Command Queue Pattern on alguidelines.dev — the community-maintained AL design patterns reference, which includes Patrick Schiefer’s own write-up of this pattern with additional context and code examples.
Pattern 6: State (Behavioral)
The State pattern allows an object to alter its behaviour based on its current state, without the calling code needing to check the state explicitly. Each state is a separate codeunit implementing a shared interface.
Patrick’s login example:
- Interface
ILoginStatedefinesLogin(),Logout(), andGetStateDescription(). LoggedInStatecodeunit:Login()throws “already logged in”;Logout()performs logout and transitions current state toLoggedOutState.LoggedOutStatecodeunit:Logout()throws “already logged out”;Login()performs login and transitions toLoggedInState.- A facade codeunit holds the current state variable and delegates all calls to it.
The result: no IF Status = Status::LoggedIn THEN checks scattered through the code. The state object itself enforces valid transitions. Patrick suggested this maps naturally to BC document statuses (Open, Released, Posted) — each status becomes a state codeunit that defines which operations are legal.
Pattern 7: Lazy Initialisation (Creational)
Lazy initialisation defers the creation or loading of an object until it is actually needed. Patrick showed this is already used in the BC base application — the REST client module uses a GetInitialized helper that checks a flag and initialises the object only on the first call.
The canonical AL example is setup table reads:
local procedure GetSetup()
begin
if SetupInitialized then
exit;
SetupTable.Get();
SetupInitialized := true;
end;
Called at the start of each procedure that needs setup data, this avoids repeated SQL reads across multiple calls in the same session — saving database round-trips without pre-loading data that may never be needed.
📖 Docs: Extend interfaces in AL — available from Business Central 2024 release wave 2, this feature lets you compose interfaces by extending existing ones (interface IFooBar extends IFoo, IBar), and adds theisandasoperators for runtime type checking and casting — building directly on the interface foundations covered in this webinar.
Resources Mentioned
- Patrick Schiefer’s GitHub — source code for all patterns demonstrated in the session (Workshop-Object-oriented-design-patterns repository).
- Design Patterns: Elements of Reusable Object-Oriented Software by the Gang of Four (Gamma, Helm, Johnson, Vlissides) — the foundational reference. Originally written in C++, the patterns translate well to AL.
- alguidelines.dev — community-maintained AL design patterns and best practices reference, including the Command Queue and other patterns from this session.
This post was drafted with AI assistance based on the webinar transcript and video content.
