CX Works

CX Works brings the most relevant leading practices to you.
It is a single portal of curated, field-tested and SAP-verified expertise for SAP Customer Experience solutions

Coding Standards for Developing with SAP Commerce Cloud

23 min read

Coding Standards for Developing with SAP Commerce Cloud

Standards are in place because they help ensure people follow an agreed upon approach for doing something. With SAP Commerce Cloud, you have the option to structure your code in many different ways. However, without standards, it can lead to difficulty in maintaining the code, or even extending the time it takes to on-board a new developer to the project. Defining consistent standards at the beginning of your project, and ensuring they are reviewed as part of your code review process, will significantly help to minimize your technical debt. With commitment from everyone on the project to follow these standards, it is possible to maintain consistency. However, you should not be extremely strict with the standards. You should leave some leeway for your developers to have their own "signature" on the result.

The coding standards here are not only based on Java language coding standards. They also focus on SAP Commerce Cloud specific guidelines and restrictions for the following areas:

  • Type system
  • Extension naming
  • Java coding standards and naming
  • Code formatting
  • Design principles

Table of Contents

Naming Conventions

Naming Convention in SAP Commerce OOTB Code:

Area Description
Extensions Extension names should only contain lower-case letters and be named according to their content. It is also common to use a project-specific prefix for all extensions of the same project.
Packages Packages should be indicating the area of their content. For example: core, storefront, and initialdata. In the case of service layer classes, which have an interface and an implementation, both should be packaged accordingly in separate packages.
Classes

Classes should use descriptive names, preferably in English. The name should be in CamelCase.

SAP Commerce implementations of interfaces are named using the following pattern:

Default<Name of Interface> implements <Name of Interface>

For example: DefaultProductService implements ProductService

Naming Conventions for Implementations

Defining a coherent naming convention is often neglected in a project. However, doing so contributes to the code quality and, if such a convention is observed, will make it easier for a developer to maintain the code of other developers.

Java

The standard Java naming convention of CamelCase, established by Sun Microsystems and other Java communities, should be used for class, method, variable, and constant names. Below, you will find examples for each identifier:

Identifier Naming Convention Example
Class

Class names are declared in UpperCamelCase, with the first letter of each word capitalized.

When possible, class names should avoid using acronyms and abbreviations.

public class MyClass() {...}
Method Method declarations follow a similar naming convention to Classes. However, the first letter of the method name is NOT capitalized, and follows the lowerCamelCase notation. public void myMethod() {...}
Variable Variable names also follow the lowerCamelCase notation. Variable names should have meaning, and should describe what they are being used for. You may declare a variable with one character when it is to be used as a temporary variable. int myVariable;
Constant

Constants are declared in all upper-case characters, and their words are separated by underscores ("_").

Constants may contain numbers/digits. However, the first character should not be a number/digit.

MY_CONSTANT

Services/Facades

  • End the class name with "Service" or "Facade" to indicate the type of class.
  • Replace the prefix of "Default" with a meaningful word describing the implementation characteristics, or at least with "Custom" for overloaded classes.
  • Prefix the extended interfaces by the project name.
  • Combine the first two rules for extended interface implementations.
Example
public interface AcmeProductService extends ProductService {...}

public class CustomAcmeProductService extends DefaultProductService implements AcmeProductService {...}

Controllers

  • The CMSComponents controllers must have the same name as the CMSComponents, but ending with "Controller".
  • Controller names must end with PageController when they generate only one page, and end only with Controller otherwise. The prefix of the naming convention must be meaningful to represent the implemented functionality.
    • For example: CategoryPageController. This generates one page (hence "PageController"), and indicates that it is for the Category page (the prefix).

Example
public class NavigationBarComponentController {...}
 
public class CategoryPageController {...}
 
public class PaymentAutoResponseController {...}

Interceptors

  • Prefix the interceptor name with the entity to which it applies the most. Generally, a name that indicates the purpose of the interceptor.
  • End the interceptor name with the interface it implements.
Example
public class AccountBalanceValidateInterceptor {...}
 
public class AccountUniqueValidateInterceptor {...}
 
public class CronJobInitDefaultsInterceptor {...}

