Technical Best Practices for Dynamics AX – Application Design Standards

11 min read

Application Design Standards

The Dynamics AX design standards consist of the following considerations:

  • Code placement
  • Performance optimization
  • Using field groups in tables
  • Auto property settings

Code Placement

Code placement is important as it affects the following:

  • Performance
  • Customization
  • Reusability
  • Maintainability (upgrade, extensibility, etc.)

The general guidelines are that the code should be placed in such a way that various calls to other layers are minimized, the operation is performed at the layer where it is least expensive, and the same code need not be written at several places (e.g. if business logic is written on a form then it needs to be written in the enterprise portal for the web client also). So we should not only think about the tier of placement in the three-tier architecture, but also about the best AOT (Application Object Tree) element type for a piece of code. Once these have been designed we need to think about the type of method or objects in the classes, etc.

Three-Tier Architecture Considerations

The three tiers in this architecture are dedicated for the following three types of jobs:

  • Client—The Presentation layer—This is where the forms are stored. Place client‑specific classes and methods here.
  • Object server—The Business application logic layer—Transaction-oriented database update jobs should be run here, close to the database.
  • Database server—The Database layer—Utilize the power of the database server by using aggregate functions, joins, and other calculating features of the database management system.

Dynamics AX has a property, RunOn, for every AOT element, which indicates the layer where it should be executed i.e. Client, AOS, or Database server. This RunOn property may have one of three values i.e. Client, Called from, and Server.

Client: The object will live on the client.

Called from: The object will live at the tier where the object is created using the ‘new’ constructor.

Server: The object will live on the server.

Now we will discuss how to decide the RunOn property value.


The value of the RunOn property for a class will decide the location of the object created from that class.

  • The value of RunOn property for a class will be same as the parent class if the parent class has a RunOn property other than Called from i.e. the RunOn property cannot be changed for a class if the parent class has as its value either Client or Server.
  • If the parent class has Called from as its RunOn property value, it can be changed to Client or Server and if it is not changed it will retain the inherited value i.e. Called from.
  • The Called from value of the RunOn property means the object will live at the tier where the code creating it (by calling the new constructor) is running.


Now we will discuss the execution place for various types of methods.

  • Class static methods and table methods (except for the database methods) can have their default behavior.
  • The execution place for a method can be changed to Server or Client by adding the Client or Server modifier keywords in the method declaration as shown below:

    • server static boolean mymethod(): to make server the execution place.

    • client static boolean mymethod(): to make client the execution place.

    • client server static boolean mymethod(): to make called from the execution place.

The following table summarizes the execution place of various types of methods:

AOT elements

Default behavior

Can be changed

Class static methods

Runs by default at the same place where the associated class runs i.e. if the associated class has the RunOn property value as server then the class static method will also be executed at the server.


Class instance methods

Runs where the object of the class lives. The class objects live as described in the class RunOn property.


Table static methods

Table static methods have the RunOn property as Called from and hence by default they run where they are called.


Table instance methods

Table instance methods have the RunOn property as Called from and hence by default they run where they are called.

The standard methods Insert/doInsert, Update/doUpdate, and Delete/doDelete run on the Server where the data source is located.


GUI Objects and Reports

GUI objects always live on Client. GUI objects include the FormRun, FormDataSource, all FormControls, DialogBox, and OperationProgress objects.

Reports always live on Called from, which means the object will live at the tier where the code creating it (by calling the new constructor) is running.

Temporary Tables

Temporary tables instantiate and live at the tier where data is first inserted and it does not matter where they are declared. Since the placement of temporary tables is very critical for performance, temporary tables should live at the tier where they are used. If a table is utilized in more than one tier then it should live on the tier where the greatest number of inserts and updates are performed.


QueryRun has Called from as the default value of the RunOn property. The QueryRun should always be supplied from the same tier from where it was originally run.

If you want to create a new QueryRun in place of an old one, it should be created on the same tier where the old QueryRun was executed.

AOT Element Type Consideration

The following guidelines must be followed to decide the type of code container:

AOT element



Write code in class when either:

Code is related to many tables.

Code is not related to any table.

Create class instance method when:

Working on the instance variable of the class.

Overriding is potentially useful.

Create class static method when:

Access to the class instance method is not required.

Overriding is not needed.

The functionality of the method is related to the class it is defined on.

The method needs to be executed on a different tier than the method’s tier.


Write code in table method when:

It is strictly related to a table.

Create table instance method when:

It is supposed to handle one record at a time.

Create table static method when:

It is supposed to handle none, some, or all the records at a time.

Global class

Write code in global class when:

Code cannot be placed more logically in another class (or table).

Code is general purpose, tool extending, and application neutral.

Forms and reports

Coding on forms or reports should be avoided as far as possible i.e. except for the calls to classes and table methods that handle complex layout and business logic.

The edit and display methods must be avoided if they are placed in a table.

If code cannot be placed anywhere else, i.e. the presentation tier is most suitable, then the following guidelines should be observed:

Place the code at the data source or data source field level and not at the control level.

Call classes from buttons on forms by using menu items. For example, rather than writing a code on the form or report, code could be written in a class and the class could be called from the menu item.


Write code in maps when a limited number of connected fields needs to be grouped.


Do not place much code in views.

Performance Optimization

The performance optimization guidelines can be categorized into the following three categories:

  • Database design
  • AOS performance optimization
  • General programming

Database Design

The database design principles are based on the following considerations:

  • Minimizing the database calls by caching
  • Minimizing database transactions
  • Wise index designing
  • Using the select statement in an optimum way
  • Performing transactions in the shortest time possible


Database access should be avoided whenever it is not absolutely necessary as retrieving database records from memory is far cheaper and faster. Recording database records in memory is known as caching. The following are the possible type of caching on the server:

  • Record caching
  • Entire table caching
  • Result-set caching
Record Caching

Record caching is a type of performance enhancement technique in which one or a group of records is retrieved from the memory rather than the database. Retrieving a record from memory rather than database significantly improves the data access. Record caching can be enabled only when the following conditions are satisfied:

  • The CacheLookup property in the table should be enabled by selecting the values notITTS, Found, or FoundAndEmpty.
  • The table has a unique index; either use the primary index or the first unique index. The index based on RecId (known as RecId index) does not qualify as a caching index.

The retrieved records can be placed in cache if the following conditions are met:

  • The table is cached i.e. the above conditions are met.
  • The select statement used to read the record uses an equal operator (= =) on the caching key.
  • All the fields in the record are retrieved.

A record is looked for when the following conditions are met:

  • The table is cached.
  • The select statement used to read the record uses an equal operator (= =) on caching key.
  • The select statement is either inside or outside TTS, but the value of the caching property for the table is not NotITTS and the select is not forupdate.

The following table summarizes the different types of caching mechanism:

CacheLookup property



No data will be cached or retrieved from the cache.

This value of CacheLookup property is used when:

Tables are frequently updated e.g. transaction tables.

It is very critical to read fresh data.


All select queries that retrieved at least one result will be cached.


All successful select queries based on caching key are cached for this type of caching.

All select queries are returned from cache if the record exists there.

A select forupdate in TTS will always read from the database and replace the record in cache.

This value of the CacheLookup property is typically used for static tables like ZipCodes where the record usually exists.


All select queriesbased on caching keys are cached, even those select queries, which do not return data.

All caching keys selects are returned from caching if the record exists or is marked as non-existing, and if it is neither marked as non-existing nor retrieving any result it will check the database and update the cache accordingly.

A select forupdate in TTS will always read from the database and replace the record in cache.

This value of the CacheLookup property is typically used for tables where registering non-existing keys is also important e.g. discount table.


A copy of table is created as temporary table.

All selects against the table will be performed on the copy.

Joins that include the table will only be performed against the copy when all tables participating in the join are EntireTable cached. Otherwise a database join is performed.

Operations that change data (insert, update, and delete) are performed against the database as well as against the copy.

The reread method will retrieve the data from database and update the copy data as well.

The Microsoft Dynamics AX Object Server thin client will regard an EntireTable cached table as FoundAndEmpty cached as well, and will therefore build a recordCache locally when accessing the table.

This value of the CacheLookup property is typically used for tables that are not supposed to be modified frequently.


Result-set Caching

The RecordViewCache is useful for caching tables that are not of static nature, or contain so many records that the other caching methods would be impractical.

This type of caching can be available through the RecordViewCache class. The RecordViewCache is instantiated using X++ select with a where clause that defines the result set. Technically the RecordViewCache can be instantiated using X++ select but it will create a copy of table in memory, which may be an inefficient use of memory.

The following rules apply to the instantiating X++ select:

  • It may not be a join.
  • It must be noFetch.
  • The table may not be temporary.
  • When running a Dynamics AX Object Server thin client, instantiation must be on the server.

The limitations of the result-set caching are as follows:

  • The RecordViewCache is not shared between Dynamics AX clients.
  • The RecordViewCache is deactivated as soon as the RecordViewCache object goes out of scope or is destroyed.

In some cases result-set caching may be dangerous and hence only careful use is recommended. The following facts about result-set caching will be helpful in deciding the use of result-set caching.

  • The database is simultaneously updated with the RecordViewCache.
  • Updating the key of a row that did not qualify the result set at the time of instantiation will not result in the row being included in the cache.
  • Inserts are always included in the RecordViewCaches for that table. It is definitely an advantage; however, care should be taken when inserting a large number of rows in a table and at the same time having a RecordViewCache on the same table as it will prolong the cache update time.
  • A delete will remove the row from RecordViewCaches on that table but not the table.
  • A Delete_from will invalidate RecordViewCaches on that table.

As mentioned above the RecordViewCache can go out of synchronization and hence we may need to re-synchronize it again. X++ has a method reread, which retrieves the data from database and updates the cached copy.


Please enter your comment!
Please enter your name here