You are on page 1of 45

Business Objects Processing Framework

(BOPF) Why ABAP developers should have


waited for it!
Posted by Martin Fischer in BOPF Application Framework on Oct 10, 2013 3:31:45 PM
inShare
There are a lot of new and very interesting and hyped new technologies in the SAP world: HANA,
mobile, cloud, NetWeaver Gateway, SAP UI5 and some more. That makes life very exciting for
developers and geeks. I also like to use and test new technologies just for the sake of technology.
And there is BOPF, an ABAP development framework which is used inside the SAP development
teams since years (e.g. SAP Transportation Management is based on BOPF). It really makes custom
development faster, more stable and the applications developed with BOPF have a similar
architecture. BOPF is part of the Business Suite Foundation and since this spring general available
for customers. BOPF is a framework which supports developing ABAP applications. This might sound
a bit boring compared to the new technologies mentioned before. In this blog I want to explain why
BOPF is a MUST for every ABAP developer who ever works in custom development projects.

When I started in my first big ABAP based custom development project a couple of years ago, we
started to build up our own object oriented ABAP development framework. We used a common three
layer architecture and built for some extend a generic database layer. This was quite a lot of work, but
it made it easy for the developers to use our database layer. Only a few developers knew exactly how
it works. All the others just used the provided interfaces to access it. We had a central transaction
manager which was responsible for committing the data and a common database lock handling. We
also used common design patterns for the rest of the framework. There I realized how important it is
to have a framework in big projects. New developers could focus on implementing new business
functionality and because we had our common patterns it was quite easy for them to switch from one
business object to another. But I have to admit, that it was quite hard work to implement and maintain
the framework.

With BOPF there is no need to design and implement such a framework on your own. You can just
use BOPF and it brings even more time saving features for the developers. In BOPF you start to
model your business objects in a declarative manner. You decide which entities a business object
has, which actions can be executed on the entity and how this entity is associated to other entities of
the same or entities of other business objects. The BOPF framework itself is implemented in a very
generic way. The framework interprets the defined meta model and provides methods to interact with
the business objects.
After modelling your business objects, including data structures, keys, associations and actions
BOPF supports you generating the needed DDIC objects and also interfaces for constants. As you
also define the lock behavior of each entity, you can already test your business objects using the
BOPF Test UI. The declarative way of modelling the business object has also a big advantage: BOPF
generates a visualization of the defined model where you can easily see how the entities relate to
each other. This visualization can also be used as a base for discussions between the functional
architects and developers. So its easier for the functional architects to understand how the system
works.
Example of BOPF model visualization

Furthermore with FBI (Floor Plan BOPF Integration) there is an easy and fast way to implement UIs
for BOPF objects. This is an integration layer between BOPF and SAP Floor Plan Manager (FPM).
You can define your UI elements based on your business model and the defined UI is from the
beginning able to read and change data.
Of course, it needs some time to be able to work with BOPF and to fully understand it. But this is a
onetime effort. Afterwards you will deliver your custom development projects much faster as today.
And you do not need to care about transaction handling and lock handling, because the framework
takes care about all this. As it is used since years within SAP development, the framework is mature
and stable. If you run into problems with the framework you can rely on your SAP support you have.
If BOPF becomes common knowledge in the ABAP world, is makes it easy for developers to start in a
new project. The developers can focus on the business functions and do not need to do a deep dive
into the underlying development framework and architecture.
With BOPF your team should have object oriented programming skills. Which is still a problem in the
SAP world. But if you have a look on all the new technologies pushed by SAP, there is now future as
an ABAP developer without these skills.
If you are going to EMEA TechEd in Amsterdam, join Oliver Jgles session CD219, on November
6th:
http://sessioncatalog.sapevents.com/go/ab.sessioncatalog/index.cfm?l=57&sf=1504
He will tell his experience with BOPF in a custom development project.

Wouldn't you like to streamline and simplify the development process for your business applications?

Then you should get to know more about BOPF, our infrastructure for developing business objects
that is available for the SAP Business Suite. With the Business Object Processing Framework, you
will save time during the development cycle because you don't have to implement all the technical
details yourself - details such as authorization control, low-level transaction handling, buffer
management, provisioning of consumer API, or business logic orchestration. Using the model-driven
approach in BOPF, you can instead focus your attention more on the actual business requirements
themselves.
What does BOPF stand for?
The Business Object Processing Framework is an ABAP OO-based framework that provides a set of
generic services and functionalities to speed up, standardize, and modularize your development.
BOPF manages the entire life cycle of your business objects and covers all aspects of your business
application development. Instead of expending effort for developing an application infrastructure, the
developer can focus on the individual business logic. Using BOPF, you get the whole application
infrastructure and integration of various components for free. This allows you to rapidly build
applications on a stable and customer-proved infrastructure.

Who uses BOPF?


BOPF is not really a new framework. In fact, it is well established and broadly used in multiple SAP
ByDesign and SAP Business Suite applications and products for example, Transportation
Management (TM), Environment, Health and Safety (EH&S), SAP Supplier Lifecycle Management,
SAP Management of Change, SAP Quality Issue Management, to name but a few. Apart from its use
in SAP internal development, BOPF is also used in customer development projects.

How can SAP customers use BOPF?


Due to increased interest, BOPF is also used in SAP customer development projects. It is released
with SAP Business Suite EHP5 SP11, SAP Business Suite EHP6 SP05, and SAP Business Suite
EHP7. However, it is not available in the SAP NetWeaver standalone system, but only in the
Business Suite Foundation layer, so that every SAP Business Suite customer can use it for their own
development projects.

What are the main components of the application infrastructure?


With BOPF, you have a framework at your disposal with which you can seamlessly integrate different
components of business applications. You can use them out-of-the-box. The advantages of using
BOPF are thus obvious:
When using BOPF you dont have to care about the development of adapters or integration layers to
consume the following components:

Figure: BOPF acts as a bridging unit between various components


User Interfaces and Consumption

BOPF provides a standard interface for consumption by the


Dynpro
classic Dynpro UI.

The generation and configuration of complex user interfaces has


never been as easy as it is today with the FPM. FPM is
implemented as a Web Dynpro component and can be easily
integrated with BOPF. BOPF provides configurable and codeless
Web Dynpro / Floor Plan
integration of FPM and enables you to seamlessly consume the
Manager
services of BOPF Business Objects in a modification-free
(FPM)
environment.

More: Floorplan Manager for Web Dynpro ABAP and Web Dynpro
ABAP on SCN

SAPUI5 is designed for building lightweight UIs for casual use.


SAPUI5
More: UI Developer Center on SCN

SAP NetWeaver Gateway is a technology that provides a


convenient way to connect devices, environments, and platforms
to SAP software based on market standards. The BOPF
Gateway (OData) integration of the Gateway is based on REST and OData
standards.

More: SAP NetWeaver Gateway on SCN


.
The Business Object Layer (BOL) provides a generic API for
accessing business data.
The Generic Interaction Layer (GenIL) enables uniform access to
Business Object Layer & GenIL business data using a stateless request/response format. BOPF
provides adapters for BOL and GenIL integration.

More: SAP CRM on SCN

Process Integration

With BOPF BOs, you can integrate business processes using the Post
Post Processing Processing Workflow.
Workflow
More: Post Processing Framework (PPF) (on SCN )
Infrastructure Component

With ADK you archive not only table records but also business
Archive Development instances. Using BOPF you can select which BO instances have to be
Kit archived and then trigger the archiving process for them.
(ADK)
More: Archive Development Kit on the SAP help portal

OPF uses the SAP NetWeaver Change Documents solution for


recording changes on business object data.
Change Documents
More: Change Documents on the SAP help portal

Application logging is used to record particular events during the


execution of an application so that you can reconstruct them later, if
necessary.
In BOPF, application logging is integrated with the help of the
Application Log BO. This business object supports you when reading
Application Logging
or writing application-specific log messages.

More: Application Log - Guidelines for Developers on the SAP help


portal

The search service of SAP NetWeaver provides a framework for


enterprise-wide indexing of and searching for structured (business
objects) and unstructured data (documents).
BOPF design time provides you with a convenient way for enabling
Enterprise Search BOPF business objects for Enterprise Search. Using an existing BO
model, you have the option of importing the BO data and, in this way,
creating a corresponding Enterprise Search model.

More: Enterprise Search on SCN

BRF+ is a rule engine. It provides a comprehensive API and user


interface for defining and processing business rules and expressions.
With a BOPF-specific expression type, it is possible to create BO data
Business Rules Framework retrieval expressions in BRF+. These expressions make data of BOs
plus (BRF+) available in BRF+ environments that support any kind of rule
processing.

More: Business Rule Framework plus on SCN

What are the elements of the programming model?


The business objectsare the basic units of the BOPF-based programming model. Business
applications or processes operate on certain business objects. A business object is represented as a
hierarchical tree of nodes. A single node includes a set of semantically related business object data
and the corresponding business logic. On the technical level, each node is implemented with a
regular Dictionary table, where each node instance corresponds to a single table entry (table rows).
Nodes and attributes set up the data part of a business object. Again from a technical viewpoint,
attributes form the columns of the table. A node serves as an anchor point for connecting the
business logic of the business object.
For each node, several types of entities can be defined to form the specific business logic part of a
business object.

Node Entity is used ...


to implement a service (operation or behavior) of a business
Action object. An action is explicitly triggered by a service consumer,
such as the user interface.
to provide functions that are automatically executed as soon as
certain trigger conditions are fulfilled. A determination is
Determination
triggered internally due to changes made to the instance of a
business object.
to either validate whether a specific action can be executed on
Validation a specific node instance (action validations) or whether a set of
node instances is consistent (consistency validations).
to search for business object instances that meet certain search
Query
criteria.
to connect business object instances that are located on
Association
different nodes.