Dynamic Attribute Handlers

  • Prefix the DynamicAttributeHandler’s name by the item’s name to which it applies.
  • Follow this prefix by the attribute name.
  • End the name with "AttributeHandler".
Example
public class TaskExpirationDateAttributeHandler {...}


CronJob

  • End the item's name with "CronJob".
  • Replace "CronJob" by "JobPerformable" for implementation classes.
Example
public class ErpProductImportCronJob {...}
 
public class ErpProductImportJobPerformable {...}
 


Spring Beans

SAP Commerce Cloud uses the Spring Framework extensively and therefore declares a lot of Spring-managed beans in XML configuration files. It is another common practice to name these Spring Beans in lowerCamelCase, with the first character of the bean ID being lower case.

Example
<alias alias="productService" name="myProductService" />
<bean id="myProductService" class="com.mycompany.core.service.impl.MyProductServiceImpl" parent="defaultProductService">
   <property name="myProductDao" ref="myProductDao" />
</bean>

Design Principles

To leverage the Spring framework consistently inside of a custom project, it is highly recommended to develop against interfaces. For each newly created facade, service, and data access object (DAO), an interface should exist, and all development should be performed against this interface only. This makes the code easier to test, and easier to swap with another implementation if required.

Facades

A facade is a software design pattern that abstracts from an underlying implementation. Facades offer an alternate, and an often less complex interface.

A facade is used to expose business logic to the front-end controllers. The front-end controllers should only use facades, services, converters, populators, and data transfer objects (DTO). Note, DTO's are actually named *Data in SAP Commerce. Using DTO’s reduces the number of exposed attributes, and also prevents the use of models in the presentation layer. The DTO itself should only contain values (no business logic).

A facade should never expose a model class. This is because using models will destroy the loose coupling of the UI to the business logic.

Services

The purpose of a service is to cover the business logic. All services together represent a public API. The service layer should be used to perform any kind of business logic, which will create, update or remove models. For self-created or extended services, it is recommended to prefix the name with the project name to make it known that this service contains custom code.

Example:


Package and Class Name
Interface de.hybris.platform.order.CalculationService

Out-of-the-box implementation as Accelerator service

de.hybris.platform.order.impl.DefaultCalculationService
Custom implementation, which extends/replaces the OOTB implementation com.<project name>.cscockpit.order.impl.Custom<project name>CalculationService

DAO

Using data access objects (DAO) is a good practice to abstract and encapsulate access to the data objects. The DAO can contain multiple methods for typical CRUD operations (Create, Read, Update, Delete). For an SAP Commerce project, it is recommended to place all Flexible Search queries in data access objects. The method names should include the parameter used, or a form of uniqueness in their name (e.g: findByCode(String code) or searchUnique(…)). This is to avoid double checks elsewhere in the code. A data access object should never return null for search methods. It is better to return an empty list. This will reduce the risk of null pointer exceptions, and follows the SAP Commerce best practice. The name of a data access objects should also include the ending "Dao". For example, CustomAcmeB2BBudgetDao to find B2BBudgetModel based on various parameter-like code.

Code Style

Coding Principles

It is highly recommended to define and document some of your own basic coding principles. This should be placed in a document or Wiki page, and will act as the guideline for every code review. Ideally, you would automate many of these code quality checks with Sonar. A few examples of basic coding principles are shown below:

Use of Null (Example of Coding Principle)

SAP Commerce avoids the use of NULL as a return value in most service implementations. The preferred method is to throw runtime exceptions if a precondition is not satisfied or a result is not expected. The use of NULL as return value can cause NullPointerExceptions during the code execution, and also adds unnecessary null checks to the code.

Example: de.hybris.platform.product.impl.DefaultProductService
public ProductModel getProductForCode(final String code)
{
   // This might throw a IllegalArgumentException
   validateParameterNotNull(code, "Parameter code must not be null");

   final List<ProductModel> products = productDao.findProductsByCode(code);

   // This might throw a UnknownIdentifierException or AmbiguousIdentifierException
   validateIfSingleResult(products, format("Product with code '%s' not found!", code),
   format("Product code '%s' is not unique, %d products found!", code, Integer.valueOf(products.size())));

   return products.get(0);
}

