Chapter 6. Anti-patterns and Handling Legacy Code

From the perspective of Architectural and Design Patterns and coding guidelines, Microsoft Dynamics NAV is very consistent, but even though the Patterns have always been the secret source, the product is not always nice and clean. Many developers have worked on solutions for many years with or without guidelines about the maintainability and upgradability of the patterns.

The Dynamics NAV application from scratch has been built in quite a neat and clean way, but throughout the years, modules that Microsoft acquired through acquisition or sub-contracting have been added. They have not always been cleaned up, and following the rules that we learned in the earlier chapters of this book.

We call this phenomenon legacy code, and since our ecosystem is three decades old, there is a lot of it.

From a business perspective, it is hard to get a budget to spend a few years in a basement, changing your legacy code, which has been working for many years to work with these patterns and practices, and come up with a way to upgrade your existing customer base.

Therefore, we will learn some guidelines on how to transform legacy code into modern code. Some of these guidelines are used by Microsoft, which is experiencing similar challenges. This process is also referred to as refactoring.

Anti-patterns

In a world that defines good design and best practices, it might be useful to also define bad design, and discuss why we consider it bad, and in which context. Where Design Patterns are best practices, Anti-patterns are considered to be proven as counter-productive practices.

There is a lot of information to be found on the internet on Anti-patterns, and it is a perfect material to be discussed over a beer during a barbeque. In this book, we will not try to come up with a complete list of anti-patterns for Dynamics NAV, but we will discuss some of them and see how they can be avoided.

Code cloning

One of the most well-known anti-patterns is code cloning. Code cloning means copying code or a part of the code in order to reuse it. When learning object-oriented programming (OOP), it is good to try to avoid this at almost any cost, whereas in Microsoft Dynamics NAV, we have sales people telling customers how easy it is to copy something in NAV and customize it. Dynamics NAV itself is full of code cloning. How can this add up to being good?

Well, some of it does not add up, but some of it also needs to be put into perspective.

In order to put it into perspective, we will divide code cloning into four subtypes. This will enable us to justify some instances of code cloning in Dynamics NAV, and allow us to focus on what parts should be addressed first.

Forking

When forking is applied, we create a new application by creating a copy of an existing application, and let it grow organically from there.

Templating

If we reuse objects because they behave just as we need, but they are not designed to be either reused or used outside their functional implementation, we call it templating.

Customizing

Sometimes when we design a new feature, we don't want to immediately replace the original feature. If we start with a copy of a feature with the intention of ultimately replacing the original, we refer to it as customizing.

Exact match

When we copy objects or code rather than making generic functions, we call it exact match. This type of code cloning is what most software engineers think of when we talk about code cloning.

Note

This document describes these forms of code cloning outside the context of Microsoft Dynamics NAV. You can find this at http://plg.uwaterloo.ca/~migod/papers/2006/wcre06-clonePatterns.pdf.

Examples

Let's look at an example of each category to understand how they are implemented in Microsoft Dynamics NAV.

Transaction mirroring

One of the fundamental design principles of Microsoft Dynamics NAV is transaction mirroring. This is best illustrated by looking at the Sales and Purchase side of the application, but the examples of it can be found anywhere in Dynamics NAV.

When we open the Sales Line (37) and Purchase Line (39) tables and look at the fields, the comparison is amazing.

Up to the level of field number, type, and often name, the fields match exactly:

Transaction mirroring

This is an example of forking. One of the advantages is the creation of recognizable software. Much like the abstract classes in OOP, the signatures of many functions are the same.

However, there is a thin line between forking and exact match. Many functions in Microsoft Dynamics NAV can be normalized.

Code normalization and writing clean code

When we discuss the term normalization, most engineers think about database modeling, and this is an important part of our job. Another part of normalization is writing clean code; avoiding code duplicates and making sure that the code is reusable and extendable. In Microsoft Dynamics NAV, we have some interesting examples of both: database normalization and code normalization.

The Document type normalization

Traditionally, Dynamics NAV is not referred to as being normalized by software engineers. However, there are some examples where the software is normalized, or may even be considered to be over normalized. An example of normalization is the Dimension structure that was introduced in Dynamics NAV 2013. With this model, customers can save up to an average of about 30 percent of database space compared to the old model.

Tip

In this blog post, I've explained how the dimensions were refactored in NAV 2013. You can find it at http://nav-skills.com/2015/08/21/dynamics-nav-2013-dimensions-i/ and http://nav-skills.com/2015/08/21/dynamics-nav-2013-dimensions-ii/.

Another example of normalization can be found in the Sales and Purchase documents before they are posted. Here a quote, blanket order, order, invoice, credit memo, and return order all live in the same table objects. This is considered by many as an example of templating. This is emphasized by the large quantity of CASE "Document Type" OF statements and IF "Document Type" = "Document Type"::"" THEN statements we find in the application.

The small business application

Inside the Microsoft Dynamics NAV objects, we can find a specific set of objects that we refer to as the Small Business Application. Internally, at Microsoft, they are referred to as MiniApp.

The following screenshot shows objects being used in the small business application. All the objects except the Rolecenter (page 9022) are in the 1300 – 1399 number range.

The small business application

This application contains a lot of UI clones of the existing objects in NAV that behave slightly different than the original object. This was introduced in NAV2013R2 and NAV2015, some design principles are inherited back to the main objects. This is an example of the Customize subtype of code cloning.

The following screenshot illustrates the full Customer Card in the Dynamics NAV application:

The small business application

In the small business application, there is less information, and fields are grouped together in an easier way:

The small business application

Information that is not always required is hidden and can be activated using actions. The examples are Financial Details and Prices and Line Discounts. The latter is a SubPage, which is displayed directly on the card; it is something that we would normally not do on Master Data.