Figure: Business object metadata model


What kind of development environment is available for BOPF?

Tool Support Features

These are internal SAP tools and utilities that provide


developers with a comprehensive feature set for
SAP Internal Design Time Tools (BOBF)
building BO-centered applications for SAP Business
Suite.

With this tool, customers can enhance SAP business


objects and also create their own ones in the
BO Builder customer namespace.
(BOB) The creation of business objects, nodes, actions,
determinations, queries, or validations is extensively
supported by wizard-driven tools and utilities.

Starting from development environment, you can


BO Builder Test Environment (BOBT) always test the current range of functions of each
business object (or enhancement).

Debugging on the business object entities level


enables you to speed up the troubleshooting process:
You can set breakpoints for entities such as actions,
BO-Specific Debugging determinations, and validations. Furthermore, you
have the option of setting watchpoints for certain
activities on the nodes (for example, to update a
particular node attribute).

BOPF supports test automation and test-driven


development. From the outset, you can apply ABAP
unit tests to test isolated units of business objects,
Integrated BO Test Infrastructure such as actions, validations, or determinations. In
addition, with the scenario testing function, you can
extend the tests to processes that consist of several
steps.

How to get started?


Getting Started with BOPF guides you through all the steps required to create from scratch
your first business object and to implement a basic operation using the BOPF API.
Read the related blogs posts on SCN:
o Navigating the BOPF: Part 2 - Business Object Overview
o Navigating the BOPF: Part 3 - Working with the BOPF API
o Navigating the BOPF: Part 4 - Advanced BOPF API Features
o Navigating the BOPF: Part 5 - Enhancement Techniques
o Navigating the BOPF: Part 6 - Testing & UI Integration
Navigating the BOPF: Part 2 - Business Object
Overview
Posted by James Wood in ABAP Development on Jan 4, 2013 10:12:04 PM
inShare
In my previous blog post, I briefly introduced the BOPF framework and its positioning within the ABAP
development landscape. With that information in tow, we're now ready to begin peeling back the
layers of the BOPF and seeing how all the pieces fit together from a technical perspective. In this blog
post, we'll get things started by taking a look at the design time aspects of business objects.

Business Objects Overview

According to SAP's BOPF Enhancement Workbench documentation, business objects within the
BOPF are "a representation of a type of uniquely identifiable business entity described by a structural
model and an internal process model." This is to say that BOPF business objects:

Have a well-defined component model.


Have a well-defined process model which governs the business object lifecycle, behaviors, etc.
Execute within a container-like environment which handles low-level tasks such as caching,
transaction management, and so on.

In this regard, BOs in the BOPF are not unlike objects developed in other component architectures
(e.g. EJBs in Java, Microsoft COM+, etc.).

Anatomy of a Business Object

From a modeling perspective, BOs are made up of several different types of entities:

Nodes
o Nodes are used to model a BO's data.
o Nodes are arranged hierarchically to model the various dimensions of the BO data. This
hierarchy is organized underneath a single root node (much like XML). From there, the hierarchy can
be nested arbitrarily deep depending upon business requirements.
o There are several different node types supported by the BOPF. However, most of the
time you'll find yourself working with persistent nodes (e.g. nodes which are backed by the database).
It is also possible to define transient nodes whose contents are loaded on demand at runtime. These
types of nodes can come in handy whenever we want to bridge some alternative persistence model
(e.g. data obtained via service calls).
o Each node consists of one or more attributes which describe the type of data stored
within the node:
Attributes come in two distinct varieties: persistent attributes and transient
attributes. Persistent attributes represent those attributes that will be persisted whenever the BO is
saved. Transient attributes are volatile attributes which are loaded on demand.
A node's attributes are defined in terms of structure definitions from the ABAP
Dictionary.
o At runtime, a BO node is like a container which may have zero, one, or many rows. If
you're familiar with the concept of controller contexts with the Web Dynpro programming model, then
this concept should feel familiar to you. If not, don't worry; we'll demonstrate how this works whenever
we look at the BOPF API.
Actions
o Actions define the services (or behavior) of a BO.
o Actions are assigned to individual nodes within a BO.
o The functionality provided by an action is (usually) defined in terms of an ABAP Objects
class that implements the /BOBF/IF_FRW_ACTION interface.
o To some extent, it is appropriate to think of actions as being similar to the methods of an
ABAP Objects class.
Associations
o Though BOs are designed to be self-contained, autonomous entities, they do not have
to exist in isolation. With associations, we can define a direct and unidirectional relationship from one
BO to another.
o For example, in just a moment, we'll take a look at a sample BO called
/BOBF/DEMO_SALES_ORDER which is used to model sales orders. Here, we'll see how the product
assignments for sales order items is defined in terms of an association with a product BO called
/BOBF/DEMO_PRODUCT. This composition technique makes it possible to not only leverage the product
BOs data model, but also its behaviors, etc.
o Associations allow us to integrate BOs together in complex assemblies la Legos.
Determinations
o According to the aforementioned BOPF enhancement guide, a determination "is an
element assigned to a business object node that describes internal changing business logic on the
business object".
o In some respects, determinations are analogous to database triggers. In other words,
they are functions that are triggered whenever certain triggering conditions are fulfilled. These
conditions are described in terms of a series of patterns:
"Derive dependent data immediately after modification"
This pattern allows us to react to changes made to a given BO node. For
example, we might use this event to go clean up some related data.
"Derive dependent data before saving"
This pattern allows us to hang some custom logic on a given BO node
before it is saved. This could be as simple as using a number range object to assign an ID value to a
node attribute or as complex as triggering an interface.
"Fill transient attributes of persistent nodes"
This pattern is often used in conjunction with UI development. Here, we
might want to load labels and descriptive texts into a series of transient attributes to be displayed on
the screen.
Note: This determination can be bypassed via the API if the lookup
process introduces unnecessary overhead.
"Derive instances of transient nodes"
This pattern allows us to load transient nodes into memory on demand.
Here, for example, we might lookup real-time status data from a Web service and load it into the
attributes of a transient node from downstream consumption.
o Determination patterns are described in detail within the aforementioned BOPF
enhancement guide.
o The logic within a determination is defined via an ABAP Objects class that implements
the /BOBF/IF_FRW_DETERMINATION interface.
Validations
o According to the BOPF enhancement guide, validations are "an element of a business
object node that describes some internal checking business logic on the business object".
o Validations come in two distinct forms:
Action Validations
Action validations are used to determine whether or not a particular action
can be executed against a BO node.
Consistency Validations
As the name suggests, consistency validations are used to ensure that a
BO node is consistent. Such validations are called at pre-defined points within the BOPF BO
transaction cycle to ensure that BO nodes are persisted in a consistent state.
o The validation logic is encapsulated within an ABAP Objects class that implements the
/BOBF/IF_FRW_VALIDATION interface.
Queries
o Queries are BO node entities which allow us to search for BOs using various types of
search criteria.
o Queries make it possible for consumers to access BOs without knowing the BO key up
front.
o Queries also integrate quite nicely with search frameworks and the like.
o Queries come in two varieties:
Node Attribute Queries
Node attribute queries are modeled queries whose logic is defined within
the BOPF runtime. These simple queries can be used whenever you simply need to search for BO
nodes by their attributes (e.g. ID = '12345').
Custom Queries
Custom queries allow you define your own query logic by plugging in an
ABAP Objects class that implements the /BOBF/IF_FRW_QUERY interface.
The figure below illustrates how all of these entities fit together within a BO node definition. Here, I've
pulled up a BO called /BOBF/DEMO_SALES_ORDER in Transaction /BOBF/CONF_UI. Here, the BO
metadata is organized into several different panels:

On the top left-hand side of the screen, you can see the BO's node structure. Here, you can
see that the node structure is organized underneath a top-level ROOT node which models sales
order header data. Underneath this node are several child nodes which model sales order items,
customer assignment, and texts. The ITEM node in turn encompasses its own child nodes to model
item-level data.
On the bottom left-hand side of the screen, we can browse through the node collection of a BO
and view the entity assignments of a given node. As you can see in the figure, each node contains
folders which organize assigned actions, validations, and so on.
In the middle of the screen, we can view additional details about a selected node by double-
clicking on a node within the Node Structure panel on the left-hand side of the screen. Here, we can
look at a node's data model, implementation classes, and so on.
We'll have an opportunity to get a little more hands on with these entities in upcoming blog entries.
For now, our focus is on grasping how pieces fit together and where to go to find the information we
need to get started with a BO.

Next Steps

At this point, you should have a decent feel for how BOs are modeled at design time. In my next blog,
we'll shift gears and begin manipulating BOs using the provided BOPF APIs. This will help put all of
these entities into perspective.

Navigating the BOPF: Part 3 - Working with the


BOPF API
Posted by James Wood in ABAP Development on Jan 16, 2013 11:35:10 PM
inShare
In my previous blog post, we explored the anatomy of business objects within the BOPF. There, we
were able to observe the various entities that make up a BO: nodes/attributes, actions, associations,
determinations, validations, and queries. Now that you have a feel for what these entities are, we're
ready to begin taking a look at the API that is used to manipulate these entities. To guide us through
this demonstration, we'll explore the construction of a simple ABAP report program used to perform
CRUD operations on a sample BOPF BO shipped by default by SAP: /BOBF/DEMO_CUSTOMER. You
can download the complete example program source code here.

Note: The code bundle described above has been enhanced as of 9/18/2013. The code was
reworked to factor out a BOPF utilities class of sorts and also demonstrate how to traverse over to
dependent objects (DOs).
BOPF API Overview
Before we begin coding with the BOPF API, let's first take a look at its basic structure. The UML class
diagram below highlights some of the main classes that make up the BOPF API. At the end of the
day, there are three main objects that we'll be working with to perform most of the operations within
the BOPF:

/BOBF/IF_TRA_TRANSACTION_MGR
o This object reference provides a transaction manager which can be used to manage
transactional changes. Such transactions could contain a single step (e.g. update node X) or be
strung out across multiple steps (add a node, call an action, and so on).
/BOBF/IF_TRA_SERVICE_MANAGER
o The service manager object reference provides us with the methods we need to lookup
BO nodes, update BO nodes, trigger validations, perform actions, and so on.
/BOBF/IF_FRW_CONFIGURATION
o This object reference provides us with metadata for a particular BO. We'll explore the
utility of having access to this metadata coming up shortly.

In the upcoming sections, I'll show you how these various API classes collaborate in typical BOPF
use cases. Along the way, we'll encounter other useful classes that can be used to perform specific
tasks. You can find a complete class listing within package /BOBF/MAIN.

Note: As you'll soon see, the BOPF API is extremely generic in nature. While this provides
tremendous flexibility, it also adds a certain amount of tedium to common tasks. Thus, in many
applications, you may find that SAP has elected to wrap the API up in another API that is more
convenient to work with. For example, in the SAP EHSM solution, SAP defines an "Easy Node
Access" API which simplfies the way that developers traverse BO nodes, perform updates, and so on.

Case Study: Building a Simple Report Program to Manipulate Customer Objects


To demonstrate the BOPF API, we'll build a custom report program which performs basic CRUD
operations on a sample BO provided by SAP: /BOBF/DEMO_CUSTOMER. The figure below shows the
makeup of this BO in Transaction /BOBF/CONF_UI.
Our sample program provides a basic UI as shown below. Here, users have the option of creating,
changing, and displaying a particular customer using its ID number. Sort of a simplified Transaction
XK01-XK03 if you will.

Getting Started
To drive the application functionality, we'll create a local test driver class called LCL_DEMO. As you can
see in the code excerpt below, this test driver class loads the core BOPF API objects at setup
whenever the CONSTRUCTOR method is invoked. Here, the factory classes illustrated in the UML class
diagram shown in the previous section are used to load the various object references.

CLASS lcl_demo DEFINITION CREATE PRIVATE.


PRIVATE SECTION.
DATA mo_txn_mngr TYPE REF TO /bobf/if_tra_transaction_mgr.
DATA mo_svc_mngr TYPE REF TO /bobf/if_tra_service_manager.
DATA mo_bo_conf TYPE REF TO /bobf/if_frw_configuration.

METHODS:
constructor RAISING /bobf/cx_frw.
ENDCLASS.

CLASS lcl_demo IMPLEMENTATION.


METHOD constructor.
"Obtain a reference to the BOPF transaction manager:
me->mo_txn_mngr =
/bobf/cl_tra_trans_mgr_factory=>get_transaction_manager( ).

"Obtain a reference to the BOPF service manager:


me->mo_svc_mngr =
/bobf/cl_tra_serv_mgr_factory=>get_service_manager(
/bobf/if_demo_customer_c=>sc_bo_key ).

"Access the metadata for the /BOBF/DEMO_CUSTOMER BO:


me->mo_bo_conf =
/bobf/cl_frw_factory=>get_configuration(
/bobf/if_demo_customer_c=>sc_bo_key ).
ENDMETHOD. " METHOD constructor
ENDCLASS.

For the most part, this should seem fairly straightforward. However, you might be wondering where I
came up with the IV_BO_KEY parameter in the GET_SERVICE_MANAGER() and GET_CONFIGURATION()
factory method calls. This value is provided to us via the BO's constants interface
(/BOBF/IF_DEMO_CUSTOMER_C in this case) which can be found within the BO configuration in
Transaction /BOBF/CONF_UI (see below). This auto-generated constants interface provides us with
a convenient mechanism for addressing a BO's key, its defined nodes, associations, queries, and so
on. We'll end up using this interface quite a bit during the course of our development.
Creating New Customers
Once we have the basic framework in place, we are ready to commence with the development of the
various CRUD operations that our application will support. To get things started, we'll take a look at
the creation of a new customer instance. For the most part, this involves little more than a call to the
MODIFY() method of the /BOBF/IF_TRA_SERVICE_MANAGER object reference. Of course, as you can see
in the code excerpt below, there is a fair amount of setup that we must do before we can call this
method.

CLASS lcl_demo DEFINITION CREATE PRIVATE.


PUBLIC SECTION.
CLASS-METHODS:
create_customer IMPORTING iv_customer_id
TYPE /bobf/demo_customer_id.
...
ENDCLASS.

CLASS lcl_demo IMPLEMENTATION.


METHOD create_customer.
"Method-Local Data Declarations:
DATA lo_driver TYPE REF TO lcl_demo.
DATA lt_mod TYPE /bobf/t_frw_modification.
DATA lo_change TYPE REF TO /bobf/if_tra_change.
DATA lo_message TYPE REF TO /bobf/if_frw_message.
DATA lv_rejected TYPE boole_d.
DATA lx_bopf_ex TYPE REF TO /bobf/cx_frw.
DATA lv_err_msg TYPE string.

DATA lr_s_root TYPE REF TO /bobf/s_demo_customer_hdr_k.


DATA lr_s_txt TYPE REF TO /bobf/s_demo_short_text_k.
DATA lr_s_txt_hdr TYPE REF TO /bobf/s_demo_longtext_hdr_k.
DATA lr_s_txt_cont TYPE REF TO /bobf/s_demo_longtext_item_k.
FIELD-SYMBOLS:
<ls_mod> LIKE LINE OF lt_mod.

"Use the BOPF API to create a new customer record:


TRY.
"Instantiate the driver class:
CREATE OBJECT lo_driver.

"Build the ROOT node:


CREATE DATA lr_s_root.
lr_s_root->key = /bobf/cl_frw_factory=>get_new_key( ).
lr_s_root->customer_id = iv_customer_id.
lr_s_root->sales_org = 'AMER'.
lr_s_root->cust_curr = 'USD'.
lr_s_root->address_contry = 'US'.
lr_s_root->address = '1234 Any Street'.

APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>.


<ls_mod>-node = /bobf/if_demo_customer_c=>sc_node-root.
<ls_mod>-change_mode = /bobf/if_frw_c=>sc_modify_create.
<ls_mod>-key = lr_s_root->key.
<ls_mod>-data = lr_s_root.

"Build the ROOT_TEXT node:


CREATE DATA lr_s_txt.
lr_s_txt->key = /bobf/cl_frw_factory=>get_new_key( ).
lr_s_txt->text = 'Sample Customer Record'.
lr_s_txt->language = sy-langu.

APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>.


<ls_mod>-node = /bobf/if_demo_customer_c=>sc_node-root_text.
<ls_mod>-change_mode = /bobf/if_frw_c=>sc_modify_create.
<ls_mod>-source_node = /bobf/if_demo_customer_c=>sc_node-root.
<ls_mod>-association =
/bobf/if_demo_customer_c=>sc_association-root-root_text.
<ls_mod>-source_key = lr_s_root->key.
<ls_mod>-key = lr_s_txt->key.
<ls_mod>-data = lr_s_txt.

"Build the ROOT_LONG_TEXT node:


"If you look at the node type for this node, you'll notice that
"it's a "Delegated Node". In other words, it is defined in terms
"of the /BOBF/DEMO_TEXT_COLLECTION business object. The following
"code accounts for this indirection.
CREATE DATA lr_s_txt_hdr.
lr_s_txt_hdr->key = /bobf/cl_frw_factory=>get_new_key( ).

APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>.


<ls_mod>-node = /bobf/if_demo_customer_c=>sc_node-root_long_text.
<ls_mod>-change_mode = /bobf/if_frw_c=>sc_modify_create.
<ls_mod>-source_node = /bobf/if_demo_customer_c=>sc_node-root.
<ls_mod>-association =
/bobf/if_demo_customer_c=>sc_association-root-root_long_text.
<ls_mod>-source_key = lr_s_root->key.
<ls_mod>-key = lr_s_txt_hdr->key.
<ls_mod>-data = lr_s_txt_hdr.

"Create the CONTENT node:


CREATE DATA lr_s_txt_cont.
lr_s_txt_cont->key = /bobf/cl_frw_factory=>get_new_key( ).
lr_s_txt_cont->language = sy-langu.
lr_s_txt_cont->text_type = 'MEMO'.
lr_s_txt_cont->text_content = 'Demo customer created via BOPF API.'.

APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>.


<ls_mod>-node =
lo_driver->mo_bo_conf->query_node(
iv_proxy_node_name = 'ROOT_LONG_TXT.CONTENT' ).
<ls_mod>-change_mode = /bobf/if_frw_c=>sc_modify_create.
<ls_mod>-source_node = /bobf/if_demo_customer_c=>sc_node-root_long_text.
<ls_mod>-source_key = lr_s_txt_hdr->key.
<ls_mod>-key = lr_s_txt_cont->key.
<ls_mod>-data = lr_s_txt_cont.
<ls_mod>-association =
lo_driver->mo_bo_conf->query_assoc(
iv_node_key = /bobf/if_demo_customer_c=>sc_node-root_long_text
iv_assoc_name = 'CONTENT' ).

"Create the customer record:


CALL METHOD lo_driver->mo_svc_mngr->modify
EXPORTING
it_modification = lt_mod
IMPORTING
eo_change = lo_change
eo_message = lo_message.

"Check for errors:


IF lo_message IS BOUND.
IF lo_message->check( ) EQ abap_true.
lo_driver->display_messages( lo_message ).
RETURN.
ENDIF.
ENDIF.

"Apply the transactional changes:


CALL METHOD lo_driver->mo_txn_mngr->save
IMPORTING
eo_message = lo_message
ev_rejected = lv_rejected.

