“Discrete Business Logic” implements Interface

If you’ve read anything about what is new in BC16 for developers then you’ll have seen that AL now supports interfaces (*fanfare*). No more convoluted workarounds with events – at least once our customers have upgraded.

I won’t explain what interfaces are and how to use them in AL. You can read the release notes here or blog posts like this and this. Instead I’m going to share some thoughts about where we might make use of interfaces. The obvious disclaimer is that we’ve only just started working with them and I’ve no doubt that we’ll learn more about how to use them properly as we go.

TL;DR

Look for discrete blocks of business logic that can be abstracted to interfaces rather than entities. Identify which business logic you, or other devs dependent on your app, would like to be able to replace. Identify the essential methods that would need to be implemented to replace that logic.

Entities

If you do a quick search for interface examples in other languages you’ll find that most of them are based on entities i.e. real world things or concepts that you are modelling in your software. The common actions those entities can perform or properties that they have are grouped into an interface.

The favourite examples seem to be animals and vehicles e.g. animals can make a sound, eat, sleep etc. You end up with:

interface IAnimal
{
  int noOfLegs;
  procedure MakeSound();
  procedure Eat();
  procedure Sleep();
}

or

interface IVehicle
{
  decimal: maxSpeed;
  int: noOfWheels;
  procedure Start();
  procedure Drive();
  procedure Stop();
  procedure GetMaxSpeed();
}

In other languages you might model your entities with classes that implement those interfaces.

class TreeFrog implements IAnimal
...

class Segway implements IVehicle
...

The trouble is, that doesn’t map particularly well into AL. We model our entities in tables and tables don’t support interfaces, at least not yet. You could image for instance IPerson and IAddress interfaces which describes the properties and methods of people and addresses.

interface IPerson
{
  field("First Name"; Text[50]);
  field(Surname; Text[50]);
}

interface IAddress
{
  field(Address; Text[50]);
  field("Address 2"; Text[50]);
  field(City; Text[50];
  field(County; Text[50]);
  field("Post Code"; Text[50]);
  field("Country/Region Code"; Code[10]);
  procedure Format(AddrArray: Array[8] of Text[50]);
}

Those interfaces could be implemented by various entities in Business Central.

table Customer implements IAddress;
table Employee implements IAddress, IPerson;
table "Salesperson/Purchaser" implements IPerson;

You could imagine that – but that’s all you can do for now. AL interfaces don’t support properties and can’t be implemented by tables.

That’s a long way of saying that it might be useful not to focus on entities when we are looking at candidates for abstracting into an interface.

Discrete Business Logic

Instead of thinking in terms of entities, try thinking in terms of discrete business logic. What self-contained blocks of business logic do you have in your app that it might be useful to replace with a different implementation? Or, what business logic do you have several implementations of that you need to choose between?

Consider that Microsoft’s first major use of an interface in the app wasn’t to do with customer, vendors, sales orders, G/L accounts or any other major entity. It was the sales price calculation logic. From BC16 you can select between different standard implementations or implement your own. More abstract than IVehicle.GetMaxSpeed() but probably far more useful in real life.

It occurs to me that there are probably several benefits to looking for existing and new blocks of business logic that are candidates for implementing via an interface.

Separation of Concerns

Hopefully obvious, but worth a mention. Anything that helps us to consider what our discrete block of business logic are and how to separate them from one another is a good thing. It’s easier to develop – especially in a team and easier to understand and maintain later on.

The process of designing the interface and deciding exactly what is the required to implement some business logic is valuable in itself.

Extensibility

Other developers want to build on top of our logic – our replace parts of our logic with their own. We try to make that as easy as possible with OnBefore/OnAfter events, Handled parameters and developer documentation. It is still a relatively complex task though and easy to subscribe to the wrong event, not respect the Handled flag or make some other mistake that breaks stuff.

An event? For me?

If you are able to just tell a 3rd party developer they can extend an enum and implement an interface life becomes a little easier. The interface, and the compiler, tell them the methods they need to implement and your app can call their implementation rather than having them subscribe to events and fire up an instance of their codeunit every time – like an excitable chihuahua barking every time post comes through the letter box only to find that the mail isn’t addressed to them.

Speaking of which I imagine there must be some performance benefit – probably negligible in most cases – but there must be one.

Obsolescence

We’ve got the Obsolete properties now and can move methods around and use the ObsoleteReason to tell dependent developers how they need to change their code. That’s all great. What if you are having a much bigger restructuring of your code though?

There might be an opportunity to follow Microsoft’s lead. Introduce an interface and have the existing business logic implement that interface. That frees you up to create a new implementation and switch customers and custom development over in a controlled fashion.

For example, we’ve got a case at the moment where existing customisation has been done through event subscriptions. In future we’re going to want dependent developers to extend an enum and implement an interface.

We’re introducing an interface and a default implementation. The default implementation calls the existing events so custom development will continue to work. Then we and dependent developers can migrate over to the interface over time.