We've reviewed the tooling for each phase (design-time, compile-time and run-time) and even discussed the file formats for each phase in the previous article (Part 3). We also discussed the fact that the separation of design-time and run-time is strict in AX7, meaning your code only exists at design time. Since you can only execute one version of a binary at the run-time, at compile-time the compiler will "flatten" all your models and layers and come up with the final version of the code across models and layers and compile that into your binary. This implies that layers and models are a design-time concept only! The run-time has no notion of models or layers (similar to previous versions, really, but as discussed in previous posts the line was slightly blurry then).
So in part 3 we talked about file formats and discussed that each object is an XML file on disk. What used to be the "model store" is now really a folder on your development machine, containing a folders and files structure. The lowest/deepest level folder is for models, and inside a model folder you will find sub-folders that resemble the old-school AOT tree nodes. Essentially, each model folder contains a sub-folder for each type of object. You'll find the usual suspects (folders for AxClass, AxEnum, AxForm, AxMenuItemDisplay, etc.) but also new ones for AX7 (AxDataEntityView, AxFormExtension - we'll touch upon the new "Ax<*>Extension" objects later on). Now, for most of these you'll find that the object type's folder has ANOTHER sub-folder called "Delta"...
Let's think back to over-layering and granularity in AX2012 as discussed in Part 1. We talked about certain sub-components (or essentially AOT tree nodes) of an object allowing over-layering, and sometimes not allowing it - meaning the whole object would be copied into your higher layer. The level of detailed over-layering is what we called the "granularity" of an object. This depended on the type of object. In AX7, each and every single object is granular down to the PROPERTY. That's right. If you change the label of an EDT - only that label property is over-layered, not the whole EDT. So, you can happily change the label in one layer, and the helptext in another. And the string size in yet another layer. But wait - there's more! In addition to ONLY storing the property you've changed by over-layering, it also stores the original value of what you've changed. The reason this is important will be clear to anyone who's had to deal with upgrades or updates or any kind... When comparing a higher and lower layer version of an object and a difference is found, the question usually becomes: is that lower layer version the one I changed, or did that just get changed in the update and do I need to re-evaluate my custom version? It becomes a bigger issue with X++ code - a whole method is over-layered and you notice a line of code that changed... but did you make that change originally? Or is the lower layer code changed and your code just has the old standard code (and so you need to merge it)? Usually the only way to tell is go back to an instance of the old version, see what explicit change you made, then decide if you need to merge anything. It can be tedious process depending on the type of customizations you have done, and it gets very difficult to automate any of this. So now, in AX7, your over-layered version (i.e. the delta XML file) contains the original code or property value you've changed. This way, you have an automatic three-way compare possibility: 1) the new lower-layer code; 2) the old lower-layer code you changed and 3) your customized code. This makes it a lot easier to compare and merge, and it is also the back bone of the automated tooling that LCS provides to upgrade and update your code.
So what does this look like? I created a new model from the Dynamics AX menu, and selected to add it to an existing package called "application suite" (more on this later). I opted to created a new project for this model. Once created, I open the Application Explorer (the new AOT) and the first Data Model / Tables object I see that is in Application Suite is called "AccountStaging". I right-click the table and select "Customize". This adds the table to my project with a [c] next to its name.
Right-click on the table and select "Properties". Scroll all the way up in the properties and you'll notice the full path to the XML file. Let's open that XML file in notepad. Since I actually haven't changed anything on the table yet, the file is virtually empty. Here's what I have:
Now, double click the table or right-click and select "Open". In the properties of the table (in the designer view, right-click, select Properties) change the Label to "DAX Musings rules" (or if you disagree, feel free to put some other text - it won't mess up the rest of this article's demos) and save. Let's now open that XML file again.<?xml version="1.0" encoding="utf-8"?>
As you can see, the only thing added to this delta file is the label property, and it contains the original value and my new value ("Original" and "Yours"). How this is used and what conflicts are related to this, I'll cover in another article.<?xml version="1.0" encoding="utf-8"?>
<Yours>DAX Musings rules</Yours>
Ok, so we have a model with an over-layered object... But since we're now storing over-layering as granular as the property, what does this mean for having multiple models in the same layer? Well, yes the granularity down to properties will avoid a lot of model conflicts, but in AX7 it doesn't matter... because models over-layer each other! That's right, models are essentially a sort of sub-layering system. This is certainly food for thought and content for an article, but for now - consider models sub-layers and essentially there's an ordering of models to know which one is higher than which other one within a given layer.
Going up the folder path from the model, you get up to the package folder. Models exist inside a package - but what does that mean? Going back to the 2012 paradigms, consider the package as sort of a model store in and of itself. A package has layers and models, and when compiling a package translates one to one into a .NET assembly (.DLL). So when compiling a package, as explained, the compiler will "flatten" all the layers and models into the final version of the source code including over-layered code, and compile the package into an assembly. So, when I mentioned in the previous article that you always have to compile all the code, you truly only have to compile the whole code of the package you're working in...
So, there are a LOT of consequences now that we know how packages and models work...
1) If a package is compiled by itself, and the compiler flattens the layers... that means over-layering can only be done within one package! You cannot over-layer something from another package (consider what we talked about how the compiler does its work...)
2) If a package is a DLL, that means if code is split up in multiple packages, you can compile them individually (reducing needed compile time), deploy/service them individually, etc.
3) If a package is a DLL, then using objects from another package/dll requires you to make a "reference" like you would with any other assembly DLLs.
<pause added for dramatic effect>
Thinking of those 3 consequences, the first one seems the more immediate significant difference compared to AX 2012 and before... if we can't over-layer from another package, what's the point of having separate packages entirely? The benefits like faster compiles and easier servicing and deployment make sense, but how does one customize then? For this purpose, AX7 features a new development paradigm called "extensions". The many features that make up extensions essentially allow you to "add" (i.e. extend) things to the standard application. Adding menu items to menus, controls to forms, fields to tables, etc.
So for all the talk about over-layering and delta files and three-way compare&merge upgrades, extensions is where our focus as developers should be! :-)