The next screenshot illustrates the SubPage, and also shows Statistics using a graph of a Master Data card:

The small business application

The VAT and Sales Tax calculation

One of the best examples of exact match code cloning in Microsoft Dynamics NAV is the way in which VAT and Sales Tax is calculated. Each table that has this calculation has an almost identical set of functions that do the calculation:

CASE "VAT Calculation Type" OF
  "VAT Calculation Type"::"Normal VAT",
  "VAT Calculation Type"::"Reverse Charge VAT":
    BEGIN
      "VAT Amount" :=
        ROUND(Amount * "VAT %" / (100 + "VAT %"),
          Currency."Amount Rounding Precision",
          Currency.VATRoundingDirection);
      "VAT Base Amount" :=
        ROUND(Amount - "VAT Amount",
          Currency."Amount Rounding Precision");
    END;
  "VAT Calculation Type"::"Full VAT":
    "VAT Amount" := Amount;
  "VAT Calculation Type"::"Sales Tax":
    IF ("Gen. Posting Type" = "Gen. Posting Type"::Purchase) AND
       "Use Tax"
    THEN BEGIN
      "VAT Amount" := 0;
      "VAT %" := 0;
    END ELSE BEGIN
      "VAT Amount" :=
        Amount -
        SalesTaxCalculate.ReverseCalculateTax(
          "Tax Area Code","Tax Group Code","Tax Liable",
          "Posting Date",Amount,Quantity,"Currency Factor");
      IF Amount - "VAT Amount" <> 0 THEN
        "VAT %" := ROUND(100 * "VAT Amount" /
          (Amount - "VAT Amount"),0.00001)
      ELSE
        "VAT %" := 0;
      "VAT Amount" :=
        ROUND("VAT Amount",Currency."Amount Rounding Precision");
    END;
END;

Some objects where this code is implemented are Codeunits 12, 442, 444 and Tables 37, 39, 5902, 751, 81.

Avoiding code cloning

Code clones of the type called exact match should always be avoided. There are Patterns and practices against this. Function libraries and interfaces are the best examples.

The Argument table is a perfect Pattern that can help you against code cloning. One of the challenges of normalizing code is the number of parameters that you end up having in functions. The Argument table is a perfect answer to this.

In case of our VAT and Sales Tax calculation example, we can potentially create a VAT and Sales Tax argument table where the tax is calculated and the results are passed back to the caller. In fact, a VAT Amount Line table already exists in Microsoft Dynamics NAV with a function called CalculateVATFields, as illustrated in the next screenshot:

Avoiding code cloning

Alternatively, when you require VAT and Sales Tax calculation for your add-on, you might consider using the General Journal Line as an argument table for your calculation. This way you are guaranteed to have a working solution if Microsoft changes the calculation; or you have proper warning that the contract might be changed in a new release of the product.

Boat anchors

Another typical anti-pattern is a boat anchor. This Pattern is implemented by leaving code or metadata in your application that is no longer used.

There are at least a dozen examples of boat anchors in the standard Microsoft Dynamics NAV code. One of them is the AltSearchField property in the Master Data tables, as shown:

Boat anchors

This property was used by the classic client. When a field has a table relation with the primary key of another table, a user can also type the value of AltSeachField, and the client will automatically search in both the columns.

Since the introduction of the Role Tailored Client, this mechanism has been replaced by the DropDown Field Group.

Other examples can be found in the Journal Posting Codeunits, as in this screenshot:

Boat anchors

The OnRun trigger was used before Navision Software introduced Dimensions in version 3.0. After its introduction, the Dimensions had to be passed and the RunWithCheck function was implemented. However, when Dimensions was refactored in NAV2013, they did not move back to the OnRun Trigger. Therefore, either the code in OnRun is never called and should be removed, or RunWithCheck should be refactored.

The second boat anchor in this Codeunit is getting the General Ledger Setup in the GetGLSetup function. To avoid roundtrips to the SQL Server, it is best practice to remember whether we already read the record inside a loop. This has been obsolete ever since the introduction of the data cache on the Service Tier. This automatically avoids the roundtrips to the server.

Tip

Other examples of boat anchors in Microsoft Dynamics NAV can be found at http://dynamicsuser.net/blogs/vanvugt/archive/tags/cleanup/default.aspx.

Other anti-patterns and context

Many other anti-patterns can be derived from the design pattern or best practice that solves them. However, a pattern and an anti-pattern should always be applied in context. Sometimes, a comment in the code is unavoidable since you don't own the code; or the addition of a suffix, explaining the datatype makes sense since the name is ambiguous.

A design pattern implemented in the wrong context can become an anti-pattern and cause performance issues or become hard to maintain.

Programming language capabilities

The ability to apply Design Patterns is largely managed by the capabilities that a programming language has. Since Microsoft Dynamics NAV is not object oriented, we have a restricted subset of the traditional OOP patterns. We also have a set of Patterns that is either related to procedural programming, or the metadata definitions as described in Chapter 1, Introduction to Patterns and Software Architecture.

Tip

All the C/AL code that we write in Microsoft Dynamics NAV is converted to C# code before generating a managed code. The managed code is generated at runtime by the service tier. The C# code is maintained in a system table. This code should never be changed or used.

Whenever Microsoft changes the capabilities of our programming language, the design patterns change. This can essentially lead to the introduction of new patterns, or even anti-patterns and boat anchors. With every release of Dynamics NAV, Microsoft adds features to our programming language, mainly due to the fact that C/AL is translated into C# before it is compiled, making it in essence as object oriented as Microsoft wants it to be, since the underlying language allows the object-oriented programming.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset