Removed Discussion Links. Using new Theme solution.

Some additional MDLint cleanup as well
This commit is contained in:
Henrik Helgesen 2022-02-24 15:36:06 -08:00
parent cd3497d542
commit fb17dc9640
27 changed files with 90 additions and 206 deletions

View file

@ -109,7 +109,3 @@ The naming convention (both starting with "IScale") also makes it very easy to f
## When not to use
Obviously, the events should be carefully considered: only the events that make sense to "share" over all implementations, need this approach.
## Discussions
You can discuss this pattern [here](https://github.com/microsoft/alguidelines/discussions/66)

View file

@ -9,6 +9,7 @@ _Created by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (Gang of Fo
## Abstract
The intent of this pattern is to provide a unified API to a single or a collection of potentially complex subsystems. If you apply this pattern as a general pattern, you will ensure improved:
- Decoupling
- Encapsulation
- Readability
@ -22,6 +23,7 @@ Whenever you want to write an isolated piece of business logic, from now on refe
## Problem
The facade pattern addresses two main problems:
- Over time as systems grow, they tend to become complex and harder to comprehend. By adding a facade on top of the subsystem, that complexity is hidden, and a clear API is defined.
- Any object or method which is publicly accessible, may not receive breaking changes in future releases without announced deprecation. This complicates maintainability of the system. By adding a facade, you ensure that the subsystem is inaccessible to the outside systems, enabling you to change the implementation details of the subsystem at will.
@ -59,7 +61,7 @@ This is arguably one of easiest patterns to understand and implement. Loosely sp
To achieve this, we are using [access modifiers](https://docs.microsoft.com/bs-cyrl-ba/dynamics365/business-central/dev-itpro/developer/devenv-using-access-modifiers). Let's try to take a look at an example, taken from the system application: [the Image module](https://github.com/microsoft/ALAppExtensions/tree/main/Modules/System/Image). I'm using this very simplified example for illustration purposes. Notice, that even the full subsystem at time of writing isn't complex - it merely has a single codeunit containing the implementation details. However, as it is expected that the complexity will increase over time or that the implementation details can change, the subsystem is already equipped with a facade from the beginning.
*The Facade*
_The Facade_
```AL
codeunit 3971 Image
@ -90,6 +92,7 @@ codeunit 3971 Image
```
The facade codeunit above has some characteristics:
- Access is explicitly set to Public, to underline that this is a facade.
- All methods are public.
- All methods are documented.
@ -102,7 +105,7 @@ Anyone who wants to access the subsystem, will only have to relate to this one p
Test of the subsystem can be limited to testing the facade - it is strictly speaking the only thing that needs verification, that it functions as designed. It is the contract of the subsystem.
*The Subsystem*
_The Subsystem_
```AL
codeunit 3970 "Image Impl."
@ -135,6 +138,7 @@ codeunit 3970 "Image Impl."
There are no rules for the subsystem, except that access needs to be **internal**. How you implement, how much you document, how you test, is entirely up to you and not the business of the outside caller. Of course, you should apply all of the best practices and patterns anyway, as you and possibly other developers will have to understand, extend and maintain the subsystem too. But from the view of this pattern, the complexity of the subsystem is irrelevant - just as long as it's not accessible.
## Usage
The facade pattern is one of the most prominent patterns in the [system application](https://github.com/microsoft/ALAppExtensions/tree/main/Modules/System). You will find plenty of examples here.
## Benefits
@ -142,18 +146,23 @@ The facade pattern is one of the most prominent patterns in the [system applicat
The benefits of this rather simple pattern should be abundantly clear by now. But let's go over them once more, structured by the advantages this patterns brings:
### Decoupling
As the entire subsystem is inaccessible to outside systems, no dependencies can be taken. Hence this patterns strongly promotes the decoupling of objects.
### Encapsulation
The entire purpose of this very pattern is to encapsulate complexity; you hide away the implementation details behind an easy to understand facade.
### Readability
If done right, the developer doesn't need to be able to understand the details of the subsystem. Everything relevant to using the subsystem is described in the facade.
### Testability
Ensuring the correct behaviour of the subsystem can be done by testing the facade. The facade defines the contract of your subsystem - what does it expose and how should it behave. That contract should be covered with adequate tests, which will ensure that it is upheld, even if you decide to change the implementation of the subsystem.
### Maintainability
The one thing you may not change freely, is the facade and the test of the facade. It can be extended, but you should not break any existing APIs. But that leaves the entire subsystem to be completely rewritten, if you desire to do so. As no external dependencies can exist, there is no risk of introducing any syntactical breaking changes to the outside world. And as the tests of the public facade remain, there is no risk of introducing semantical breaking changes either - the contract is upheld, as long as your tests pass.
## When not to use
@ -173,6 +182,3 @@ This is one of the most commonly used and discussed, initially described here:
It is also a key pattern in the design of our system application modules, which is described here:
[Module Architecture](https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-blueprint)
## Discussions
You can discuss this pattern [here](https://github.com/microsoft/alguidelines/discussions/42)

View file

@ -9,6 +9,7 @@ _Created by Gary Winter (Cloud Ready Software), Described by waldo (iFacto Busin
## Abstract
The goal of this pattern is to facilitate a lot of things in one single awesome way of writing code. If you apply this pattern as a general pattern, you'll implement:
- Extensibility
- Decoupling
- Readability
@ -25,11 +26,11 @@ Usually, when you ask people where to place code, they all have their own opinio
## Description
What if we have some kind of "standard way" to always write our code. The *Generic Method Pattern* is kind of like what it says: a generic way to implement a method.
What if we have some kind of "standard way" to always write our code. The _Generic Method Pattern_ is kind of like what it says: a generic way to implement a method.
### What is a method?
Well, a method is *a significant piece of business logic* - maybe best explained by some examples:
Well, a method is _a significant piece of business logic_ - maybe best explained by some examples:
- Posting a document
- Any button on a page that executes business logic
@ -41,7 +42,7 @@ In a way, except "data validation", most of the things we write in our daily lif
### The Pattern
**One method, one codeunit**
The idea is to put the code in one *encapsulated* codeunit with the purpose to have all the code in that one codeunit function for that one method. This way, the codeunit will stay relatively small and readable.
The idea is to put the code in one _encapsulated_ codeunit with the purpose to have all the code in that one codeunit function for that one method. This way, the codeunit will stay relatively small and readable.
Let me start by showing an example, so you can refer to this complete example during the rest of the article:
@ -101,17 +102,19 @@ codeunit 53100 "WLD BlockCustomer Meth"
```
Within that codeunit, the pattern is always the same:
- One public (internal) procedure
- The rest is always local
So, from outside the codeunit, there is only one clear entrypoint: that one (public) internal function with its parameters.
The **pattern** within the codeunit exists of a few layers:
- The UI layer
- The Event layer
- The method layer
*The UI layer*
_The UI layer_
The UI layer takes care of the UI, obviously. What is important in this case, is that you always make sure that there is a "HideDialog" parameter that the business logic can use to still decide whether to use the dialog or not.
These are the UI Layer parts, where you see the public function gets the HideDialog, and passes it to the UI-related procedures, where the business logic for showing the UI takes place. Also, the default answer of the confirmation is handled there as well (what if the business logic calls this method with HideDialog to "true").
@ -152,7 +155,7 @@ codeunit 53100 "WLD BlockCustomer Meth"
}
```
*The Event layer*
_The Event layer_
This layer is going to add flexibility to any app that has a dependency on this app. By default, the pattern always foresees an `OnBefore` and an `OnAfter` event.
This is the relevant code for the event layer:
@ -183,7 +186,7 @@ codeunit 53100 "WLD BlockCustomer Meth"
}
```
*The method layer*
_The method layer_
The last layer is obviously where the business logic will be written.
The relevant part is:
@ -211,7 +214,9 @@ codeunit 53100 "WLD BlockCustomer Meth"
...
}
```
Usually indicated with a "do"-function, the business logic takes place in that procedure. Obviously, when you have a decent amount of code, it's recommended that you make it readable by applying all the Best Practices in terms of readability in the codeunit. Though, a few pointers here:
- keep the [cyclomatic complexity](https://en.wikipedia.org/wiki/Cyclomatic_complexity) low
- one line (function call) after an IF-clause
- one line (function call) after a repeat
@ -243,11 +248,12 @@ tableextension 53100 "Customer Ext BASE" extends Customer
This practice improves readability. In fact, by doing this, you just extended the suggestions-list in VSCode (IntelliSense) indicating a new method that your class can do. This is very convenient for the developer that might need your new method.
*Note - it could very well be that there simply isn't any table that can act as a class for our method. In that case, you could use a codeunit as well.*
_Note - it could very well be that there simply isn't any table that can act as a class for our method. In that case, you could use a codeunit as well._
**Naming Conventions**
You might have noticed that the naming of our method is quite strict:
- codeunit name: `WLD BlockCustomer Meth`
- internal proc: `BlockCustomer`
- do-procedure: `DoBlockCustomer`
@ -255,6 +261,7 @@ You might have noticed that the naming of our method is quite strict:
It is important to align these namings. It indicates that the codeunit only does one thing (remember: encapsulation), and it improves searchability from outside the codeunit (for example when you're searching symbols or something).
## Usage
Currently, there is no usage of this pattern in the BaseApp.
The pattern has a main advantage in an ISV product, just because of the decoupling and extensibility. Although, I have seen many occasions where parts of the pattern was useful on PTE's as well. You simply never know if ever at the customer site, there is going to be another partner that needs to create its own PTE, and has to depend on yours. So I'd say, this pattern is everywhere applicable, no matter the type of the app.
@ -264,9 +271,11 @@ The pattern has a main advantage in an ISV product, just because of the decoupli
As I said, it will facilitate a lot of advantages. Let's explain a bit more in depth:
### Extensibility
Thanks to the *event layer*, by applying this pattern for all methods, we will automatically have the bare minimum of events that we need to hook into a method: the `OnBefore-` and the `OnAfter`. Of course it would make sense to even add more events to the method when appropriate (eg, when you're inserting a record in a table, it might be interesting to also raise an event just before you call the insert).
Thanks to the _event layer_, by applying this pattern for all methods, we will automatically have the bare minimum of events that we need to hook into a method: the `OnBefore-` and the `OnAfter`. Of course it would make sense to even add more events to the method when appropriate (eg, when you're inserting a record in a table, it might be interesting to also raise an event just before you call the insert).
### Decoupling
Thanks to these same events, and the fact the pattern foresees a handler as well, we are able to "decouple" our method as well. What do I mean with that? Well, we can simply subscribe to the `OnBefore`event, and set `IsHandled` to `true`. This means it will never execute the do-procedure, which means, the original procedure/method/business logic is "decoupled".
We can use this obviously for implementing our own method (a new way to accomplish this method), or to disable the method by simply subscribing to it, and only providing the `IsHandled := true` in our subscriber. However, there are many more usages where we can use this for.
@ -277,34 +286,41 @@ if you would apply this pattern to your product, at the customer, you'll be able
This gives a lot of flexibility.
### Readability
When we talk about readability, we actually talk about the part where we expose our method on the class. The rule is: never call the codeunit, but only from one place: from its "class" - or in BC terms: its table (or codeunit).
In terms of readability, that means that intellisense comes into play. In stead of:
```AL
Codeunit.Run(Codeunit::"Sales-Post", SalesHeader);
```
you simply get
```AL
SalesHeader.Post();
```
THAT is readable. The previous is not! That is just something we got used to.
### Testability
There are two things in terms of testability where this pattern helps a lot.
*Unit testing*
_Unit testing_
You can interpret "unit testing" very broadly. But just imagine: when you're building your software entirely out of "methods" - which means: when you'd build your software entirely with this "Generic Method Pattern". Now, the list of methods, are all the units that you need to test: if you test all your methods, you kind of like test the majority of your software, right?
So you could simply set up rules in your company like: EVERY method needs a test-codeunit. And even more: since every method only has one global function - it's pretty easy to know the context, and all the flavors to test your method.
The pattern describes the tests that needs to be written.
*Disabling methods*
_Disabling methods_
Coming back to the "decoupling" part - in tests, you actually might need it more than you realize. Just imagine: you want to test method 1, but method 2 comes in the way by interfering with configurations that you need to do, or UI that is popping up, while it could be completely pointless.
Solution: simply - within your test-codeunit - subscribe (with a manual subscriber) to method 2, set `IsHandled` to `false` - done!
### Encapsulation
Don't underestimate the power of the encapsulation part of this pattern. One of the first questions that people ask themselves when reading into this pattern is: "*isn't it going to consume all my codeunit-id's*" or "*so many codeunits, that can't be readable, right?*".
Don't underestimate the power of the encapsulation part of this pattern. One of the first questions that people ask themselves when reading into this pattern is: "_isn't it going to consume all my codeunit-id's_" or "_so many codeunits, that can't be readable, right?_".
The fact that the functionality of one method is encapsulated in one codeunit is very powerful. You'll avoid [Boat anchors](https://sourcemaking.com/antipatterns/boat-anchor) simply because because, thanks to the encapsulation, there is a limited amount of code in the codeunit, of course.
And because of that, it so much more maintainable, upgradable, readable, .. . Only advantages.
@ -316,13 +332,14 @@ So all I can say is: use your common sense.
One example: set the bar at "validation code": any code that is solely there to facilitate data integrity doesn't belong in method codeunits.
Another tip might be: don't let the amount of codelines trick you in deciding to *not* use this pattern: when it's a method, it's a method. When it makes sense to be able to extend, decouple, .. then this pattern can help.
Another tip might be: don't let the amount of codelines trick you in deciding to _not_ use this pattern: when it's a method, it's a method. When it makes sense to be able to extend, decouple, .. then this pattern can help.
## Snippets
[waldo's CRS AL Language Extension](https://marketplace.visualstudio.com/items?itemName=waldo.crs-al-language-extension) contains snippets that help you in setting up the boiler plate code in a matter of seconds.
The snippets are:
- `tcodeunitMethodWithoutUIwaldo`
- `tcodeunitMethodWithUIwaldo`
@ -331,7 +348,3 @@ The snippets are:
There have been a number of occasions where people have been sharing this pattern. Here is one:
{{< youtube id="CWpaD9RUa6U" yt_start="1516" >}}
## Discussions
You can discuss this pattern [here](https://github.com/microsoft/alguidelines/discussions/41)