DYNEXAL GUIDE • AL ARCHITECTURE

Business Central Extension Architecture: A Practical AL Developer Guide

Writing AL code that works is only the first step. A Business Central extension also needs a structure that remains understandable when the project grows, requirements change, integrations are added and multiple developers work on the same solution.

Architecture principle: keep data, user interface, business logic, integrations and extension points separated enough that each part can evolve without creating unnecessary dependencies.

1. What is extension architecture in Business Central?

Extension architecture is the way an AL solution is divided into objects and responsibilities. Business Central provides tables for data, pages for user interaction, codeunits for reusable business logic, reports for output, interfaces for interchangeable implementations, events for extensibility, and permission-related objects for controlled access.

Microsoft's AL documentation describes these objects as core building blocks for developing Business Central applications. A good architecture uses those building blocks deliberately instead of putting all logic into pages or table triggers. Microsoft Learn — Developing Business Central Apps with AL.

2. Start with business capabilities, not just object types

A common beginner approach is to create a table, then place everything related to the feature inside its page. That can work for a small proof of concept, but larger solutions benefit from organizing the design around business capabilities.

Business Requirement
        │
        ▼
Application / Feature
        │
 ┌──────┼────────┐
 ▼      ▼        ▼
Data    Logic   Integration
 │       │        │
Table   Codeunit  API / HttpClient
 │       │        │
Page    Events   External system

For example, an “AI Customer Analysis” feature might contain customer context tables, a setup page, an analysis page, a service codeunit, an AI provider interface, an event subscriber and an integration layer. This makes the feature easier to test and replace later.

3. Give each object a clear responsibility

Tables — own the data model

Tables should define fields, keys, relationships and data-related behavior. Avoid turning a table into a large service layer containing unrelated integration and orchestration logic.

Pages — own the user experience

Pages should present and collect information. When a button needs to execute a substantial business process, prefer calling a dedicated codeunit or service method instead of embedding a large procedure directly in the page action.

Codeunits — own reusable business logic

Codeunits are a natural place for processes that may be called from pages, APIs, job queues, event subscribers or other application components. A well-designed codeunit exposes focused procedures rather than becoming a “god codeunit” containing every feature in the application.

Reports — own reporting and document output

Keep report-specific dataset and layout concerns inside report objects. If the report needs complex reusable business calculations, move those calculations into appropriate application services instead of duplicating them across reports.

4. Separate orchestration from implementation

One useful pattern is to have an orchestration codeunit coordinate a process while smaller services perform individual responsibilities.

procedure RunCustomerAnalysis(CustomerNo: Code[20])
var
    ContextBuilder: Codeunit "AI Customer Context";
    Analyzer: Codeunit "AI Customer Analyzer";
begin
    ContextBuilder.Build(CustomerNo);
    Analyzer.Analyze(CustomerNo);
end;

The exact implementation will depend on the project, but the design idea is important: the orchestration layer should explain what happens, while specialized components own how each step happens.

5. Use interfaces when implementations may change

Interfaces are useful when the application needs a stable contract but may have multiple implementations. For example, an AI feature could support different providers, or an integration could have different transport implementations.

interface "AI Provider"
{
    procedure Analyze(Context: Text): Text;
}

codeunit "Provider A" implements "AI Provider"
{
    procedure Analyze(Context: Text): Text
    begin
        // Provider-specific implementation
    end;
}

The interface keeps the calling code focused on the capability instead of the concrete provider. Microsoft documents interfaces as an extensibility mechanism that allows different implementations to satisfy the same contract. Microsoft Learn — Interfaces in AL.

Interview point: if asked why you use an interface, explain that it reduces coupling and makes it possible to change or add implementations without rewriting the consumer of the service.

6. Use events to create extension points

Events allow custom functionality to react to application behavior without directly modifying the publisher's implementation. This is especially useful when building an extension on top of standard Business Central functionality.

[EventSubscriber(ObjectType::Codeunit,
    Codeunit::"My Sales Service",
    'OnAfterProcessOrder',
    '', false, false)]
local procedure OnAfterProcessOrder(OrderNo: Code[20])
begin
    // Extension-specific follow-up logic
end;

Microsoft describes events as a way to separate custom functionality from application business logic, which can reduce the cost of modifications and upgrades. The EventSubscriber attribute is used on subscriber methods in codeunits. Types of events for extensibility · EventSubscriber attribute.

7. Keep integrations outside core business logic

External integrations should not spread HTTP calls, JSON parsing and authentication details throughout the application. Create a focused integration layer instead.

Business Process
      │
      ▼
Integration Service
      │
 ┌────┴─────┐
 ▼          ▼
Auth      Payload
 │          │
 └────┬─────┘
      ▼
 HttpClient / API
      │
      ▼
External System

For a Shopify, payment, AI or custom REST integration, the business layer should ideally request an operation such as SendOrder or AnalyzeCustomer. The integration layer should handle endpoint details, headers, JSON serialization, response validation and error mapping.

8. Design security into the architecture

Security should not be added after development. Decide early which users can read or modify the data, which codeunits perform sensitive actions, and which integration credentials are required.

AL provides permission-set objects and other mechanisms for controlling access to application data and functionality. Microsoft Learn — AL development reference.

9. Build for testability

A large procedure that reads data, calls an API, transforms JSON and posts a document in one block is difficult to test. Split the workflow into smaller units with clear inputs and outputs.

Input
  ↓
Validation
  ↓
Context / Data preparation
  ↓
Business calculation
  ↓
External integration
  ↓
Response validation
  ↓
Business action
  ↓
Result

This structure makes it easier to test validation failures, API failures and business-rule scenarios independently. It also makes troubleshooting easier when a production process fails.

10. Think about performance while designing

Architecture and performance are connected. Avoid unnecessary database reads, repeated calculations and expensive work inside page triggers. When processing large read-only datasets, Business Central supports patterns such as partial records and set-based operations.

Microsoft's developer performance guidance specifically discusses efficient data access, partial records, set-based methods, asynchronous processing and performance testing. Microsoft Learn — Performance articles for developers.

For example, when only a few fields are needed during a read operation, SetLoadFields can limit the fields initially loaded:

Item.SetLoadFields("No.", "Description", "Inventory");

if Item.FindSet() then
    repeat
        // Read only the fields required by this process.
    until Item.Next() = 0;

Partial records can reduce the amount of data loaded, but they require careful handling because accessing fields that were not initially loaded can trigger additional loading. Microsoft Learn — Using partial records.

11. Move non-interactive work to background processing

Long-running or non-urgent work may be better suited to background execution. Business Central supports job queues, scheduled tasks and page background tasks for different asynchronous scenarios.

For example, an integration that synchronizes thousands of records does not necessarily need to block the user interface. A job queue can process recurring work and provide logging and telemetry. Page background tasks can be useful for read-only computations that would otherwise slow down a page. Task Scheduler · Job Queue.

12. A practical project structure

A project does not have to use exactly these filenames, but a predictable structure can make a growing AL solution easier to navigate.

src/
├── Tables/
├── TableExtensions/
├── Pages/
├── PageExtensions/
├── Codeunits/
│   ├── Application/
│   ├── Integration/
│   ├── Services/
│   └── Subscribers/
├── Interfaces/
├── Enums/
├── Reports/
├── ReportExtensions/
├── Queries/
├── Permissions/
└── Tests/

The important part is not the folder names. The important part is that a developer can quickly identify where data, UI, business logic, integrations, events and tests belong.

13. Common architecture mistakes

14. Architecture for a real AI + Business Central project

For an AI-enabled Business Central solution, a practical high-level design can look like this:

Business Central UI
        │
        ▼
Application Service
        │
 ┌──────┼─────────┐
 ▼      ▼         ▼
Data   Rules    AI Provider
 │      │         │
 ▼      ▼         ▼
BC DB  Validation  Secure API
          │
          ▼
     Human Review
          │
          ▼
   Controlled Action

The AI component should not automatically become the owner of business decisions. The application should control what data is supplied, what actions are permitted, how responses are validated and when a human must approve a transaction.

15. Interview-ready answer

Sample answer: “When I design a Business Central extension, I first separate responsibilities. Tables handle data, pages handle the UI, codeunits handle reusable business logic, interfaces handle interchangeable implementations, and events provide extension points. I keep external integrations in a dedicated layer, apply permissions from the beginning, and design processes so they can be tested independently. For high-volume workloads, I consider efficient data access and background processing. The goal is an extension that is maintainable, testable, secure and upgrade-friendly.”

Conclusion

Good Business Central architecture is not about creating the maximum number of objects. It is about giving each component a clear responsibility and controlling the dependencies between components.

For an AL developer, the progression is straightforward: first learn the objects, then learn how they interact, then learn how to isolate responsibilities, and finally learn how to design for testing, security, performance and upgrades.

Sources & references

← Back to Dynexal Insights