You know about PRS (Partner Ready Software), don’t you? It’s the initiative started by Mark Brummel, Eric Wauters (Waldo), and Gary Winter (not necessarily in this order), and then they decided to expand their team by one more member. I am not quite sure if this was a good move, but the time will tell. It always does.
The main goal of the initiative is to enable you to customize NAV in a repeatable way. Repeatability is kind of a buzzword, but PRS doesn’t just buzz the word. PRS believes repeatability is the key. Today maybe you don’t care about repeatability. In two years, or latest five, you’ll want to go back and rethink your angle. PRS wants you to rethink now.
One way of getting repeatable customizations is through patterns. There are patterns, all over NAV, that repeat, time after time, over and again. Agosto dopo agosto dopo agosto dopo agosto as Jovanotti would put it. Pun intended. When a pattern repeats itself, it’s repetition, not repeatability. There is a big difference between them, big as a house. To get repeatable, is to get rid of repetition. Instead of having essentially the same piece of code all over the place with a hint of Goldberg Variations, how about having it only once, in a single place?
Like, having one place where you assign a number from a number series to any master record, document, journal, you name it. Instead of at least—let me guess—120 different places in about as many objects. Then, if you want to customize one small aspect of it, you just touch that one single place. You can then repeat the same customization for hundreds of customers, not only surviving the version upgrade, but making it (the upgrade) an essential, non-disruptive, piece-of-cakeish part of your service offering. That’s the repeatability that PRS is all about.
But—there is always a but—the way NAV is today, we are a long way from this kind of repeatability. A long and painful way away.
That’s why I had a dream. I know it’s only a dream because to make NAV do what I am about to share here would be to downright rearchitect it from the core. But I’ll share it nonetheless. Keep reading.
Couple of disclaimers right away. First, a lot of this is going to look self-evident to any C# developer who stumbles upon this, but C/AL is so far away from C/AL that stuff that is obvious to C# folks is still science fiction to C/AL folks. Second, this is a very long post; I intentionally didn’t split it up into three posts, which it could have easily been.
So, I had a dream.
Well, not quite a dream—because I was fully awake and absolutely sober—it was more of a discussion. Couple of days ago, during an event that got all of PRS together, we discussed about how to go one step beyond the current state of affairs, and think of things that could be done to NAV to improve repeatability. I was talking about interfaces so much that Waldo grew sick and tired of me and my talk and interfaces and asked me to shut up. He told me: “why don’t you write a blog post instead of just babbling about it?”
Indeed, why not?
NAV is extremely tightly coupled. This means that every tiny piece of logic depends on many other pieces of logic so much that making a change, a slightest one, can easily disrupt the whole application. A good example here is the shoe size problem. I didn’t invent this term.
The shoe size problem goes like this. You sell shoes. Shoe size is an important parameter for shoe sales. You extend your Item table with the Shoe Size field. Now you need to make sure that all transactions get this field to all the tables it needs to get to. Adding one extra field to a single table ends up being one extra field in dozens of tables, and hundreds of lines of custom code over scores of objects.
NAV doesn’t have a good solution to this problem. There are conceptual solutions, though, but they all bring a bad bargain about: the repeatability of the solution is proportional to the complexity of the architecture change.
A traditional NAV solution to this problem is exactly as explained in the problem above: add the field to all the tables that need it, and then customize all the triggers in all tables, codeunits, or other objects, where this field needs to be handled. Translated and simplified, it reads: tightly couple the shoe size logic into existing code.
Another possible solution is through hooks. These are events that you call from existing functions which allow you to stuff your code into execution without changing the original code. However, making NAV hookable calls for a formidable effort in rearchitecting the original code, or the product itself, or both. In other words: reinventing the wheel. And whenever reinventing a wheel, you must take care to not reinvent a wrong wheel. Making NAV hookable would be reinventing a wrong wheel, and lead me not into the discussion why. It would just be.
Okay, for all of you who don’t buy it, hooks are a very old fashioned, procedural way of handling the problem. We, the homo sapiens, know of better ways.
One possible better way, that I am about to propose, is through interfaces. This is how object oriented languages handle shoe size problems.
At the core of handling shoe size and similar problems, and in this post I’ll add two more to the lot, is decoupling. Decoupling is a technique of binding dependencies in such a way that you can easily take one dependency out of the whole, and the rest still continues working. Easily in the last sentence means as easy as plugging in and out. Take a computer as an example. It is much decoupled from a camera. You plug in a camera, and you can video chat. You unplug the camera, and you cannot. But the computer still runs. Nothing happens. Excel still calculates. Outlook still asks you to snooze missed deadlines. Chrome still sucks.
But how do we decouple NAV? Let me take two examples: number series and address formatting.
This is how number series is assigned to a customer:
And this is how it is assigned to a vendor:
And this is how it is assigned to an item:
And… don’t let me go on. This is how it is assigned all over the place.
It doesn’t take a long time before you notice that this is exactly the same thing. The only difference is that you are using a different table, and a different field to specify which No. Series to take, but from the piece of code is the same. It’s a typical example of boilerplate code.
Number Series does not stop there. There are at least two more functions that every master table does exactly the same way.
First, it makes sure that numbers can be assigned manually:
And second, it allows the user to choose from multiple number series:
Some more boilerplate code. Unfortunately, this is as optimized as NAV can possibly get. The actual code that assigns a number is in fact abstracted away into the InitSeries function. However, this is not good enough. It’s very tightly coupled. The problem is – NAV does not really allow you to decouple this.
But let me dream away. Let me theorize how this could be decoupled.
If a table were an object in C#, we could start by presenting the whole thing as a series of objects:
This is more or less just a C# representation of NAV situation, with a couple of minimal optimizations which just can be done because this is all object-based. However, this is still not good enough, because for other tables we still need code duplication. This would be vendors and items:
Unnecessary duplication if you ask me. Let’s optimize it a couple of more steps further to get rid of duplication by making the NoSeries class more encapsulated:
This makes it simple to create specific master data classes:
This is more manageable, there is less duplication, but this is still not quite it. We can optimize further:
This makes creating new descendants of the NoSeriesTable class a charm:
This is probably as decoupled as we can possibly get with objects alone. Call me a perfectionist, but this is still not good enough – it can be even more decoupled. A lot more. And, it also comes with a problem: you can only inherit from a single class. What if you had another Table descendant—call it FooTable—and you want it to inherit from both NoSeriesTable and FooTable. Two alternatives here: inheritance chains, which opens the door for the Chain of Responsibility pattern; and interfaces. I would possibly want to have another dream, and blog about possibilities with the Chain of Responsibility pattern, but I am already crazy enough to dream about interfaces, so let me go on in that direction in this post.
Before we get to interfaces, let me first explain why the example above, while pretty elegant, is still not good enough. It’s simply because of hardcoded dependencies. Every expected behavior, such as assigning the number series, depends directly on a specific class. The NoSeriesTable depends directly on the Table type. The Customer, Vendor, and Item tables depend directly on the NoSeriesTable type. Imagine that you want to make change to the NoSeries class, a change that applies automatically to all tables that inherit from NoSeries type. It’s not possible without changing the definition of all such tables, which means a lot of manual coding, and a lot of room for error. And error is a funny animal – if there is room for it, it’ll surely get into the room.
So, what do you do? If you expose the entire C# source code to third parties, the way how current C/AL implementation does, you make it extremely simple to totally break the whole thing in no time. If you seal the classes, and hide them away in an assembly, then your system is rigid and you have to pull various delegate-based patterns, or end up having hooks all over the place. If you protect the classes in an assembly, then again your system is not stable enough because customizations depend on a lot of manual work.
The solution is – get out of classes and get into interfaces. Interfaces allow you to really decouple the abstract logic from the actual implementation. Keep in mind – interfaces don’t simplify the code – their purpose is to provide decoupled architecture, and to provide such architecture they typically depend on certain infrastructure patterns.
So, let’s take a look how the stuff above could look if we had interfaces instead. In the first step, I’ll create an interface for the Field class, and also make it generic. I’ll also have a new delegate for generic events that I intend to call from IField and ITable interfaces. I’ll also provide a default class for both interfaces.
Now we get to the real point: the number series stuff. For this purpose I have the INoSeriesField interface, which inherits from the IField<string>, just to make it assignment compatible, as you will see later. I also provide a default implementation of this interface in the NoSeriesField class. Here I’ve still done a tight coupling of the NoSeriesField class with the Field<string> class, but this is only for illustration purposes to keep the code simpler. In practice, even this could have been done with the IField<string> interface instead.
What is new as compared to the previous class-based configuration is the Initialize method on the NoSeriesField, so I have moved the code out of the table and into the field. It’s the field which has the number series behavior, not the table.
Finally, this is how to implement the Customer table, which applies the number series functionality:
Here you also see why interfaces are so beautiful – they allow for total decoupling of the functionality. First, I’ve decided that NoSeries functionality is something that anybody may change in the future, so I’ve created an interface for this functionality. The No field does not specify the class and it specifies the interface instead. This literally means: the actual class will be coupled somewhere else, preferably loosely, and at the run time. Which is exactly what I do in the constructor. There I first instantiate the class, and then call the Initialize method of the interface to configure the options for the number series functionality.
Now, what does this ObjectFactory.GetInstance method do? It’s the inversion of control technique applied through the service locator pattern. I could have also gone for a simple dependency injection pattern, but that one is less extensible because it requires changing the constructor signature.
So, we have a repeatable and extensible architecture. Okay, I am still dreaming, but I’ll wake up soon. Let’s first go to the next example.
I have to thank to Mark Brummel for pointing out this as an example of an extremely ugly duplication of code. It’s the codeunit 365 Format Address. In essence, it formats the address for printing in reports, by applying two sets of rules: it applies the address formatting rules from the Country/Region code table, and then it compresses the address lines so that there are no blanks (for example, to avoid a blank line for an empty Address 2 field).
As you imagine, there is a single function which does this. However, there are more than 50 individual functions which call this single function, for many different source tables and address types. Now, if you want to customize the way how address is formatted, you potentially end up customizing dozens of tables, and almost every single function in this codeunit.
First, let’s create the interface and the default implementation:
And then, here’s how to use it from two different tables that require address functionality:
Finally, the function that formats the address can be one and only one:
Inside this function, I could use reflection to iterate through all properties that are of the IAddressField type, filter them according to their usage, and sort them according to their AfterField and BeforeField settings, and happily return a string which can be printed on reports.
So again, interfaces help a great deal.
Back to reality
Ok, time to wake up now. This all looks great in C#, but NAV, C/SIDE, and C/AL are far away from anything like this. But this does not mean that certain simple changes to C/SIDE would not help.
If I had any call into what C/SIDE and C/AL would be capable of, then the first thing I would do is to create a complex data type which is a common type for all tables. Yes, of course, I know that we have RecordRef, but it’s nowhere close to this. This is what RecordRef cannot do:
Making such data type would come at a cost: no compile-time field checks can be done. And yes, it introduces a risk of failure at run-time, but hey! We do have testability features, and we should test our code. So I don’t really think that this would be too high a price for a tremendously flexible feature such as this. And if we have a generic table type, maybe we could also have a generic field type. It’s the same kind of additional features.
This thing alone could easily help reduce complexity of anti-patterns such as codeunit 365.
Another change that could generally help in C/AL would be explicit binding of events. Currently we have hardcoded events for tables, fields, and so on, but why not having custom-bindable events, just like in C#? Then we could write this:
When we are at that, making these interchangeable with .NET delegate types would make this an insanely versatile feature.
Finally, something to help Number Series and similar situations would not even be that far from C/SIDE logic that’s already in there. Start with the introduction of interface codeunits. These could be codeunits types (on par with Test and TestRunner types) that allow you to provide common behavior for tables (or maybe even other object types). You could then bind these interface codeunits to tables in a very similar way how you bind CaptionClasses. For example, the interface codeunit for Number Series functionality could easily look like this:
And you could bind it to any table just by specifying the CustomClass property on the table. For example, for Customer table it would read: NoSeriesInterface.Initialize(SalesSetup,SalesSetup.”Customer Nos.”,Rec,Rec.”No.”,Rec.”No. Series”);
These changes would not be so demanding on the overall architecture of NAV, and would be a nice first step towards something much, much better (at least in terms of repeatability) as compared to what we have now.
Time to wake up.
Whose woods these are I think I know.
His house is in the village though;
He will not see me stopping here
To watch his woods fill up with snow.
My little horse must think it queer
To stop without a farmhouse near
Between the woods and frozen lake
The darkest evening of the year.
He gives his harness bells a shake
To ask if there is some mistake.
The only other sound’s the sweep
Of easy wind and downy flake.
The woods are lovely, dark and deep.
But I have promises to keep,
And miles to go before I sleep,
And miles to go before I sleep.
This Post Has 4 Comments
Keep the dreams coming … and keep waking up, Vjeko. NAV (community) will gain form that.
The notion of code reusability has been around for a few decades, it’s about time this sound software development practice to be adopted by NAV Developers at large, which would ultimately lead to repeatable design patterns …
Pingback: I had a dream: decoupled NAV - Navigate Into Success - Microsoft Dynamics NAV - Microsoft Dynamics Community
Pingback: I had a dream: decoupled NAV - Navigate Into Success - Microsoft Dynamics NAV - Microsoft Dynamics Community