Use of Null in Your Own Code

    • The use of null leads to null pointers. Avoid the use of null anywhere in your code.
    • A method should never return null.
    • Avoid null checks of objects you obtained from a method call in your own code. Null should never be returned.

The following example shows a common requirement for a DAO method to find an object while avoiding the use of null. This example makes use of the Guava library, however Java 8 provides the Optional<> generic class as well.

The result of the findOrderById method is either "found something" or "found nothing,", where "nothing" is an expected scenario.

Throwing an exception in when nothing was found will cause performance issues. This is mainly seen when the method is called very often, and returning "no result" is frequent (see why).

public Optional<Order> findOrderById(String id)
{
	OrderResponse response = lookup(id)
	if (response.success)
		return Optional.of(response.result);
	else
		return Optional.absent();
}

Handling of the result in the client code:

In our case, result and no result are expected business cases!

Optional<Order> result = findOrderById(id)
if(result.isPresent()) {
 	// result logic using result.get()
} else {
	// no result logic
}

The Java 8 lambda syntax provides an interesting way to handle the results without the if () {} else {} pattern:

// assuming we want to calculate the order if present and do nothing otherwise:
findOrderById(id).ifPresent( o -> calculationService.calculate(o); );

// or if we want to return the entries if present and an empty list otherwise
return findOrderById(id).map( o-> getEntries() ).orElse(Collections.emptyList());

In case the outcome is "no result", and it is an exceptional case which cannot be recovered from, you can throw a runtime exception. At this point, no special handling is required in the client code.

Exception Handling (Example of Coding Principle)

SAP Commerce is based on a multi-layered architecture with a Service layer, Façade Layer, and DAO layer. Further, you can have web services interacting with the Service layer. When dealing with exceptions in a multi-layered architecture, the following best practices should be followed:

When to use Exceptions

    • Use exceptions for unusual cases that you do not expect. If you expect something to happen, you should handle it through a return value.

    • Do not use checked exceptions unless you have a very good reason for it!

Where and how should exceptions be handled

    • Handle exceptions close to the origin code, where the issue is first seen. In SAP Commerce, multi-layered architecture can catch the exception in the DAO layer. If you use FlexibleSearchService in the DAO layer, you will notice that exceptions are caught within FlexibleSearchService, and thrown up the call stack.

    • Throw exceptions up the method call stack using a custom exception relevant to that source layer. This allows you to create groups of exceptions, and handle them in a generic manner.

    • When throwing the method call stack, always pass the original exception cause, so that you can preserve the original root cause of the exception.

    • Never catch the “Exception” exception. The exception “RuntimeException”, and other checked exceptions, inherit from “Exception.” By catching the “Exception” exception, you are catching “RuntimeException” as well, which should be avoided. All checked exceptions should be caught and handled using appropriate catch handlers.

    • Common RuntimeException's, such as referencing an out-of-bounds array element, inappropriate use of a null-pointer, and illegal cast operations, should be avoided by checking the code for such conditions.

    • An exception should be logged only once. If the same exception is logged multiple times, examining the stack-trace to try to find the original source of the exception can be difficult and confusing.

Documenting Exceptions

    • The calling code of a method should be provided as much information as possible so that it can handle or prevent an exception. You should document your exception using Javadoc. This can be done by adding an @throws declaration, as seen below:
/**
 * @param transaction
 * @return PaymentTransactionEntryModel
 * @throws AdapterException 
 */

public PaymentTransactionEntryModel authorize(java.lang.String merchantTransactionCode, BigDecimal amount, Currency currency, AddressModel deliveryAddress, String subscriptionID)

