Organizing your “big PTE” in Micro Services

Plans have changed for tonight. Sorry Rene, we’ll try again on thursday. So at home and time to share the next part of the best practices for Per Tenant Extensions.

And just in time, because when I shared part I last night there was confusion right away on Twitter. Especially about confusing shipping all your modifications as one “big” Per Tenant Extensions with making a monolyth.

Let’s look at Microsoft Windows, or Office. Just because Microsoft packages it as s product does not make the development into a monolyth. You can develop in Modules and ship as a software package.

The “Perfect” Example

It’s already a few years ago that Microsoft took our community by surprise and broke the “Functional App” and the “System Modules” into two extensions.

The Functional App is still a Monolyth. It’s almost impossible to break into modules that compile independently. A few years ago we tried with a few people from the community to do so and failed. Mainly because we lacked the availabilty of enums and interfaces but anyway, the cost of doing it does not weigh up against the benefits and the “big app” stayed big.

The System App is a totally different story. Microsoft developed it as modules that compile individually. You can see this on GitHub.

When we get the product on the “DVD” or create a dependency we can see that even though MSFT makes small modules, they ship a big .app file.

This is also normal in other software frameworks and I’ve seen this also for example when a product like ForNAV is packaged. The fact that design.exe is 75MB does not mean it is coded in one large C# file.

How do you implement this in your PTE Project?

The trick to code in modules and ship as software starts with your folder structure.

Please tell me how many times have you seen this as a “structure” for an AL project:

This is horrible and let’s call this an Anti-Pattern from now on.

The Best Practice is to organize your extension in meaningfull foldernames like this

This is not something I “invented” or Microsoft invented. I actually stole this idea from an Uncle Bob video I saw a few years ago. Aparently other programming frameworks also have a tendency to group code by nerdy categories rather than functional elements.

But with our AL framework we have an extra benefit. I’ve marked that benefit in RED.

You see that my “Big App” has an app.json but my “Core App” also has an app.json.

This allows me to open the “Core” folder separate in Visual Studio Code and compile it as a “Micro Service”.

Every Per Tenant Extension has at least two sub modules. Core and Report Pack. With PrintVis we also have “typical PrintVis things” like Calculation Formula’s and Status Code triggers. This will be different for other vertical solutions.

Reusable modules go into Feature folders with their own App.json that compile separately and can be easily reused. I’ll explain in one of the next posts.

Free Documentation

One of the biggest advantages of this way of organizing your code is that the folder stucture becomes your documentation. You can see exactly what the customer wanted customized without even opening an object.

I’ll dive deeper into this in the next blog where I describe the rules for the “Core” extension. Hopefully tomorrow.

Wait a minute Marije, this is not new is it?

No it is not. This way of working could actually have been done in C/Side already and many of us did.

In C/Side we did not have an app.json. Instead we had a “Version List” for an object and a “Documentation” property for table fields.

Many of us have been working like this “unofficially” for decades and had great business succes. Why should we not keep on doing that.

The “Because we can” Anti-pattern

If you ask me, the biggest Anti-pattern we have these days in the Business Central community is the argument “Because we (finally) can”. And this is costing us very valuable time.

I see great AL developers become PowerShell experts, CI/CD gods and Docker Guru’s and all for no reason whatsoever.

We need to look back at C/Side and take back what made us succesful and stop doing things just because we can without adding value for customers.

This and this alone will solve the capactiy problem we have in our community. It is the elephant in the room that for some reason nobody wants to see.


  1. Frédéric Vercaemst says:

    If a feature extends a table, page, … would it get it´s own extension objects? So a same object could possible get extended several times in the ´big´ app. Or do you group these ?


    1. Hi Frederic, you are smart. This is a problem and I think I’ve fixed it. It will be scribbled down in the 4th part of this series (I think) depending on the flow. Thank you for following my blog and the interaction. It is appreciated and makes it more fun.


    2. I had exactly the same question in my mind, while reading the blog post. How do you handle if an object is needed in Core, Feature1, Feature X, …? Looking forward to the answer.
      Another great point is your last statement, we are doing too much stuff around our actual work.


  2. Filip Smets says:

    In my opinion it would add great value to your blog post to add a sentence explaining what you put in the version list and why, as that is a cornerstone of your blog posts ‘conclusion’. Maybe with an example added. In your mind you see this clearly, but different ISVs or VARs put different things in the version list and they might miss the point in matching their way of using the version list to your way of using it. That’s why I think it is necessary to elaborate on what you’d put in the version list in C/AL. Thanks for considering.


  3. Stijn B says:


    I’m all in favour of reducing the number of PTE extension, even to a single one.
    Since this solves the problem of dependencies, as you mentioned.

    But there are some scenarios that I am just not able to solve.
    Consider one has a single PTE extension.
    And in the same environment multiple ISV extension exist, let’s call them A, B & C.

    CRUD operations on data A, B & C in AL code can be done with RecRefs & FieldRefs in the PTE,
    in order to avoid a dependency. Not that much of a big a fan of this (obscurity), but it gets the job done.

    But recrefs & fieldrefs will only get you so far…
    Consider I want to subscribe to event raised in extension A? -> Dependency
    Consider I want to extend a page of extension B? -> Dependency
    Consider I want to extend a table of extension C? -> Dependency

    Result, my single PTE has dependencies on all ISV extensions.
    With the potential danger that a flaw in one of these can bring my entire PTE down.

    The solution to this problem? Isolating the dependency code in a seperate PTE.
    Yes, I’m back on my way creating multiple extensions & dependencies.
    Let’s call them PTE_DEPA, PTE_DEPB & PTE_DEPC.

    For example my Sales Invoice Report resides in the main PTE and I need to print some information
    that resides in the table extension I created in PTE_DEPC. And I’m back to using recrefs & fieldrefs.
    Probably there’s a scenario werhe recrefs & fieldrefs just don’t cut it (doesn’t come to mind right now) that
    require me to add a dependency in the main PTE to PTE_DEPA/B/C.

    Back to square one…

    Dependencies are something that’s here to stay.
    But how these are handled when comes to data CRUD operations… I feel there’s probably some room for improvement by the platform.

    Maybe I’m getting ahead of things since this blogpost series is still running…
    But I interested in your opinion in these matters, these are things we stuggle with on a daily basis.

    Much appreciated!


Leave a Comment

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.