Loose coupling with commands and dependency injection
as this is my very first blog post, I really would like to give a very short introduction to myself. I live with my family in Ingelheim, Germany. A small town located near Mainz in the beautiful wine region of Rhinehessen (Rhineland-Palatinate).
I work since 20 years mainly as a functional consultant in the area of accounting and controlling. But I have a dark side…I love programming 😈
The recent years I have changed my programming style from the procedural style to more object oriented style. Actually it was a quite hard transition and took me a while to fully understand the concepts behind it.
Over the years I realized that my developments still are not well designed, even if they are object oriented. Every time when new requirements came in, I had to change a lot of the existing code. Also I wanted to be sure that the new code did not break the existing code. And in addition I was looking for some kind of methods, where you can do developments on your own system and not the final customers environment, but still be able to test the code with proper test data. After researching and studying for a while I found what is known as the SOLID Design Principles and Test-driven development. Since this time I try to follow those principles whenever it is possible.
There is one concept which I discovered recently and I thought this is something worth to give it a try. The original design is described here https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=91. I have transformed the examples from there into ABAP and thought others might be interested into the design as well. So here it is.
The basic idea behind the whole concept is that methods should either perform an action (Commands) or return data (Queries), but not both. This concept is also known as Command-Query Separation (CQS). We will have a look at the command site for now.
We first need an Interface which represents our command.
interface lif_command. endinterface.
Well, this doesn’t look to complicated. The next thing we need is our interface for the command handler.
interface lif_cmd_handler. methods handle importing i_command type ref to lif_command. endinterface.
We are now able to decouple the business logic from the data. The command handler now operates on the command that we provide. Using interfaces give us a lot of flexibility, which we will see later.
We can now create our concrete command. As you can see our command is a pure data object without any logic (Setter and Getter methods would be possible). We could also create a data reference for our data, but I decided to use objects for data representation as well.
class lcl_move_customer_cmd definition. public section. interfaces lif_command. data customer_id type i. data new_adress type string. endclass. class lcl_move_customer_cmd implementation. endclass.
Here is the implementation of our concrete command handler.
class lcl_move_customer_cmd_handler definition. public section. interfaces lif_cmd_handler. endclass. class lcl_move_customer_cmd_handler implementation. method lif_cmd_handler~handle. data cmd type ref to lcl_move_customer_cmd. cmd ?= i_command. cl_demo_output=>write( 'I handled the command.' ). cl_demo_output=>write( 'Customer:' && ` ` && cmd->customer_id ). cl_demo_output=>write( 'Adress:' && ` ` && cmd->new_adress ). endmethod. endclass.
The command handler receives our command and process the data. The only thing here which is not nice, that we have to cast to the concrete command type ( cmd ?= i_command ). The original C# code shows some kind of type checking using generics which is something that is not available in ABAP. At least I haven’t found anything in ABAP. So, if somebody knows a possibility to avoid the casting here, please let me know.
We now need the controller which knows how to operate.
class lcl_controller definition. public section. methods constructor importing i_handler type ref to lif_cmd_handler. methods move_customer importing i_customer_id type i i_new_adress type string. private section. data handler type ref to lif_cmd_handler. endclass. class lcl_controller implementation. method constructor. * Constructor injection. Assert ensures that controller is working correct assert i_handler is bound. me->handler = i_handler. endmethod. method move_customer. data(cmd) = new lcl_move_customer_cmd( ). cmd->customer_id = i_customer_id. cmd->new_adress = i_new_adress. * Passing the data object to the handler me->handler->handle( cmd ). cl_demo_output=>display( ). endmethod. endclass.
The handler will be injected into the controller via constructor injection
class lcl_main definition. public section. class-methods start. endclass. class lcl_main implementation. method start. data(handler) = new lcl_move_customer_cmd_handler( ). data(controller) = new lcl_controller( handler ). controller->move_customer( i_customer_id = '12345' i_new_adress = 'The new adress' ). endmethod. endclass.
Now we come to the most interesting part. As our command handler is based on a abstraction, the command handler interface, we are now able to create simply a Decorator for the handler, which means we can add additional functionality like validation or implementing cross cutting concerns like logging without changing the existing code.
It is just a simple example, but I think you can imagine how powerful this can be.
class lcl_customer_cmd_decorator definition. public section. interfaces lif_cmd_handler. methods constructor importing i_decorated_handler type ref to lif_cmd_handler. private section. data decorated_handler type ref to lif_cmd_handler. endclass. class lcl_customer_cmd_decorator implementation. method constructor. * Constructor injection. assert i_decorated_handler is bound. me->decorated_handler = i_decorated_handler. endmethod. method lif_cmd_handler~handle. data cmd type ref to lcl_move_customer_cmd. cmd ?= i_command. cl_demo_output=>write( 'I decorated the command before.' ). cl_demo_output=>write( 'Customer ID validated.' ). me->decorated_handler->handle( cmd ). cl_demo_output=>write( 'I decorated the command after.' ). cl_demo_output=>write( 'Save Customer ID with new adress.' ). endmethod. endclass.
All we have to do is setting up the handler correctly with the decorated handler. For details about the Decorator Design Pattern you can look here.
class lcl_main implementation. method start. data(handler) = new lcl_customer_cmd_decorator( new lcl_move_customer_cmd_handler( ) ). data(controller) = new lcl_controller( handler ). controller->move_customer( i_customer_id = '12345' i_new_adress = 'The new adress' ). endmethod. endclass.
The output will be now:
A while after I discovered the original blog post, I noticed that there is already a formal description for the concept. The design is described as the Command-Processor Pattern.
I hope you’ll find this blog useful and gave you some inspiration. Also if you find something that can be improved or enhanced, please let me know. The next time I will show an example for the query part.
The complete source code for the Example report can be found here.
Thanks for sharing your post.
Like in most of the design patterns, the main concern is: Flexibility vs. complexity.
May you please elaborate on some real-life use cases in SAP?
my main concern is not Flexibility or Complexity, it is regarding Maintainability and Testability.
I have no idea whether this design is used somewhere in the SAP standard environment. But I am actually use this design in a project where I have to implement some kind of business logic for postings.
I have a command which is the 'Line item', a command handler which is my 'Line item validator' and the 'Controller'.
The main point here is that I am now able to create unit tests to start with coding and test my business logic, without having somebody do some real postings all the time in the test system. In this case it is not even possible, because there are some master data set-up missing (does this sound familiar?). I can just create this set-up in my test-classes and already start with the development and testing the logic. I can also be sure if later things change I can add functionality just by creating a decorator for the handler, without breaking the code. I consider this as a huge advantage.
Thanks for the info.
You are right. This is an important advantage.
On the other hand, it does add some complexity, which need to be taken into account.