IF lv_rejected EQ abap_true.
lo_driver->display_messages( lo_message ).
RETURN.
ENDIF.

"If we get to here, then the operation was successful:


WRITE: / 'Customer', iv_customer_id, 'created successfully.'.
CATCH /bobf/cx_frw INTO lx_bopf_ex.
lv_err_msg = lx_bopf_ex->get_text( ).
WRITE: / lv_err_msg.
ENDTRY.
ENDMETHOD. " METHOD create_customer
ENDCLASS.

As you can see in the code excerpt above, the majority of the code is devoted to building a table
which is passed in the IT_MODIFICATION parameter of the MODIFY() method. Here, a separate record is
created for each node row that is being modified (or inserted in this case). This record contains
information such as the node object key (NODE), the edit mode (CHANGE_MODE), the row key (KEY)
which is an auto-generated GUID, association/parent key information, and of course, the actual data
(DATA). If you've ever worked with ALE IDocs, then this will probably feel vaguely familiar.

Looking more closely at the population of the node row data, you can see that we're working with data
references which are created dynamically using the CREATE DATA statement. This indirection is
necessary since the BOPF API is generic in nature. You can find the structure definitions for each
node by double-clicking on the node in Transaction /BOBF/CONF_UI and looking at the Combined
Structure field (see below).

Once the modification table is filled out, we can call the MODIFY() method to insert the record(s).
Assuming all is successful, we can then commit the transaction by calling the SAVE() method on the
/BOBF/IF_TRA_TRANSACTION_MANAGER instance. Should any errors occur, we can display the error
messages using methods of the /BOBF/IF_FRW_MESSAGE object reference which is returned from both
methods. This is evidenced by the simple utility method DISPLAY_MESSAGES() shown below. That's
pretty much all there is to it.

CLASS lcl_demo DEFINITION CREATE PRIVATE.


PRIVATE SECTION.
METHODS:
display_messages IMPORTING io_message
TYPE REF TO /bobf/if_frw_message.
ENDCLASS.

CLASS lcl_demo IMPLEMENTATION.


METHOD display_messages.
"Method-Local Data Declarations:
DATA lt_messages TYPE /bobf/t_frw_message_k.
DATA lv_msg_text TYPE string.
FIELD-SYMBOLS <ls_message> LIKE LINE OF lt_messages.
"Sanity check:
CHECK io_message IS BOUND.

"Output each of the messages in the collection:


io_message->get_messages( IMPORTING et_message = lt_messages ).
LOOP AT lt_messages ASSIGNING <ls_message>.
lv_msg_text = <ls_message>-message->get_text( ).
WRITE: / lv_msg_text.
ENDLOOP.
ENDMETHOD. " METHOD display_messages
ENDCLASS.

Performing Customer Queries


If you look closely at the customer creation code illustrated in the previous section, you can see that
each node row is keyed by an auto-generated GUID of type /BOBF/CONF_KEY (see below). Since most
users don't happen to have 32-character hex strings memorized, we typically have to resort to queries
if we want to find particular BO instances. For example, in our customer demo program, we want to
provide a way for users to lookup customers using their customer ID value. Of course, we could have
just as easily defined an alternative query selection to pull the customer records.

As we learned in the previous blog post, most BOs come with one or more queries which allow us to
search for BOs according to various node criteria. In the case of the /BOBF/DEMO_CUSTOMER business
object, we want to use the SELECT_BY_ATTRIBUTES query attached to the ROOT node (see below).
This allows us to lookup customers by their ID value.
The code excerpt below shows how we defined our query in a method called
GET_CUSTOMER_FOR_ID(). As you can see, the query is executed by calling the aptly named QUERY()
method of the /BOBF/IF_TRA_SERVICE_MANAGER instance. The query parameters are provided in the
form of an internal table of type /BOBF/T_FRW_QUERY_SELPARAM. This table type has a similar look
and feel to a range table or SELECT-OPTION. The results of the query are returned in a table of type
/BOBF/T_FRW_KEY. This table contains the keys of the node rows that matched the query parameters.
In our sample case, there should be only one match, so we simply return the first key in the list.

CLASS lcl_demo DEFINITION CREATE PRIVATE.


PRIVATE SECTION.
METHODS:
get_customer_for_id IMPORTING iv_customer_id
TYPE /bobf/demo_customer_id
RETURNING VALUE(rv_customer_key)
TYPE /bobf/conf_key
RAISING /bobf/cx_frw.
ENDCLASS.

CLASS lcl_demo IMPLEMENTATION.


METHOD get_customer_for_id.
"Method-Local Data Declarations:
DATA lo_driver TYPE REF TO lcl_demo.
DATA lt_parameters TYPE /bobf/t_frw_query_selparam.
DATA lt_customer_keys TYPE /bobf/t_frw_key.
DATA lx_bopf_ex TYPE REF TO /bobf/cx_frw.
DATA lv_err_msg TYPE string.
FIELD-SYMBOLS <ls_parameter> LIKE LINE OF lt_parameters.
FIELD-SYMBOLS <ls_customer_key> LIKE LINE OF lt_customer_keys.

"Instantiate the test driver class:


CREATE OBJECT lo_driver.
"Though we could conceivably lookup the customer using an SQL query,
"the preferred method of selection is a BOPF query:
APPEND INITIAL LINE TO lt_parameters ASSIGNING <ls_parameter>.
<ls_parameter>-attribute_name =
/bobf/if_demo_customer_c=>sc_query_attribute-root-select_by_attributes-customer_id.
<ls_parameter>-sign = 'I'.
<ls_parameter>-option = 'EQ'.
<ls_parameter>-low = iv_customer_id.

CALL METHOD lo_driver->mo_svc_mngr->query


EXPORTING
iv_query_key =
/bobf/if_demo_customer_c=>sc_query-root-select_by_attributes
it_selection_parameters = lt_parameters
IMPORTING
et_key = lt_customer_keys.

"Return the matching customer's KEY value:


READ TABLE lt_customer_keys INDEX 1 ASSIGNING <ls_customer_key>.
IF sy-subrc EQ 0.
rv_customer_key = <ls_customer_key>-key.
ENDIF.
ENDMETHOD. " METHOD get_customer_for_id
ENDCLASS.

Displaying Customer Records


With the query logic now in place, we now know which customer record to lookup. The question is,
how do we retrieve it? For this task, we must use the
RETRIEVE() and RETRIEVE_BY_ASSOCIATION() methods provided by the
/BOBF/IF_TRA_SERVICE_MANAGER instance. As simple as this sounds, the devil is in the details. Here,
in addition to constructing the calls to the RETRIEVE*() methods, we must also dynamically define the
result tables which will be used to store the results.

As you can see in the code excerpt below, we begin our search by accessing the customer ROOT
node using the RETRIEVE() method. Here, the heavy lifting is performed by the GET_NODE_ROW() and
GET_NODE_TABLE() helper methods. Looking at the implementation of the GET_NODE_TABLE() method,
you can see how we're using the /BOBF/IF_FRW_CONFIGURATION object reference to lookup the node's
metadata. This metadata provides us with the information we need to construct an internal table to
house the results returned from the RETRIEVE() method. The GET_NODE_ROW() method then
dynamically retrieves the record located at the index defined by the IV_INDEX parameter.

Within the DISPLAY_CUSTOMER() method, we get our hands on the results by performing a cast on the
returned structure reference. From here, we can access the row attributes as per usual.

After the root node has been retrieved, we can traverse to the child nodes of the
/BOBF/DEMO_CUSTOMER object using the RETRIEVE_BY_ASSOCIATION() method. Here, the process is
basically the same. The primary difference is in the way we lookup the association metadata which is
used to build the call to RETRIEVE_BY_ASSOCIATION(). Once again, we perform a cast on the returned
structure reference and display the sub-node attributes from there.

CLASS lcl_demo DEFINITION CREATE PRIVATE.


PUBLIC SECTION.
CLASS-METHODS:
display_customer IMPORTING iv_customer_id
TYPE /bobf/demo_customer_id.

PRIVATE SECTION.
METHODS:
get_node_table IMPORTING iv_key TYPE /bobf/conf_key
iv_node_key TYPE /bobf/obm_node_key
iv_edit_mode TYPE /bobf/conf_edit_mode
DEFAULT /bobf/if_conf_c=>sc_edit_read_only
RETURNING VALUE(rr_data) TYPE REF TO data
RAISING /bobf/cx_frw,

get_node_row IMPORTING iv_key TYPE /bobf/conf_key


iv_node_key TYPE /bobf/obm_node_key
iv_edit_mode TYPE /bobf/conf_edit_mode
DEFAULT /bobf/if_conf_c=>sc_edit_read_only
iv_index TYPE i DEFAULT 1
RETURNING VALUE(rr_data) TYPE REF TO data
RAISING /bobf/cx_frw,

get_node_table_by_assoc IMPORTING iv_key TYPE /bobf/conf_key


iv_node_key TYPE /bobf/obm_node_key
iv_assoc_key TYPE /bobf/obm_assoc_key
iv_edit_mode TYPE /bobf/conf_edit_mode
DEFAULT /bobf/if_conf_c=>sc_edit_read_only
RETURNING VALUE(rr_data) TYPE REF TO data
RAISING /bobf/cx_frw,

get_node_row_by_assoc IMPORTING iv_key TYPE /bobf/conf_key


iv_node_key TYPE /bobf/obm_node_key
iv_assoc_key TYPE /bobf/obm_assoc_key
iv_edit_mode TYPE /bobf/conf_edit_mode
DEFAULT /bobf/if_conf_c=>sc_edit_read_only
iv_index TYPE i DEFAULT 1
RETURNING VALUE(rr_data) TYPE REF TO data
RAISING /bobf/cx_frw.
ENDCLASS.

CLASS lcl_demo IMPLEMENTATION.


METHOD display_customer.
"Method-Local Data Declarations:
DATA lo_driver TYPE REF TO lcl_demo.
DATA lv_customer_key TYPE /bobf/conf_key.
DATA lx_bopf_ex TYPE REF TO /bobf/cx_frw.
DATA lv_err_msg TYPE string.
DATA lr_s_root TYPE REF TO /bobf/s_demo_customer_hdr_k.
DATA lr_s_text TYPE REF TO /bobf/s_demo_short_text_k.

"Try to display the selected customer:


TRY.
"Instantiate the test driver class:
CREATE OBJECT lo_driver.

"Lookup the customer's key attribute using a query:


lv_customer_key = lo_driver->get_customer_for_id( iv_customer_id ).

"Display the header-level details for the customer:


lr_s_root ?=
lo_driver->get_node_row(
iv_key = lv_customer_key
iv_node_key = /bobf/if_demo_customer_c=>sc_node-root
iv_index = 1 ).

WRITE: / 'Display Customer', lr_s_root->customer_id.


ULINE.
WRITE: / 'Sales Organization:', lr_s_root->sales_org.
WRITE: / 'Address:', lr_s_root->address.
SKIP.

"Traverse to the ROOT_TEXT node to display the customer short text:


lr_s_text ?=
lo_driver->get_node_row_by_assoc(

iv_key = lv_customer_key
iv_node_key = /bobf/if_demo_customer_c=>sc_node-root
iv_assoc_key = /bobf/if_demo_customer_c=>sc_association-root-root_text
iv_index = 1 ).
WRITE: / 'Short Text:', lr_s_text->text.
CATCH /bobf/cx_frw INTO lx_bopf_ex.
lv_err_msg = lx_bopf_ex->get_text( ).
WRITE: / lv_err_msg.
ENDTRY.
ENDMETHOD. " METHOD display_customer

METHOD get_node_table.
"Method-Local Data Declarations:
DATA lt_key TYPE /bobf/t_frw_key.
DATA ls_node_conf TYPE /bobf/s_confro_node.
DATA lo_change TYPE REF TO /bobf/if_tra_change.
DATA lo_message TYPE REF TO /bobf/if_frw_message.

FIELD-SYMBOLS <ls_key> LIKE LINE OF lt_key.


FIELD-SYMBOLS <lt_data> TYPE INDEX TABLE.

"Lookup the node's configuration:


CALL METHOD mo_bo_conf->get_node
EXPORTING
iv_node_key = iv_node_key
IMPORTING
es_node = ls_node_conf.

"Use the node configuration metadata to create the result table:


CREATE DATA rr_data TYPE (ls_node_conf-data_table_type).
ASSIGN rr_data->* TO <lt_data>.

"Retrieve the target node:


APPEND INITIAL LINE TO lt_key ASSIGNING <ls_key>.
<ls_key>-key = iv_key.

CALL METHOD mo_svc_mngr->retrieve


EXPORTING
iv_node_key = iv_node_key
it_key = lt_key
IMPORTING
eo_message = lo_message
eo_change = lo_change
et_data = <lt_data>.
"Check the results:
IF lo_message IS BOUND.
IF lo_message->check( ) EQ abap_true.
display_messages( lo_message ).
RAISE EXCEPTION TYPE /bobf/cx_dac.
ENDIF.
ENDIF.
ENDMETHOD. " METHOD get_node_table

METHOD get_node_row.
"Method-Local Data Declarations:
DATA lr_t_data TYPE REF TO data.

FIELD-SYMBOLS <lt_data> TYPE INDEX TABLE.


FIELD-SYMBOLS <ls_row> TYPE ANY.

"Lookup the node data:


lr_t_data =
get_node_table( iv_key = iv_key
iv_node_key = iv_node_key
iv_edit_mode = iv_edit_mode ).

IF lr_t_data IS NOT BOUND.


RAISE EXCEPTION TYPE /bobf/cx_dac.
ENDIF.

"Try to pull the record at the specified index:


ASSIGN lr_t_data->* TO <lt_data>.
READ TABLE <lt_data> INDEX iv_index ASSIGNING <ls_row>.
IF sy-subrc EQ 0.
GET REFERENCE OF <ls_row> INTO rr_data.
ELSE.
RAISE EXCEPTION TYPE /bobf/cx_dac.
ENDIF.
ENDMETHOD. " METHOD get_node_row

METHOD get_node_table_by_assoc.
"Method-Local Data Declarations:
DATA lt_key TYPE /bobf/t_frw_key.
DATA ls_node_conf TYPE /bobf/s_confro_node.
DATA ls_association TYPE /bobf/s_confro_assoc.
DATA lo_change TYPE REF TO /bobf/if_tra_change.
DATA lo_message TYPE REF TO /bobf/if_frw_message.

FIELD-SYMBOLS <ls_key> LIKE LINE OF lt_key.


FIELD-SYMBOLS <lt_data> TYPE INDEX TABLE.

"Lookup the association metadata to find out more


"information about the target sub-node:
CALL METHOD mo_bo_conf->get_assoc
EXPORTING
iv_assoc_key = iv_assoc_key
iv_node_key = iv_node_key
IMPORTING
es_assoc = ls_association.

IF ls_association-target_node IS NOT BOUND.


RAISE EXCEPTION TYPE /bobf/cx_dac.
ENDIF.
"Use the node configuration metadata to create the result table:
ls_node_conf = ls_association-target_node->*.

CREATE DATA rr_data TYPE (ls_node_conf-data_table_type).


ASSIGN rr_data->* TO <lt_data>.

"Retrieve the target node:


APPEND INITIAL LINE TO lt_key ASSIGNING <ls_key>.
<ls_key>-key = iv_key.

CALL METHOD mo_svc_mngr->retrieve_by_association


EXPORTING
iv_node_key = iv_node_key
it_key = lt_key
iv_association = iv_assoc_key
iv_fill_data = abap_true
IMPORTING
eo_message = lo_message
eo_change = lo_change
et_data = <lt_data>.

"Check the results:


IF lo_message IS BOUND.
IF lo_message->check( ) EQ abap_true.
display_messages( lo_message ).
RAISE EXCEPTION TYPE /bobf/cx_dac.
ENDIF.
ENDIF.
ENDMETHOD. " METHOD get_node_table_by_assoc

METHOD get_node_row_by_assoc.
"Method-Local Data Declarations:
DATA lr_t_data TYPE REF TO data.

FIELD-SYMBOLS <lt_data> TYPE INDEX TABLE.


FIELD-SYMBOLS <ls_row> TYPE ANY.

"Lookup the node data:


lr_t_data =
get_node_table_by_assoc( iv_key = iv_key
iv_node_key = iv_node_key
iv_assoc_key = iv_assoc_key
iv_edit_mode = iv_edit_mode ).

IF lr_t_data IS NOT BOUND.


RAISE EXCEPTION TYPE /bobf/cx_dac.
ENDIF.

"Try to pull the record at the specified index:


ASSIGN lr_t_data->* TO <lt_data>.
READ TABLE <lt_data> INDEX iv_index ASSIGNING <ls_row>.
IF sy-subrc EQ 0.
GET REFERENCE OF <ls_row> INTO rr_data.
ELSE.
RAISE EXCEPTION TYPE /bobf/cx_dac.
ENDIF.
ENDMETHOD. " METHOD get_node_row_by_assoc
ENDCLASS.
Note: In this simple example, we didn't bother to drill down to display the contents of the
ROOT_LONG_TEXT node. However, if we had wanted to do so, we would have needed to create a
separate service manager instance for the /BOBF/DEMO_TEXT_COLLECTION business object since the
data within that node is defined by that delegated BO as opposed to the /BOBF/DEMO_CUSTOMER BO.
Otherwise, the process is the same.

Modifying Customer Records


The process of modifying a customer record essentially combines logic from the display and create
functions. The basic process is as follows:

1. First, we perform a query to find the target customer record.


2. Next, we use the RETRIEVE*() methods to retrieve the node rows we wish to modify. Using the
returned structure references, we can modify the target attributes using simple assignment
statements.
3. Finally, we collect the node row changes into the modification table that is fed into MODIFY()
method provided by the /BOBF/IF_TRA_SERVICE_MANAGER instance.

The code excerpt below shows how the changes are carried out. Here, we're simply updating the
address string on the customer. Of course, we could have performed wholesale changes if we had
wanted to.

CLASS lcl_demo DEFINITION CREATE PRIVATE.


PUBLIC SECTION.
CLASS-METHODS:
change_customer IMPORTING iv_customer_id
TYPE /bobf/demo_customer_id.
ENDCLASS.

CLASS lcl_demo IMPLEMENTATION.


METHOD change_customer.
"Method-Local Data Declarations:
DATA lo_driver TYPE REF TO lcl_demo.
DATA lv_customer_key TYPE /bobf/conf_key.
DATA lt_mod TYPE /bobf/t_frw_modification.
DATA lo_change TYPE REF TO /bobf/if_tra_change.
DATA lo_message TYPE REF TO /bobf/if_frw_message.
DATA lv_rejected TYPE boole_d.
DATA lx_bopf_ex TYPE REF TO /bobf/cx_frw.
DATA lv_err_msg TYPE string.

FIELD-SYMBOLS:
<ls_mod> LIKE LINE OF lt_mod.

DATA lr_s_root TYPE REF TO /bobf/s_demo_customer_hdr_k.

"Try to change the address on the selected customer:


TRY.
"Instantiate the test driver class:
CREATE OBJECT lo_driver.

"Access the customer ROOT node:


lv_customer_key = lo_driver->get_customer_for_id( iv_customer_id ).
lr_s_root ?=
lo_driver->get_node_row( iv_key = lv_customer_key
iv_node_key = /bobf/if_demo_customer_c=>sc_node-root
iv_edit_mode = /bobf/if_conf_c=>sc_edit_exclusive
iv_index = 1 ).

"Change the address string on the customer:


lr_s_root->address = '1234 Boardwalk Ave.'.

APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>.


<ls_mod>-node = /bobf/if_demo_customer_c=>sc_node-root.
<ls_mod>-change_mode = /bobf/if_frw_c=>sc_modify_update.
<ls_mod>-key = lr_s_root->key.
<ls_mod>-data = lr_s_root.

"Update the customer record:


CALL METHOD lo_driver->mo_svc_mngr->modify
EXPORTING
it_modification = lt_mod
IMPORTING
eo_change = lo_change
eo_message = lo_message.

"Check for errors:


IF lo_message IS BOUND.
IF lo_message->check( ) EQ abap_true.
lo_driver->display_messages( lo_message ).
RETURN.
ENDIF.
ENDIF.

"Apply the transactional changes:


CALL METHOD lo_driver->mo_txn_mngr->save
IMPORTING
eo_message = lo_message
ev_rejected = lv_rejected.

IF lv_rejected EQ abap_true.
lo_driver->display_messages( lo_message ).
RETURN.
ENDIF.

"If we get to here, then the operation was successful:


WRITE: / 'Customer', iv_customer_id, 'updated successfully.'.
CATCH /bobf/cx_frw INTO lx_bopf_ex.
lv_err_msg = lx_bopf_ex->get_text( ).
WRITE: / lv_err_msg.
ENDTRY.
ENDMETHOD. " METHOD change_customer
ENDCLASS.

Next Steps
I often find that the best way to learn a technology framework is to see how it plays out via code. At
this level, we can clearly visualize the relationships between the various entities and see how they
perform at runtime. Hopefully after reading this post, you should have a better understanding of how
all the BOPF pieces fit together. In my next blog post, we'll expand upon what we've learned and
consider some more advanced features of the BOPF API.

Navigating the BOPF: Part 4 - Advanced BOPF


API Features
Posted by James Wood in ABAP Development on Jan 30, 2013 12:16:32 AM
inShare
In my previous blog post, I introduced the BOPF API and demonstrated how the API could be used to
perform routine CRUD operations on a business object. With this basic introduction out of the way,
we're now ready to tackle more advanced API features such as consistency checks, the execution of
actions, and transaction management. So, without further ado, let's get started.

Performing Consistency Checks & Validations


In keeping with the object-oriented paradigm, business objects (BOs) are designed to combine
business data and business functions together into one tidy capsule (hence the term encapsulation).
One of the primary benefits of combining these entities is to ensure that updates to business data are
reliably filtered through a set of business rules. To put this concept into perspective, imagine a BO
which defines a header-level status field (e.g. 01 = Initial, 02 = In Process, 03 = Closed). Now, from a
pure data perspective, there's nothing stopping us from updating the status field using the MODIFY()
method of the /BOBF/IF_TRA_SERVICE_MANAGER interface (or heck, even via an SQL UPDATE
statement). However, from a business perspective, there are probably some rules which define when,
where, and how we should change the status field. For example, it might be that the BO cannot be
closed until any open line items are closed out, etc.

Whatever the business rules might be, the point is that we want to ensure that a BO is consistent
throughout each checkpoint in its object lifecycle. As we learned in part 2 of this blog series, the
BOPF allows us to define these consistency checks in the form of validations. For example, in the
screenshot below, you can see how SAP has created a validation called CHECK_ROOT for the ROOT
node of the /BOBF/DEMO_SALES_ORDER demo BO. This validation is used to perform a consistency
check on the sales order header-level fields to make sure that they are valid before an update is
committed to the database.

One of the nice things about validations like CHECK_ROOT is that they are automatically called by the
BOPF framework at specific points within the transaction lifecycle. However, sometimes we might
want to trigger such validations interactively. For example, when building a UI on top of a BO, we
might want to provide a check function which validates user input before they save their changes.
This is demonstrated in the /BOBF/DEMO_SALES_ORDER Web Dynpro ABAP application shown below.

From a code perspective, the heavy lifting for the check operation is driven by the
CHECK_CONSISTENCY() method of the /BOBF/IF_TRA_SERVICE_MANAGER interface as shown in the code
excerpt below. Here, we simply provide the service manager with the target node key and the BO
instance key and the framework will take care of calling the various validations on our behalf. We can
then check the results of the validation by looking at the /BOBF/IF_FRW_MESSAGE instance which was
introduced in the previous blog.

DATA lt_key TYPE /bobf/t_frw_key.


FIELD-SYMBOLS <ls_key> LIKE LINE OF lt_key.
DATA lo_message TYPE REF TO /bobf/if_frw_message.

TRY.
APPEND INITIAL LINE TO lt_key ASSIGNING <ls_key>.
<ls_key>-key = iv_key. "<== Sales Order BO Key

CALL METHOD mo_svc_mngr->check_consistency


EXPORTING
iv_node_key = /bobf/if_demo_sales_order_c=>sc_node-root
it_key = lt_key
iv_check_scope = '1'
IMPORTING
eo_message = lo_message.
...
CATCH /bobf/cx_frw INTO lx_frw.
...
ENDTRY.

I'll show you how to implement validations within a BO in an upcoming blog entry.
Triggering Actions
The behaviors of a business object within the BOPF are defined as actions. From a conceptual point-
of-view, actions are analogous to methods/functions in the object-oriented paradigm. The following
code excerpt demonstrates how actions are called using the BOPF API. Here, we're calling the
DELIVER action defined in the ROOT node of the /BOBF/DEMO_SALES_ORDER demo BO. As you can
see, the code reads like a dynamic function/method call since we have generically pass the name of
the action along with its parameters to the DO_ACTION() method of the
/BOBF/IF_TRA_SERVICE_MANAGER interface. Other than that, it's pretty much business as usual.

DATA lt_key TYPE /bobf/t_frw_key.


FIELD-SYMBOLS <ls_key> LIKE LINE OF lt_key.
DATA ls_parameters TYPE /bobf/s_demo_sales_order_hdr_d.
DATA lr_s_parameters TYPE REF TO data.
DATA lo_change TYPE REF TO /bobf/if_tra_change.
DATA lo_message TYPE REF TO /bobf/if_frw_message.
DATA lt_failed_key TYPE /bobf/t_frw_key.
DATA lt_failed_act_key TYPE /bobf/t_frw_key.

TRY.
"Set the BO instance key:
APPEND INITIAL LINE TO lt_key ASSIGNING <ls_key>.
<ls_key>-key = iv_key. "<== Sales Order BO Key

"Populate the parameter structure that contains parameters passed


"to the action:
ls_parameters-item_no = '001'.
GET REFERENCE OF ls_parameters INTO lr_s_parameters.

"Call the DELIVER action defined on the ROOT node:


CALL METHOD mo_svc_mngr->do_action
EXPORTING
iv_act_key = /bobf/if_demo_sales_order_c=>sc_action-root-deliver
it_key = lt_key
is_parameters = lr_s_parameters
IMPORTING
eo_change = lo_change
eo_message = lo_message
et_failed_key = lt_failed_key
et_failed_action_key = lt_failed_act_key.

...
CATCH /bobf/cx_frw INTO lx_frw.
...
ENDTRY.

We can verify if a BOPF action call was successful by querying the EO_MESSAGE object reference
and/or the ET_FAILED_KEY table parameters returned by the DO_ACTION() method. Refer back to my
previous blog for an example of the former technique. As always, remember to commit the
transaction using the
SAVE() method defined by the /BOBF/IF_TRA_TRANSACTION_MGR interface.

Action Validations
In part two of this blog series, I mentioned that there are technically two different types of validations:
the consistency check validations discussed earlier in this blog and action validations which are used
to determine whether or not an action can be executed at runtime. Since the BOPF framework
invokes action validations automatically whenever actions are invoked, it is rare that you will have a
need to invoke them directly. However, the /BOBF/IF_TRA_SERVICE_MANAGER interface does provide
the DO_ACTION() method if you wish to do so (see the code excerpt below).

DATA lt_key TYPE /bobf/t_frw_key.


FIELD-SYMBOLS <ls_key> LIKE LINE OF lt_key.
DATA ls_parameters TYPE /bobf/s_demo_sales_order_hdr_d.
DATA lr_s_parameters TYPE REF TO data.
DATA lo_message TYPE REF TO /bobf/if_frw_message.
DATA lt_failed_key TYPE /bobf/t_frw_key.
DATA lt_failed_act_key TYPE /bobf/t_frw_key.

TRY.
"Set the BO instance key:
APPEND INITIAL LINE TO lt_key ASSIGNING <ls_key>.
<ls_key>-key = iv_key. "<== Sales Order BO Key

"Populate the parameter structure that contains parameters passed


"to the action:
ls_parameters-item_no = '001'.
GET REFERENCE OF ls_parameters INTO lr_s_parameters.

"Check to see if the DELIVER action can be invoked:


CALL METHOD mo_svc_mngr->check_action
EXPORTING
iv_act_key = /bobf/if_demo_sales_order_c=>sc_action-root-deliver
it_key = lt_key
is_parameters = lr_s_parameters
IMPORTING
eo_message = lo_message
et_failed_key = lt_failed_key
et_failed_action_key = lt_failed_act_key.

...
CATCH /bobf/cx_frw INTO lx_frw.
...
ENDTRY.

