AOP vs Functions

Aspect-oriented programming (AOP) is quite a popular programming paradigm. The article on Wikipedia explains well the motivation of this approach.

image

AOP is a great tool for global concepts such as: logging, which, directly, does not affect the logic of the code.

However, problems with AOP are detected when it is used for business requirements, such as authorization. Such aspects should be clearly visible in the appropriate code so that the developer can immediately see, when reading the source code, whether they are correctly implemented. AOP frameworks usually solve this problem with annotations:

@RequireRole(Role.Admin)// clearly visible aspectfunupdateUserPermissions(…) {
    // logic here
}

However, in terms of readability, it is not much different from the functional approach, using the requireRole method instead of annotation.

Translator's note : the original article uses the expression when rewritten in a functional way or the functional approach that can be interpreted as using a functional approach as well as a direct / specific function call. The article has examples that can be interpreted in different ways.

Also:

1. In the following, we will return to the concept of Higher-Order Functions

2. The article contains the word aspect, which is Anglicism and the concept in AOP aspect


funupdateUserPermissions(…) {
    requireRole(Role.Admin) // throws SecurityException // logic here
}

Moreover, the functional approach has the advantage of scaling up to more complex access control checks, such as analyzing method parameters, before deciding which user role is required.

The same is true for other types of aspects, such as transactions. Unfortunately, functionally presenting more complex concepts in Java is cumbersome and inconvenient, which creates artificial popularity of AOP frameworks in the Java ecosystem.

In Kotlin, instead of a Java-like approach to transactions using AOP and annotations, like this:

@TransactionalfunupdateUserPermissions(…) {
    // logic here
}

It is also readable and looks clean when rewritten without the use of annotations, with a functional approach.

funupdateUserPermissions(…) = transactional {
    // logic here
}

The advantage of this approach is that you can always press Ctrl / Cmd + Click on the declaration of the transactional function in your IDE and immediately see what it does. What is usually impossible with any of the commonly used AOP frameworks. Even when navigation to an aspect code is provided using an IDE plug-in, deciphering its logic requires knowledge of a separate multi-functional API and / or conventions.

Unfortunately, there are problems with scaling the way Kotlin replaces AOP annotations. For the case when several aspects are applied to the same function, with an accumulation of braces and indents:

funupdateUserPermissions(…) = logged {
    transactional {
        // logic here
    }
}

A workaround is to create a higher-order function to preserve acceptable code when applying several aspects:

funupdateUserPermissions(…) = loggedTransactional {
    // logic here
}

Another disadvantage of the functional approach is that aspects such as logging require access to method parameters. They are usually available in AOP frameworks through special APIs, but using the standard functions of the Kotlin language you cannot easily access them. Thus, in order to actually present a real example of the logging aspect, in a purely functional form, you still need to write a significant amount of boiler-plate code :

funupdateUserPermissions(params: Params) = 
    logged("updateUserPermissions($params)") {
        // logic here
    }

This consideration still makes AOP the preferred logging tool when you really need to have it globally and consistently in your application. However, the use of AOP for such functionality as authorization and transactions is an abuse, given the rich functional abstractions that are available in Kotlin. Functions cope with these requirements better and cleaner.

In conclusion, I would say that further improvement of functional abstractions to provide an even better replacement for AOP could be a promising vector for the future development of the Kotlin language. Java-based AOP frameworks are usually JVM specific and are perceived as magic, while Kotlin's functional abstractions are truly cross-platform and transparent to the user.
Translator's note :
1. Article on medium (eng) .
2. The author of the original article is Roman Elizarov (Team Lead JetBrains, sports programming / ICPC, concurrency & algorithms, math / quantitative finance; formerly Devexperts). On Habré elizarov

Also popular now: