The Consultingwerk OERA Maturity Model
 

Consultingwerk Blog

The Consultingwerk OERA Maturity Model

by Peter Judge | Feb 23, 2024

 The Introduction to the OpenEdge Reference Architecture provides an overview of the OERA: 

The OpenEdge Reference Architecture (OERA) defines the general functional categories of components that comprise an application. It can be used as a high-level blueprint for developing OpenEdge service-oriented business applications. 

 

The OpenEdge Reference Architecture 

Each layer of the OERA consists of distinct components, each with specific characteristics, roles and responsibilities.  In addition, the OERA provides guidelines as to how each of the architectural components interacts.  The following diagram illustrates the component architecture and the relationships between each of the components. 

The purpose of the OERA is to provide guidance on implementing service oriented – may even microservice oriented – applications using the OpenEdge platform. It provides architecture blue prints that facilitate the use of AppServers and encourages separation of business logic and consumers such as user interfaces and (web or rest) services. 

The OERA is not prescriptive, and application developers may choose to implement only some aspects or components of the OERA. Certainly, separate Data Access and Data Source layers are frequently not implemented, and are then considered a single layer. Likewise, the separation between business logic and data access is not always implemented as strictly as described in the OERA. 

Implementing principles of the OERA will help future-proofing and OpenEdge application. The authors of this article expect that in 10 years from now, we will still have access to a more or less compatible variant of the OpenEdge AppServer and ABL. At the same time we are expecting that todays “hottest” user-interface technologies or API standards will most likely be outdated by then. In other words it is important to focus on the backend of a business application and make sure that it is capable of supporting not only today’s consumers. 

Introducing the OERA maturity model  

The OERA maturity model aims to give application architects and developers some orientation in how to implement OERA compliant ABL applications – typically as a result of modernizing existing (legacy) ABL applications.  

The OERA Maturity model follows the assumption that different developer teams have different requirements and expectations for the architecture and coding style of modernized ABL applications. The OERA maturity model builds upon the OERA and is an approach to describe different methods how the OERA has been implemented, primarily relating to the Business Components and Data Access layers. 

Modernization efforts may happen in multiple stages or iterations. So a specific application implementation may aim to achieve the higher levels described in this document at different stages of the implementation. Following mind-sets of domain-driven design, different levels defined in this document may coexist in different components of an application. 

A consequence of following the OERA maturity model is that applications have more, smaller programs – OOABL types ideally, since this means that the compiler can help improve the quality of the application. These components will better follow the separation of concerns principle (sometimes known as the Single Responsibility Principle), resulting in an application codebase that becomes more maintainable, since there are fewer sprawling “God programs”, where minor changes may have unforeseen and large impacts due to the program having many responsibilities.  

In addition, development from the business application developer’s perspective may be simplified, since a certain type of task – fetching data, for instance – is typically handled using the same set of common code and coding approaches in all cases. The number of exceptions to these tasks are reduced, and if there are sufficient numbers of “exceptions”, this becomes a sign that a new concern/responsibility exists. 

The downside of implementing full separation of concerns is that is typically becomes more difficult to reuse existing blocks of code. The ability to reuse large parts of existing code promises a faster migration process while at the same time reducing the risk as tested code is continued to be used (either by wrapping procedural code into classes or running such procedures as-is). This is a typical conflict of goals – as migration speed and risk reduction are honorable goals as much as increased maintainability and future-proofness are. 

Software development factors such as maintainability and agility are generally negatively impacted by code duplication often found in legacy applications. Duplication of processing logic (e.g. validation, writing related records, calculations, etc.) is frequently not an exact duplication leading especially new developers challenged with questions such as which of the many routines calculating a certain value is the right or best one. 

In the OERA maturity model we describe various approaches (levels) how the OERA can be implemented in an OpenEdge application.  

 

Levels of the Consultingwerk OERA Maturity Model 

Level 0: Business services run on an AppServer 

At this level, business services exist, and run on an AppServer. The business services are written in OOABL or procedural ABL, and each have their own API in terms of method/function names and/or parameters. The data structures consumed and/or returned may be ProDatasets, temp-tables or OOABL objects. Clients – whether they are GUI, TTY or Web – call into these services via a service interface. The presence of the service interface layer is seen as an important factor that alone qualifies the application as OERA compliant. The service interface layer provides future-proofness as requirements of different clients (communication protocol, data format, authentication and authorization, session management etc. ) can be handled in a central component or application layer. 

While having business services running on an AppServer is good, at this level there is typically much duplication, or at best truly little common or shared code. This results in higher maintenance costs and lower productivity, since changes generally need to be made across multiple programs, in potentially subtly separate ways. This impacts the client code as well as the services themselves. 

Access to the business services on an AppServer is typically done using many service interfaces –potentially one per business service, if not one per operation/task. While multiple or even many service interfaces are not bad per-se, these service interfaces should not duplicate code but for minor details. 

Additionally, the OERA does not prescribe the use of OOABL code, which may lead – particularly for APSV clients such as ABL or OpenClients - to business services being directly called from a client, without the use of service interfaces. 

 

Level 1: Standard interfaces: business entities / tasks, service interfaces  

Level 1 requires business services to have a single responsibility: an operation to put a customer on hold should not update the order data directly, for example. Single responsibilities (or separation of concerns) can be abstracted into standardized interfaces for accessing business services. The separation of concerns will also lead to central – or core – services such as those used for Service Management or text translation. 

The use of OOABL is strongly recommended for this purpose. While it may be possible through code review and other discipline to ensure that procedural code does not violate the Single Responsibility Principle and bleed across interface boundaries, we believe that the ABL compiler excels at this function and should be used to the fullest possible extent. There are parts of the ABL that may never support OOABL – for example, calls across an AppServer boundary from an ABL client – but the use of procedural code should be as limited as possible. 

Standardized interfaces can be for single services (Business Entities), or more complex services (Business Tasks). Note that the complexity here does not refer to what a business service does behind the standardized interface (a service that calculates a bill of materials may be extremely complex), but rather to the fact that complex services – with its own responsibility - are typically composed of a number of distinct services that have their own responsibilities. So, a Business Task that generates an invoice for an order may need to access the Order, Customer and Tax Rate Business Entity, as well as the Invoice Business Entity. Each of these five business services has their own responsibility. 

Standardized interfaces should have their own service interfaces. There may be more than one service interface for a business service: for instance, different client types – WEB or APSV or SOAP – receive requests via different APIs, with different format and error handling approaches. For example, it may be possible to access the invoice generation service via a RESTful API, via SOAP, messaging or an ABL GUI client. Each of these clients has its own service interface that calls the invoice generation Business Task: for the RESTful API, this would be a web handler; for SOAP and ABL clients, a procedure. 

Following the assumptions made in the introduction paragraphs of this document Business Services need to be implemented in a manner that is agnostic to potential consumers; in addition, API industry standards of today such as JSON and RESTful should not influence business logic. In consequence of this, developers should for instance consider even standards such as JSON being more an aspect of today and not a fundamental asset of application architecture. Business Services should never be producing JSON documents directly. Application data or requests should be transformed to and from JSON only at the service interface level. 

The Consultingwerk.OERA.IBusinessEntity interface and Consultingwerk.OERA.BusinessEntity class are the foundation of the SmartComponent Library’s Business Entities. The Consultingwerk.OERA.RestResource.RestEntitiesWebHandler class (for RESTful clients), Consultingwerk.OERA. JsdoGenericService.WebHandler.* webhandler classes (for JSDO-based clients), and OERA/support/proSIretrieve.p and OERA/support/proSIsubmit.p procedures (for ABL GUI clients) are the service interfaces primarily used to access the Business Entities. Each of these service interface implementations are optimized for communication with a certain kind of consumer. 

 

Level 1A: Service-specific interfaces for application services 

Level 1 of the OERA maturity model introduces standard (or generalized) interfaces that support access to business services. These interfaces (and their corresponding service interfaces) provide access to domain logic. While these business services may be complex in their operation, the interfaces they implement are common to many business services. Many applications have additional, more complex, interfaces to services that need to be provided to clients. 

Level 1A of the OERA maturity model can be considered optional, or a refinement of Level 1, rather than a required level. The creation of standardized services and interfaces (Level 1) should not be considered an optional part of the OERA maturity model. 

Application services allow the creation of application-specific services (and interfaces) that are composed of multiple domain services. Interfaces used for application services are typically specialized, and are not generally implemented by other services. As a result, each application service has its own service interfaces (depending on the client technology and the number of operations exposed). 

Application services are introduced to implement consumer-specific requirements separately from the domain logic that they are based on, and can better support reuse of the business services from which they are composed. Application services should interact with other business services using the standardized interfaces that are the outcome of achieving Level 1 of the OERA maturity model. 

 

Level 2: Separate data access from business entities 