Transaction Management
Another element of the BOPF API that we have glossed over up to now is the transaction manager
interface /BOBF/IF_TRA_TRANSACTION_MGR. This interface provides us with a simplified access point
into a highly sophisticated transaction management framework. While the details of this framework
are beyond the scope of this blog series, suffice it to say that the BOPF transaction manager does
more here than simply provide basic object-relational persistence. It also handles caching,
transactional locking, and more. You can see how some of these features are implemented by
looking at the Transactional Behavior settings of a business object definition in Transaction
/BOBF/CONF_UI (see below).
So far, we have seen a bit of the /BOBF/IF_TRA_TRANSACTION_MGR on display whenever we looked at
how to insert/update records. Here, as you may recall, we used SAVE() method of the
/BOBF/IF_TRA_TRANSACTION_MGR interface to save these records. In many respects, the SAVE()
method is analogous to the COMMIT WORK statement in ABAP in that it commits the transactional
changes to the database. Here, as is the case with the COMMIT WORK statement, we could be
committing multiple updates as one logical unit of work (LUW) - e.g. an insert followed by a series of
updates.

Once a transaction is committed, we can reset the transaction manager by calling the CLEANUP()
method. Or, alternatively, we can also use this method to abandon an in-flight transaction once an
error condition has been detected. In the latter case, this is analogous to using the ROLLBACK WORK
statement in ABAP to rollback a transaction.

During the course of a transaction, the BOPF transaction manager tracks the changes that are made
to individual business objects internally so that it can determine what needs to be committed and/or
rolled back. If desired, we can get a peek of the queued up changes by calling the
GET_TRANSACTIONAL_CHANGES() method of the /BOBF/IF_TRA_TRANSACTION_MGR interface. This
method will return an object reference of type /BOBF/IF_TRA_CHANGE that can be used to query the
change list, modify it in certain cases, and so on.

Next Steps
At this point, we have hit on most of the high points when it comes to interacting with the BOPF API
from a client perspective. In my next blog, we'll shift gears and begin looking at ways of enhancing
BOs using the BOPF toolset.

Navigating the BOPF: Part 5 - Enhancement


Techniques
Posted by James Wood in ABAP Development on Feb 22, 2013 11:32:31 PM
inShare
In my previous two blog posts, we explored the BOPF API from a client point-of-view. Here, we
learned how to perform basic CRUD operations, execute actions, and so on. Now that you have a
feel for how the API operates, we're ready to take a peek behind the curtain and see these services
are implemented within the business objects themselves. For now, our emphasis will be on
enhancing these services since SAP does not yet support the creation of new business objects.
However, whether we're enhancing existing business objects or creating new ones from scratch, the
concepts remain the same.

What to Enhance?
Before we dive into the exploration of specific enhancement techniques, let's first take a look at the
kinds of entities we're allowed to enhance in a business object. Aside from implicit enhancements
applied to implementation classes using the Enhancement Framework, the types of entities that we
can enhance within a business object are as follows:

Custom Attributes
o For a given node, we might want to define a handful of additional custom
attributes.These attributes could be persistent (i.e., they get appended to the target database table
which contains the node data) or transient in nature.
New Sub-Nodes
o In some cases, we may need to do more than simply define a few new attributes on an
existing node. Using the relational data model as our guide, we may determine that a new sub-node
is needed to properly model some new dimension of data (e.g. 1-to-many relations, etc.). Depending
on the requirement, the sub-node(s) might be persistent or transient in nature.
Determinations
o If we add new custom attributes to a given node, it stands to reason that we might also
want to create a custom determination to manage these attributes.
o Or, we might have a standalone requirement which calls for some sort of "trigger" to be
fired whenever a specific event occurs (e.g. fire an event to spawn a workflow, etc.).
Consistency Validations
o If we are enhancing the data model of a business object, we might want to define a
consistency validation to ensure that the new data points remain consistent.
o A custom validation might also be used to graft in a new set of business rules or a
custom security model.
Actions
o If we have certain operations which need to be performed on a business object, we
would prefer to encapsulate those operations as an action on the business object as opposed to
some standalone function module or class.
Queries
o In some cases, the set of defined queries for a business object might not be sufficient
for our needs. In these situations, we might want to define custom queries to encapsulate the
selection logic so that we can use the generic query services of the BOPF API as opposed to some
custom selection method.
You can find a detailed treatment of supported enhancement options in the BOPF Enhancement
Workbench Help documentation which is provided as a separate download in SAP Note #1457235.
This document provides a wealth of information concerning the use of the BOPF Enhancement
Workbench, enhancement strategies, and even the BOPF framework in general. Given the amount of
detail provided there, I won't attempt to re-invent the wheel in this blog post. Instead, I'll simply hit on
the high points and leave the nitty-gritty details to the help documentation.

Working with the Enhancement Workbench


When enhancing a business object, you'll be spending quite a bit of time with the BOPF
Enhancement Workbench which can be accessed using Transaction BOPF_EWB. Here,
enhancement projects are organized into enhancement objects. From a conceptual point-of-view,
enhancement objects bear a lot of similarities to sub-classes in the object-oriented programming
(OOP) paradigm. This is to say that enhancement objects inherit all of the entities of their parent BO.
With this foundation in place, we can begin defining custom entities in much the same way we might
add new attributes/methods to a subclass in the ABAP Class Builder tool. However, as is the case
with classes in the OOP world, we cannot extend BOs which are marked as final or that do not have
the "Business Object can be enhanced" flag set (see below).
All of the BOs which are eligible for enhancement will show up in the Enhancement Browser
perspective of the BOPF Enhancement Workbench shown below. To create an enhancement, simply
right-click on the BO that you wish to enhance and select the Create Enhancement menu option (see
below). From here, the BOPF Enhancement Workbench will guide you through a wizard process
which allows you to select the name of the enhancement object, the constants interface for the
enhancement object, and so on.

Once the enhancement is created, you will be able to edit your enhancement object in the workbench
perspective of the BOPF Enhancement Workbench shown below. As you can see, it has a similar
look-and-feel to that of the normal BO browser tool (Transaction /BOBF/CONF_UI). From here, we
can begin adding custom entities by right-clicking on the target node and selecting from the available
menu options. We'll see how this works in the upcoming sections.
One final item I would draw your attention to with enhancement objects is the assigned constants
interface (highlighted above). This constants interface can be used to access the enhancement object
entities in the same way that the super BO's constants interface is used for BOPF API calls, etc.

Enhancing the BO Data Model


Perhaps the most common type of enhancement to BOs in the BOPF is the addition of new fields.
Here, we have the option of adding new fields to existing nodes or creating sub-nodes to model more
complex relationships. In the former case, we sometimes don't even need to create an enhancement
object; just a simple append structure will suffice (see below).

For more complex data requirements, we typically need to define sub-nodes. This can be achieved by
right-clicking on the parent node and selecting the Create Subnode menu option. This kicks off a
wizard process in which you can select the sub-node's name, its persistent and/or transient
structures, and the rest of the auto-generated dictionary types which go along with a node definition
(e.g. combined structure/table type, database table, etc.). Most of this is pretty standard stuff, but I
would draw your attention to the step which creates the persistent and/or transient structures. Note
that these structures must exist in the database before you move on from the Attributes step in the
wizard process. And, in the case of the persistent structure, you must include the /BOBF/S_ADMIN
structure as the first component.
After the custom sub-node is created, you can fill out its attributes by adding components to the
persistent/transient structures defined by the sub-node. If the sub-node is a persistent node, then we
can create, modify, and retrieve node instances using the BOPF API as per usual. However, in the
case of transient nodes, we need determinations to pre-fetch the data for us. We'll see how to define
such determinations next.

Defining Determinations
According to the help documentation, determinations encapsulate internal changing business logic on
a business object. Unlike the logic encapsulated in actions which can be triggered at any time, the
business logic contained within determinations is triggered as specific times within the BO life cycle
(e.g. right before a node is saved, etc.). So, in a way, it is appropriate to think of determinations as
being a little bit like user exits/BAdIs/enhancement spots in that they provide a place to hang custom
logic at particular points within the process flow.

Once we determine (no pun intended) that we want to create a determination for a given node, we
can do so by simply right-clicking on that node and selecting the Create Determination menu option.
This will spawn a wizard which guides us through the process. Here, there are two main properties
that we must account for:

1. Implementing Class:
o We must create or assign an ABAP Objects class that implements the
/BOBF/IF_FRW_DETERMINATION interface.
2. Determination Pattern:
o This property defines the event which triggers the determination. As you can see below,
the set of available patterns will vary depending on the type of node you're enhancing, its location in
the node hierarchy, and so on.
o Once a pattern is selected, you may be presented with additional options for refining
when an event is triggered. For example, if we select the pattern "Derive dependent data immediately
after modification", we will have the opportunity to specify if the dependent data should be
created/modified after any modification, only when the node is created the first time, etc.
Because determinations can be used for a lot of different things, they can be implemented in a lot of
different ways. Here, it is very important that you pay close attention to selecting the right pattern for
the right job. The aforementioned help documentation provides a good set of guidelines to assist
here. Other valuable resources include the interface documentation for the
/BOBF/IF_FRW_DETERMINATION interface in the Class Builder tool and SAP standard-delivered
determinations implementations available in the system you're working on.

Defining Consistency Validations


The process of defining a custom consistency validation is quite similar to the one used to define
determinations. Walking through the wizard process, there are three main properties that we must
account for:

1. Implementing Class:
o Here, we must create/assign an ABAP Objects class which implements the
/BOBF/IF_FRW_VALIDATION interface.
2. Request Nodes:
o This property allows us to specify which node operations should force a validation to
occur (e.g. during creates, updates, etc.)
3. Impact:
o With this property, we can specify the behavior of the BOPF framework in cases where
the validation fails. For example, should we simply return an error message, prevent the requested
operation from proceeding, or both?

From an implementation perspective, the /BOBF/IF_FRW_VALIDATION interface provides us with


everything we need to perform the validation check: the context of the validation, the current data
within the node instance being validated, and so on. For more information about how to implement
the validation class, I would highly recommend that you read through the interface documentation for
the /BOBF/IF_FRW_VALIDATION interface in the Class Builder tool. It can also be helpful to look at
various standard-delivered classes which already implement this interface to see common
patterns/idioms used by SAP.
Working with Actions
When it comes to the customization of actions, we have a couple of options:

We can create a brand new action definition for a given node (standard or custom).
We can enhance existing actions with pre/post action enhancements.

The first case is pretty straightforward. Basically, we simply follow along with the wizard process up to
the point that we reach the Settings step shown below. Here, we must define three main properties
for the action:

Implementing Class:
o This property is used to specify the ABAP Objects class which encapsulates the action
logic. The class must implement the /BOBF/IF_FRW_ACTION interface.
Action Cardinality:
o The action cardinality property defines the scope of the action. This is somewhat
analogous to the way we have the option of defining class methods or instance methods within a
regular ABAP Objects class. In this case however, we also have the third option of defining a sort of
"mass-processing" action which works on multiple node instances at once.
Parameter Structure:
o If we wish to pass parameters to the action, we can plug in an ABAP Dictionary
structure here to encapsulate the parameters.

Once the action is created, we simply need to plug in the relevant logic in the defined implementation
class. You can find implementation details for this in the interface documentation and/or sample
action classes in the system.

In order to create a pre/post action enhancement, the target action definition in the super BO must
have its "Action Can Be Enhanced" flag set (see below). Assuming that the flag is set, then we can
proceed through the corresponding wizard process in much the same way we would if we were
creating a custom action from scratch. Indeed, as is the case with regular actions, the implementation
class(es) for pre/post action enhancements must implement the /BOBF/IF_FRW_ACTION interface.

Before you go to implement a pre/post action enhancement, I would definitely recommend that you
read through the help documentation so that you understand what you can and cannot do within an
action enhancement. Most of the rules are intuitive, but you can definitely get into trouble if you abuse
these enhancements by using them for things they weren't designed for.

Defining Custom Queries


Compared to the various enhancement options we've seen thus far, custom queries are perhaps the
easiest entities to create within an enhancement object. Indeed, if all we want is a simple node
attribute query, we can zip through the wizard and have a working model up and running in a matter
of minutes. If we want something a little more custom/sophisticated, our job is only marginally more
difficult (at least from a configuration perspective) in that we must assign an implementing class and
an optional data type which serves as the parameter structure passed into the query from the client
side (see below).
From an implementation perspective, all of the query logic for a custom query gets encapsulated in
the implementation class (which must implement the /BOBF/IF_FRW_QUERY interface). For the most
part, you'll find that the framework doesn't really get in the way with regards to how we go about
implementing the query. Basically, it passes in the query parameters up front and it's up to us to
figure out how to find all of the node instances which match the given parameters. Here, we must pay
careful attention to the SQL statements that we use since the query may be used extensively by a
number of different clients.

Next Steps
Hopefully by now you have a general feel for how BOs are enhanced and the basic steps required to
achieve these enhancements. As is the case with most programming-related subjects, the best way
to really drive these concepts home is to look at live examples and experiment for yourself. I would
also highly recommend that you read through the aforementioned help documentation as it devotes
quite a bit of time to understanding when and where to apply specific enhancement techniques.

In my next and final blog post in this series, I'll demonstrate another useful tool within the BOPF
toolset: the BO test tool. This tool can be used to experiment with BOs and perform ad hoc unit tests,
etc.

Navigating the BOPF: Part 6 - Testing & UI


Integration
Posted by James Wood in ABAP Development on Mar 4, 2013 10:59:35 PM
inShare
In my previous blog post, we looked at ways of enhancing business objects (BOs) defined within the
BOPF. Once these enhancements are in place, it is a good idea to unit test the changes to make sure
that the BO is working correctly. If you subscribe to the test-driven development (TDD) model, then
an obvious choice here would be to use the BOPF API to write some ABAP Unit test cases. However,
sometimes we just want to run a quick ad hoc test. Or, we might just want to display BO data at a
glance without constructing a multi-table join in Transaction SQVI. For these tasks and others, SAP
provides us with a very slick tool: the BOPF Test UI. In this final blog post, I will introduce this
transaction and demonstrate its basic capabilities. Then, to round things out, I'll briefly touch on UI
integration and the FBI framework.

Working with the BOPF Test UI


We can access the BOPF Test UI by opening up Transaction /BOBF/TEST_UI. When you initially
open up the tool, you'll be presented with a screen like the one shown below. From here, we can
begin working with a BO instance by plugging in the BO type in the Select BO input field (either key it
in or use the provided input help) and hitting the ENTER key. This causes the BO metadata to be
loaded into context so that we can use it to guide ourselves through the editing process.

Editing BO Instances
Once the BO metadata is loaded, we have two choices for maintenance:

1. To create a new BO instance, we can double-click on the root node contained in the "Metadata
and Instances" tree on the left-hand side of the editor screen and then select the Create button in the
toolbar (see below). This will cause a new record to be created and loaded into an editable ALV grid.
From here, we can begin filling in node attributes, creating sub-node instances, and so on. Here, I
would draw your attention to the Messages panel located in the bottom left-hand corner of the editor.
These messages can be used to help you fill in the right data.

2. If the BO instance that we want to maintain/display exists already, then we can load it into
context using the Load Instances button menu. As you can see in the screenshot below, this menu
affords us with several different alternatives for loading node instances: via a BOPF node query, by
the node instance key, or by an alternative key (e.g. ID). Regardless of the menu path that we take,
the system will attempt to find the target node instance(s) and then load them into the editor window.
From here, we can select individual node instances by double-clicking on them in the Metadata and
Instances tree located on the left-hand side of the screen.

To edit node instances, we can select the node instance record in the editor on the right-hand side of
the screen and choose the appropriate option from the Edit button menu (see below). Then, we can
edit attributes for a node instance using the provided input fields. Alternatively, we also have the
option of deleting a node instance (or indeed an entire BO instance in the case of a root node
instance) by clicking on the Delete Node Instances button.

Regardless of whether or not we're creating a new BO instance or editing an existing one, the entire
scope of our changes is tracked via a BOPF transaction like the one we would create if we were
doing all this by hand using the BOPF API. At any point along the way, we can choose to commit the
changes using the Save Transaction button, or revert the changes using the Cleanup Transaction
button. Then, we can start the process over by selecting another BO instance or editing the existing
one in place. All in all, it's kind of like table maintenance on steroids. But wait, there's more!

Triggering Actions, Validations, & Determinations


In addition to the basic CRUD operations described earlier, the test UI also provides functions to call
actions, validations, and even trigger determinations. For a given node instance, these functions can
be accessed in the node instance toolbar via the Check and Actions button menus (see below). If you
read through my blog posts related to the BOPF API, then these should feel quite

intuitive.
UI Integration and the FBI Framework
Since the focus of this blog series has been primarily on introducing the BOPF framework, I have
purposefully avoided digressing into specific applications of the BOPF (e.g. in Transportation
Management or EHSM) since these products add additional layers on top of the BOPF that can sort
of cloud the picture a bit if you don't understand core principles of the BOPF itself. However, before I
bring this blog series to a close, I would be remiss if I didn't point out one important (and relatively
generic) framework built on top of the BOPF: the Floorplan Manager BOPF Integration (FBI)
framework. As the name suggests, this framework links BOs from the BOPF with Web UIs based on
the Floorplan Manager (FPM) framework and Web Dynpro ABAP (WDA).

If you're developing Web UIs on top of BOs from the BOPF, then the FBI is definitely something to
take a look at. Essentially, the FBI exploits the genericity of the BOPF API and the accessibility of BO
model data to enable the rapid development of Generic User Interface Building Blocks (GUIBBs)
based on BO nodes. Here, for example, we could create a form GUIBB that allows users to populate
the data for a BO node using a simple input form. In many applications, this can be achieved without
having to write a single line of code. While a detailed discussion of the FBI is beyond the scope of this
blog series, a quick Google search will lead you to some pretty decent resource materials. If you're
new to FPM, I would also offer a shameless plug for my book Web Dynpro ABAP: The
Comprehensive Guide (SAP PRESS, 2012).
Conclusion
When I first started working with the BOPF almost a year ago, I was surprised at how little
documentation there was to get started with. So, what you've seen in this series is the result of a lot of
trial-and-error and lessons learned by debugging past application-specific frameworks into the heart
of the BOPF itself. If you're just getting started with the BOPF, then I hope that you'll find this series
useful to get you up and running. In the coming months and years, I think many more learning
resources will materialize to supplement what I've offered here. Indeed, the number of new dimension
applications based on the BOPF appears to be growing by the day...

One complaint I sometimes hear from other developers is that the BOPF API is cumbersome to work
with. On this point, I can agree to a point. However, I would argue that such complexities can be
abstracted away pretty easily with a wrapper class or two and some good old fashioned RTTI code.
Other than that, once you get used to the BOPF, I think you'll find that you like it. And this is coming
from a developer who has had many bad experiences with BO frameworks (both in and outside
SAP...curse you EJBs!!!). All in all though, I have found the BOPF to be very comprehensive and
flexible. For me, one of the feel tests I normally conduct to gauge the effectiveness of a framework is
to ask myself how often the framework gets in my way: either because it's too intractible, limited in
functionality or whatever. I have yet to run into any such occurrences with the BOPF. It does a good
job of providing default behaviors/functionality while at the same time affording you the opportunity to
tweak just about everything. For example, if I want to build my own caching mechanism, I can do so
by plugging in my own subclass. If I want to pull data from a HANA appliance in real time, I can do so
in a determination. You get the idea. It's all there, so just poke around a bit and I think you'll find what
you need.

You might also like