Types of Exceptions in a multi-layered architecture

    • Within FlexibleSearchService you will see exceptions that reflect data related concerns. Examples include:
      • ModelNotFoundException
      • AmbiguousIdentifierException
      • FlexibleSearchException
      • IllegalStateException
      • UnsupportedOperationException
    • Within DAO, the exceptions typically bubble up from FlexibleSearchService. If FlexibleSearchService is not used (which is not standard practice), you will see exceptions that reflect data related concerns. In addition to the exceptions listed in the point above, you may also find the following:
      • UnknownIdentifierException
    • The Façade layer reflects technical or application related exceptions. Examples of such exceptions include:
      • IOException
      • UnknownIdentifierException
      • IllegalArgumentException
      • CommerceCartRestorationException
      • NotImplementedException
      • PasswordMismatchException
    • The Service layer should throw exceptions that do not leak sensitive or critical information to consumers. The service layer should also not reveal any architecture or design of the software. “Business exceptions” should be thrown on the service layer since developers are dealing with business logic. Examples of such exceptions include:
      • For TransportService we see a MessageDeliveryException
      • For CommerceCartService we see a CommerceSaveCartException
      • For TaxCalculationService we see a CalculationException
      • For CustomerLocationService we see a LocationServiceException

Security concerns with Exceptions

    • Web Services and Controllers should not pass sensitive or critical information to consumers or the User Interfaces. Therefore, exceptions should be handled before the response to the consumer/UI. All checked exceptions should be caught and handled by creating proper error responses before sending the response to the consumer.

Resource Handling

    • Resources that are opened need to be cleaned up. Use the "finally" block to clean up open resources, or use the java 7 feature “try with resource”. 
    • All the clean-up code should be in the finally block to close open resources.
    • To use “try by resource”, your resource must implement the AutoCloseable interface.

Transactions and Exceptions

    • When using exceptions, always use the "finally" block for rollback. During transaction execution, there may be issues which do not result in an Exception, but in an Error. Typically, errors will not be intercepted by a catch statement. If such an error arose during a transaction, the Transaction.current().commit(); statement would not be executed. The (incomplete) transaction would remain open, which will block resources and impact performance.
    • However, the final statement will be executed if Error's arise, and it can rollback the transaction. The database can then be in a consistent state again, whereupon the connection to the database will be closed.

Naming Convention

    • Java Exceptions are classes, and therefore follow the naming convention for Java classes (CamelCase).
    • The name of the exceptions should end with the word "Exception" (e.g: IllegalStateException).
    • There is no standard naming convention with Java. However, the name should indicate the issue and, if possible, where the issue is occurring.
    • It is recommended to use the following naming convention: <issue><where>Exception. Examples that follow this convention include:
      • UnsupportedOperationException (<issue><where>Exception)
      • FlexibleSearchException (<issue>Exception)
      • IllegalStateException (<issue><where>Exception)

Validation of Input Parameters

Validation of Results

    • Always return results that are valid.
    • Throw a runtime exception in case of an invalid data state.
    • Do not throw exceptions for results that are expected.

Optimizations vs. Readability

The coding principles should also define the distinction between readability and performance. Some developers tend to place a lot of functionality in one line, making the code hard to read and debug. The definition of this principle should also recommend a certain depth of modularity or preferred technologies. For example:

  • Development against interfaces.
  • External bean vs. internal or "hard-wired" object binding.
  • Usage of Spring annotation vs. XML based Spring wiring.
  • AOP vs. inheritance for behavior/implementation changes.

Example:



Readability
Example 1 original new DoSomethingObject(new SelectionContext(context, map), currentUser.getCurrentRole().getRole_id()).execute(getResultContext());

better

new DoSomethingObject(

new SelectionContext(context, map),

currentUser.getCurrentRole().getRole_id()

).execute(getResultContext());


better

final SelectionContext selectionContext = new SelectionContext(context, map);
final String roleId = currentUser.getCurrentRole().getRole_id();
final DoSomethingObject doSomethingCmd = new DoSomethingObject(selectionContext,roleId);
doSomethingCmd.execute(getResultContext());

Example 2 original int fourTimes = originalValue << 2;

better int fourTimes = originalValue * 4;

Most of the above "optimizations" will be performed at the compiler level today. Therefore, it is rarely a good practice to make the code less readable in an attempt to manually optimize a piece of code. The principle should be "readability first".

Code formatting

Another area that is positioned around readabilty and reusability is the code formatting. This should be a standard for all developers in order to avoid issues with the:

  • Look & feel.
  • Compatibility with different development environments.
  • Compatibility with different operating systems.
  • Preventable issues with code diffs or merge conflicts. 

