In this video, I show how to extend an AppSource app using the new Interface object type. The Cloud Replicator from E Foqus is built around an interface, making it possible to add support for new databases.

In this video, Erik demonstrates how to use interfaces in AL to extend a Business Central app published on AppSource. Using a real-world example — the Cloud Replicator app from eFoqus — he walks through the process of adding a new data destination (MongoDB) by implementing an interface exposed by the app. This is a practical guide to one of the more powerful extensibility patterns available in modern AL development.
What Is an Interface?
An interface is essentially a contract. It declares a set of functions that any implementing codeunit must provide. The interface itself doesn’t contain any logic — it simply says “these functions must exist.” One or more codeunits can then implement that interface, each providing their own version of the logic.
While this concept has existed in other programming languages for decades, it’s relatively new to AL. Erik notes that he hasn’t seen many examples of interfaces being used in the wild yet, but the Cloud Replicator app was designed from the ground up with interfaces in mind — making it a perfect case study.
The Scenario: Extending the Cloud Replicator
The Cloud Replicator is a replication engine that replicates data from Business Central to a cloud database. It ships with several built-in destinations, but what if you want to replicate to a destination that isn’t on the list — say, MongoDB?
Because the app exposes an interface, you can add your own destination without modifying the original app. The entire extension requires just three AL files and a dependency declaration.
Step 1: Add the Dependency
First, create a new blank AL app and add a dependency to the Cloud Replicator in your app.json. You can find the app ID, name, publisher, and version in Extension Management within Business Central.
{
"id": "f3845c99-5b7d-4fff-8cdf-04473a67e746",
"name": "Interface",
"publisher": "Default publisher",
"version": "1.0.0.0",
"brief": "",
"description": "",
"privacyStatement": "",
"EULA": "",
"help": "",
"url": "",
"logo": "",
"dependencies": [
{
"id": "18a4d438-88d1-44ee-a38d-3c0ea0d77338",
"name": "Cloud Replicator",
"publisher": "E Foqus Canada Inc.",
"version": "1.0.0.22"
}
],
"screenshots": [],
"platform": "1.0.0.0",
"application": "18.0.0.0",
"idRanges": [
{
"from": 50100,
"to": 50149
}
],
"contextSensitiveHelpUrl": "https://Interface.com/help/",
"showMyCode": true,
"runtime": "7.0"
}
After adding the dependency, download symbols so that you can browse the enums, interfaces, and other objects exposed by the Cloud Replicator.
Step 2: Extend the Enum
Looking at the symbols, we can see the Cloud Replicator has an enum called Data Destination EFQ with five existing values. We need to extend this enum to add our new MongoDB destination:
enumextension 50100 "My Destination" extends "Data Destination EFQ"
{
value(99; MongoDB)
{
Caption = 'MongoDB';
}
}
Erik uses value 99 rather than 6 to leave room for the original publisher to add more values without causing conflicts. After deploying this, the MongoDB option appears in the destination dropdown on the Cloud Replicator’s table mapping page.
Step 3: Implement the Interface
Selecting MongoDB as a destination and trying to run the replication results in an error because nothing is actually handling that destination yet. We need to create a codeunit that implements the interface exposed by the Cloud Replicator.
By browsing the symbols, we can see the app exposes an interface called DataProviderEFQ with five functions: Connect, CheckTable, AddTable, Insert, and DeleteTable.
codeunit 50100 "MongoDB" implements DataProviderEFQ
{
procedure Connect(): Boolean
begin
Error('MongoDB is not yet implemented!');
end;
procedure CheckTable(TableName: Text): Boolean
begin
end;
procedure AddTable(TableName: Text): Boolean
begin
end;
procedure Insert(Ref: RecordRef; AddDateToPrimaryKey: Boolean; AddDate: Date): Boolean
begin
end;
procedure DeleteTable(TableName: Text): Boolean
begin
end;
}
As soon as you add implements DataProviderEFQ to the codeunit declaration, the compiler shows squiggly lines for every function that hasn’t been implemented yet. As you add each procedure, the errors disappear one by one. Once all five functions are present, the contract is fulfilled and the compiler is happy.
A few notes on the Insert procedure parameters:
- Ref (RecordRef) — The record to be replicated to the cloud destination.
- AddDateToPrimaryKey (Boolean) — Supports a feature where a new copy of a table is created each day to build timeline data.
- AddDate (Date) — Ensures that even if replication runs across midnight, all data within the same replication run is tagged with the same date.
Step 4: Wire It Up with an Event Subscriber
Now we need to make sure our codeunit gets selected when MongoDB is chosen as the destination. The Cloud Replicator engine exposes an event called OnSelectingCustomDestination for exactly this purpose:
codeunit 50101 "Wire up Codeunit"
{
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Cloud Replicator Engine EFQ", 'OnSelectingCustomDestination', '', true, true)]
local procedure SelectingCustomDestination(TableMap: Record "Replicator Table Mapping EFQ"; var Destination: Interface DataProviderEFQ; var Handled: Boolean)
var
MongoDB: Codeunit MongoDB;
begin
if TableMap.Destination = TableMap.Destination::MongoDB then begin
Destination := MongoDB;
Handled := true;
end;
end;
}
The event provides three parameters:
- TableMap — The record containing the table mapping configuration, including which destination was selected.
- Destination — An interface variable that we need to assign our implementing codeunit to.
- Handled — A boolean we set to
trueto tell the engine that we’ve handled this destination.
The key line is Destination := MongoDB; — this is how you make an interface type variable work: you assign it to a codeunit that implements the interface. Once assigned, the engine can call any of the interface’s functions and our MongoDB codeunit’s code will execute.
Testing It Out
After deploying, Erik configures a table mapping to replicate the Currency table to MongoDB and hits “Manual Run.” The debugger stops on the Connect function, which shows the error message: “MongoDB is not yet implemented!” — confirming that the entire chain works correctly. The interface was resolved, the right codeunit was selected, and the function was called.
Why Interfaces Matter for Extensibility
Erik highlights several advantages of this pattern:
- Clean extensibility — You implement a codeunit and fulfill a contract. No need for complex event chains or hacking into someone else’s code.
- Testability — Testing events is hard because you’re testing code inside someone else’s extension. With interfaces, you can easily create test codeunits that verify your implementation independently.
- Designed for extension — The Cloud Replicator was built from the bottom up with interfaces. All the built-in engines use the same pattern, so adding a new one is just doing more of what the app already does.
- Minimal plumbing — The entire extension is just an enum value, a codeunit implementing the interface, and an event subscriber to wire it up. The rest is your actual business logic.
Summary
To extend an AppSource app using interfaces in AL, the process boils down to four steps:
- Add a dependency to the target app and download symbols.
- Extend the enum to add your new option value.
- Create a codeunit that implements the exposed interface, fulfilling all required functions.
- Subscribe to an event to wire your codeunit to the appropriate selection logic.
Erik encourages developers to both look for interfaces in apps they work with and to add interfaces to their own apps. There are also interfaces in the base app (such as for sales prices) that are worth exploring. The more the community adopts this pattern, the easier it becomes to build truly extensible solutions in Business Central.