In the OERA, a Business Entity is considered to have a logical data model. In Business Entities, these logical models are usually represented as temp-tables and ProDataSets. A logical data model includes data from one or more database tables, sometimes in a denormalized form, as well as derived or calculated data, which may combine data from various sources, and be presented as read-only data. The temp-table and field names may differ from the physical names to be more human-readable (eliminating very short and specialized abbreviated field names; using real names for spare fields), or machine-readable by other systems (e.g., removing dashes from names, or splitting array fields into individual fields). 

Level 1 requires business services to have a single responsibility: it is entirely feasible for a read-only and an updateable version of a Business Entity to exist, as separate responsibilities. Each of these services could have the same logical model, with a Business Entity indicating whether it is read-only or not by means of an interface. 

The population of the logical model is one responsibility of the Data Access layer, along with committing changes to its data source(s). Types of Data Access objects might be those that use an OpenEdge database, or those that use an XML document, or those that call a SOAP or RESTful service. Multiple Data Access components might exist in parallel for the same Business Entity, ideally addressed through configuration data. The Business Entity should not know, nor care, as to where the data comes from, or goes to. Its responsibilities include operating on that logical model once populated. In a real world application, local XML files might be used as a cache or fail-over for a remote database that may not be accessible. 

Separate data access capabilities have additional benefits, including the ability to better test business services by injecting known (mock) data into the Business Entity. 

The Consultingwerk.OERA.IDataAccess interface and its companion Consultingwerk.OERA.DataAccess class are used for data access in almost all Business Entities. 

In our view, the OERA’s separation of a Data Access and a Data Source layer does not provide sufficient separation of concerns: there is usually a 1-1 relationship between such components, and so we believe that the Data Access layer is sufficient. 

 

Level 3: Separate validation routines 

At this level, there are multiple business services using the same or similar logical data model, populated from the same Data Access object. Retrieving data is achieved using a set of components that can be combined in numerous ways. 

However, when submitting data this is not true. Even if a client has performed some validation on the data that has been sent to the business service, it does not absolve the business service from its own validation. Business Entities, in particular, must validate that the data provided meets business or domain constraints before being committed: these may include ensuring that a field has a value, that values are in a particular range, that if one field has a value, another field has a related value (or empty). Validation such as ensuring that the value of a submitted order (as calculated by the sum of its order lines) does not cause the customer’s outstanding balance to exceed their credit limit may also be executed in a business service. 

Level 3 requires the validation logic as its own component: this allows validation of data, separated from the context of the Business Entity or Business Task, using a data model (i.e., temp-table or ProDataSet or entity objects). The validation logic must be reusable by multiple business services that share the same – or substantially similar – data model; must have its own set of interfaces; and must follow the separation of concerns that is the foundation of the OERA Maturity Model. 

The Consultingwerk.OERA.Validate and Consultingwerk.OERA.ValidationHelper classes simplify standard validation scenarios, to be used by BusinessEntities. This class provides static methods that can be used in an Assertion style to implement validation logic. 

 

Level 4: Object model for data 

Business service data models are typically in the form of temp-tables and ProDataSets. These data structures are a core feature of the ABL, but they are handle-based. This means that they lack many of the benefits that OOABL brings, such as: strong typing, interfaces, reference-based parameter passing, and more.  

While there are some compiler checks possible on temp-tables (for parameter passing), they do not extend to more generic code, and they operate on the whole data structure. So, it is not possible to define a data model as consisting of multiple components (e.g., it has an address, it has contact details, etc.). 

Level 4 defines the temp-table and ProDataSet data models as a set of OOABL classes and interfaces. A typical implementation uses classes to contain or wrap the temp-tables and ProDatasets, so providing the benefits of OOABL strong-typing and the benefits of the temp-table’s easy in-memory and on-disk storage, as well as simple read and write operations in ABL. 

Defining a data model as distinct parts also allows more granular interfaces for validation, amongst other purposes. So, a routine that validates an address’s correctness can be passed an object model, and only work on the properties and/or methods that are defined as belonging to the address by means of an interface type. 

This is not to say that the data model needs to move away from using temp-tables and ProDataSets; on the contrary, the ABL is powerful in allowing handle- and OOABL-based data structures to co-exist.  

Similarly, such object data models are not replacements for Business Entities or Business Tasks. They will almost certainly use the Business Entity to perform updates and reads. The Consultingwerk.OERA.TableModel provides such a base model for temp-tables; the Consultingwerk.OERA.DatasetModel , for ProDatasets. Individual Business Entities have their own (largely generated) versions that expose temp-table fields as OOABL properties.