The common set of rules for code formatting should cover, but is not limited to, the following topics:

Topic items to cover
filename
file encoding UTF8
line end character 
tabulator &
spaces 
leading spaces
tabulator width
spaces before and after round brackets 
imports

NO wildcard imports
sequence of imports

braces line break before and after braces
line length max line length, like 100 character
comment /
block comments 

reasons for comments
comments in the general/clean code
block comments (max size) 

variable naming

camel case
max name length
literals 

@Override use of @Override annotation
static objects/variable general usage of static objects and variables
what & how can be solved with Spring 

Javadoc

Each class and public method (except Getter and Setter methods) should have a Javadoc. This is especially the case for public methods representing an API for other extensions. Note that these Javadoc’s should still be accompanied with descriptive methods and parameters.

  • Avoid plainly using automatically ­generated comments.
  • Each protected method should have a Javadoc as well, since it is part of the newly created API.
  • The Javadocs should explain how the extension is designed.
  • Synchronization needs to be documented.
  • Do not document property style Getters and Setters (e.g: on a DTO).
  • Use Javadoc to document what a method is doing, instead of using inline comments.

*-items.xml

The items.xml files contain the various elements of the domain model. These *-items.xml files require an internal structure which needs to be followed.

  • collectiontypes
  • enumtypes
  • relations
  • itemtypes

This order cannot be changed. However, the parts that are not used do not need to be included.

Guidelines Inside of *-items.xml

See Data Model Design with the SAP Commerce Cloud Type System for our recommendations on how to structure your Type System in your *-items.xml file.

Tools

Various tools can help during the development process to reduce the amount of “bad code.” It is recommended using tools to make sure that the general and/or project specific coding standards are followed.

These tools will not remove the need for code reviews. The verification of standards should still happen during the code review. It is an essential responsibility for the architect and lead developer to make sure that these guidelines are followed and implemented.

Development Integrated Development Environments (e.g., Eclipse, IntelliJ)

Most common IDE’s today offer the support to achieve various goals. These include:

  • Creating Javadoc’s.
  • Saving actions for code formatting.
  • Organizing imports. 

Using this functionality will not replace the responsibility of the developer to follow the standards. The IDE can only support the developer in following them.

For SAP Commerce development there is no specific IDE required. The decision to use a specific IDE belongs to the developer, and their team. Commonly used IDE's are:

  • Eclipse.
  • IntelliJ (a lot of OOTB functionality for SAP Commerce development, Spring bean validation, Ant & Maven support, ...).
  • Spring Tools Suite (Eclipse-based, brings a lot of plugins pre-installed for SAP Commerce development, like Spring bean validation, Ant & Maven support, ...).

Sonar

SonarQube (commonly known simply as Sonar), is a tool that visualizes the results of static code analysis, and is able to show the change at different times of the project. Sonar should be included in your CI build process for at least every release build. However, it is better to be included in a daily build job due to Sonar being able to provide an easy overview of the current quality metrics of the project. For example:

  • Code duplication.
  • Dependency cycles.
  • Cyclomatic complexity.
  • Violation against the coding standard.

These graphics sometimes provide more information than a peer review can. For example, Copy&Paste code.

SAP Commerce ships with Sonar ant tasks and multiple profiles. More details about how to configure Sonar with SAP Commerce can be found in the article: Measuring Code Quality with Sonar.

Source Code Repository Exclusions

The purpose of source code repository exclusions is to enable every developer to have a clean workspace to work with. This will also ensure that only intentionally modified or newly created files can be committed to the source code repository. Any files generated automatically via build scripts should be added to your exclusion list, so that they are not checked into your source code repository. All common source code repositories (such as Git and SVN) support the exclusion of files and folders by name or pattern.

Conclusion

Coding standards may not seem like a big deal at the beginning of a project, but they can have a significant impact on the maintenance and readability of your code. This can usually be seen as early as a few sprints into a project. Taking the time to create and enforce a set of standards will pay off in the future. That said, you should work to ensure your standards are not too overbearing or strict that they become tough to follow/review. Additionally, you should strive to automate as many of the checks as possible.

Overlay