ABAP to the future – my version of the BOPF chapters – Part 6: Validations
In “ABAP to the future” (A2tF) it is being said that validations in BOPF are meant to check the consistency and prevent saving. This is only partially correct. Validations are those parts of the model which represent idempotent (repeatable without changing the state) checks. When a validation is being executed and what the effect of a failed validation is, depends on the validation’s configuration. In general, there are two types of validation configuration: Action validations and consistency validations. The implementation of both types is based on the same interface (/BOBF/IF_FRW_VALIDATION) and thus, the same implementation class can be used in multiple places. Therefore, I also recommend restricting the scope of a validation as much as possible in order to facilitate re-use.
We still don’t really know what an action is (this will be covered in the next chapter of the book), but we’ll start with action validations. An action validation is – surprise, surprise – registered to be executed before an action is being executed. If the validation fails, the action will not be invoked for the failed instances.
Actions can easily be invoked from the Test-UI, this includes the execution of the action validations. Or you can execute the core-service “check action” from the check-menu.
Figure 46 – Result of a failed action validation: You can see the error message and the highlighted origin location.
Not only modeled actions can be subject to validations, but also the so-called framework actions. For our purpose, it is now enough to know that for each node, a CREATE_<NODE>, UPDATE_<NODE>- and DELETE_<NODE> action exists and is being executed by the framework when performing a corresponding modification.
Figure 47 – Configuration of the alternative key validation: The framework actions are prevented if the alternative key is not unique
Let’s have a look at a sample which we foreshadowed some chapter ago: The business requirement reads that howling at the moon should only be possible for monsters with at least one head.
Figure 48 – Definition of an action validation. The really important part is the “trigger actions”
Figure 49 – Trigger actions: which actions are guarded by the validation
In addition, in future werewolves should be able to howl at the moon only if it’s full moon. The two aspects are semantically independent; therefore we create two validations which we both register for the action HOWL_AT_THE_MOON: HAS_AT_LEAST_ONE_HEAD and IS_FULL_MOON. If later on, we might for example add an action BITE, we could also register the HAS_AT_LEAST_ONE_HEAD for it, but not the full-moon-check (assuming that also human-bodied werewolves can bite). This additional configuration would not require a single line of code and makes our model more readable with respect to the pictured business.
The implementation of HAS_AT_LEAST_ONE_HEAD looks very similar to what we coded in the previous chapter’s property determination with one big difference: We now want to prevent the action execution and provide a meaningful error message. Remember: The property-determination was only executed on request of the consumer (the UI-layer).
DATA lt_root TYPE zmonster_t_root. “The combined table type of the node to be retrieved
CLEAR: et_failed_key, eo_message.
iv_node = zif_monster_c=>sc_node–root
it_key = it_key
it_requested_attributes = VALUE #( ( zif_monster_c=>sc_node_attribute–root–number_of_heads ) )
et_data = lt_root ).
LOOP AT lt_root ASSIGNING FIELD-SYMBOL(<ls_root>).
IF <ls_root>-number_of_heads = 0.
* Get a message container. Unfortunately, the message container is not injected as a handler,
* so we must instantiate it actively. #architectural_fail
IF eo_message IS INITIAL.
eo_message = /bobf/cl_frw_factory=>get_message( ).
* Create a message by instantiating the message-class. Don’t use the instantiation of /bobf/cm_frw_core as written in the book
* (this is bad style), but simply make your exception-message-class inherit from /bobf/cm_frw.
* There’s also a blog post about message-objects in BOPF on SCN ;). The constructor-expressions pay-off very well in this case!
eo_message->add_cm( NEW zcm_monster(
textid = zcm_monster=>no_head
severity = zcm_monster=>co_severity_error
symptom = /bobf/if_frw_message_symptoms=>co_bo_inconsistency
lifetime = /bobf/if_frw_c=>sc_lifetime_set_by_bopf
ms_origin_location = VALUE #(
bo_key = zif_monster_c=>sc_bo_key
node_key = zif_monster_c=>sc_node–root
key = <ls_root>-key ) ) ).
* The message just created is not meant to be interpreted at all,
* neither by the framework nor by a consumer. What really matters
* (and what makes the framework not execute the action in case of an action validation)
* is the indication of the instance as failed
INSERT VALUE #( key = <ls_root>-key ) INTO TABLE et_failed_key.
ENDIF. “monster has no head
The validation code will always be executed if the action to howl at the moon is about to being performed or if the core-service “check_action” is being executed by a consumer: In this case, all action validations configured for the requested action will be executed. In contrast to what’s been said in the book, there is no option to invoke a particular validation.
A consistency validation is a check which is being executed as a side-effect of an interaction with a business object changing the state of one or multiple instances (without a change of its state, the consistency of the instance cannot change anyway). As such, the configuration of a consistency validation is comparable to the configuration of a determination: You can select trigger nodes and modification (CUD). Additionally, all consistency validations which have been configured on “check” are being executed through the core-service check_consistency.
Figure 50 – Invoking the core-service “check consistency” on the test-UI (including the scope-options “local” and “substructure”)
While an action validation prevents the action if it fails, the consistency validation’s reaction on failure is defined by a so-called “consistency-group”: Either it can prevent saving or it can set a consistency-status (which is a dedicated node-attribute).
Figure 51 – A consistency check fails on update – but accepts the values give on update (creator) nevertheless. An action validation would have rejected the complete modification.
Figure 52 – Result of a failed consistency validation preventing the save.
Setting a status value is particularly interesting if you think about stateless-applications in which a save is effectively being requested after each round-trip: The framework will mark the instance as inconsistent if a consistency validation fails on save. With a new stateless request, this consistency-status can be interpreted in order to prevent a subsequent action which should only be possible for consistent instances.
In order to define a consistency validation, one needs to define the validation itself and assign it to a consistency group.
Figure 53 – Configured consistency validation
Figure 54 – The trigger of the validation is defined in the validation’s trigger conditions
Figure 55 – The consistency group defines the reaction on failure of one of the assigned consistency validations
With respect to implementation, there’s no difference compared to action validations (both implement the same interface). But as consistency-validations are being executed as a result of a modification (which – once again – the consistency validation will not prevent), there’s good reason to also implement the check and check_delta-methods, which don’t make much sense for action validations. I will not provide a complete implementation now (you can read one in A2tF), but I want to point out some of the major mistakes made (which you unfortunately can also find in the book):
- As all interface-methods,also the validation methods are mass-enabled! If you ever see a READ INDEX 1
- Most of the BOPF framework-classes (containg _frw_/bobf/cl_frw_factoryio_read and).
- For message-classes or libraries, inheritance is a common reuse-mechanism in BOPF.
- In addition, all the pitfalls mentioned in the chapter on determinations apply – including the lengthy “no need for a single model class”-pledge.
About raising exceptions
As you might have noticed, the signature also features an exception of type /BOBF/CX_FRW which can be raised and you may wonder whether it would not be suitable for failed validations. The short answer: No, it is not. The system will go for a (“controlled”) short-dump. I could also go for a long explanation why this is desired, but I fear no-one is really interested in reading it. If you would like to know more about it, please comment 😉