Announcement: You can find the guides for Commerce 7.5 and later on the new Elastic Path Documentation site. This Developer Center contains the guides for Commerce 6.13.0 through 7.4.1.Visit new site

This version of Elastic Path Commerce is no longer supported or maintained. To upgrade to the latest version, contact your Elastic Path representative.

Resource Repositories

Resource Repositories

A repository class provides information through either Commerce Engine or another repository to a resource via a consistent set of CRUD (create, read, update, delete) methods. Repositories translate between commerce domain objects and API representations, abstracting the complexity of Commerce Engine's databases and business logic from the API layer.

How Repositories Work

Repositories typically implement either the Repository or LinksRepository interface in the com.elasticpath.repository package, and one or more of its methods. A repository class may have multiple repositories implemented.

CRUD Repositories

CRUD repositories implement the Repository interface, and at least one of the following methods:

  • create(): Creates an entity.
    • E.g. Create a new shipping address for a customer's profile.
  • findOne(): Finds (reads) a single entity.
    • E.g. Find a specific shipping address for a customer's profile.
  • findAll(): Finds (reads) all entities of a specified type.
    • E.g. Find all shipping addresses for a customer's profile.
  • delete(): Deletes an entity.
    • E.g. Delete a shipping address from a customer's profile.
  • update(): Updates an entity.
    • E.g. Delete a shipping address from a customer's profile.

Links Repositories

Links repositories are used to link an identifier of one resource type with zero or more identifiers of another resource type. They implement the LinksRepository interface and at least one of the following methods:
  • getElements(): Returns all identifiers of the linked resource type.
    • E.g. Return all identifiers for items on a wishlist.
  • deleteAll(): Deletes all elements of the linked resource type.
    • E.g. Delete all items from a wishlist (but not the wishlist itself).

Extending Repositories

To add functionality or fields to an entity, or to change its default behaviour, you must extend a repository. To extend a repository, either override its out of the box methods or implement new ones.

Implementing Additional CRUD Functionality

A repository may not implement all CRUD methods by default. To implement additional CRUD functionality, implement the desired CRUD method in an existing repository.

For example, the ability for a user to create more than one wishlist per profile is not implemented by default. To implement this functionality, extend the out of the box repository by adding a create() method to it.

Changing the Default Behaviour of a Repository's CRUD Method

To change the default behaviour or business logic of an repository, override a repository's CRUD method with the desired functionality.

Adding Fields to a Repository

Sometimes, when extending a Commerce Engine domain object like addresses, you may need to pass extra fields to the repository and resource. To add fields to an entity that the repository returns, override the appropriate CRUD methods in a repository class to include the extra fields.

An example of this is examined in detail in the Extend a Helix Resource tutorial.

Creating New Repositories for New Resources

When creating a custom resource, you will have to write the repositories from scratch. Your repository class should do the following:
  • Annotate the repository class with org.osgi.service.component.annotations.Component, to ensure it is exposed as an OSGi service and injectable.
  • Implement the Repository or LinksRepository interface.
  • Implement at least one CRUD method provided by the interfaces.
  • Use either public or protected to ensure method extensibility.

Implementing CRUD Methods

Repositories implement an interface's methods based on a resource's intended functionality. To implement a CRUD method, you will typically invoke one or more Commerce Engine services or legacy repository methods using the ReactiveAdapter class. ReactiveAdapter helps to integrate CE Services and legacy repositories into reactive Rx execution chains.

To use the ReactiveAdapter methods, inject an instance of ReactiveAdapter into your repository implementation, then use one of its methods to call a Commerce Engine service or legacy repository:

@Inject
CarsRepositoryImpl(
  @Named("reactiveAdapter")
  final ReactiveAdapter reactiveAdapter) {
    this.reactiveAdapter = reactiveAdapter;
 }
          
...
        

Calling Commerce Engine Services

To initiate a non-blocking Commerce Engine service call with automatic null and exception handling, use one of the following ReactiveAdapter methods:
  • fromService() – returns a RxJava Observable which wraps the return value of a Commerce Engine service.
  • fromServiceAsSingle() – returns a RxJava Single which wraps the return value of a Commerce Engine Service.
  • fromServiceAsCompletable() - returns whether or not an operation was successful.

An example of returning an Observable from a Commerce Engine service is below:

Observable o = reactiveAdapter.fromService(() -> ceService.myMethod())
          

ReactiveAdapter methods handle InvalidBusinessStateException (HTTP 409) and EpValidationException (HTTP 400) exceptions automatically, and return appropriate structured error messages.

Custom Exception Handling when Calling Commerce Engine Services

If needed, you can still do custom exception handling:

Completable c = reactiveAdapter.fromServiceAsCompletable(() -> {
    try {
      return customerService.update(customer);
    } catch (UserIdExistException error) {
      throw exceptionTransformer.getResourceOperationFailure(error);
    }
  });
}
          

Or:

Completable c = reactiveAdapter.fromServiceAsCompletable(() -> customerService.update(customer))
    .onErrorResumeNext(throwable -> {
      try {
        throw throwable;
      } catch (NullPointerException npe) {
      return Completable.error(ResourceOperationFailure.notFound());
    }
  });
}
          

Calling Legacy Repositories

You may have to call a legacy resource's repository from your resource. Legacy repository methods return an ExecutionResult. ReactiveAdapter provides methods for returning this data to your repository as a RxJava type, depending on the type of data wrapped in the ExecutionResult.

If the legacy repository call returns an ExcecutionResult containing an Iterable as data use ReactiveAdapter.<String, List<String>>fromRepository():

Observable o = reactiveAdapter.<String, List<String>>fromRepository(() -> repository.getShoppingItems())
          

If the legacy repository call returns an ExecutionResult containing a non-Iterable, non-null Object use ReactiveAdapter.<String, List<String>>fromRepositoryAsSingle():

Single s = reactiveAdapter.<String, List<String>>fromRepositoryAsSingle(() -> repository.getDefaultCard())
          

If the legacy repository call returns an ExecutionResult containing a null value as data use ReactiveAdapter.<String, List<String>>fromRepositoryAsCompletable():

Completable c = reactiveAdapter.<String, List<String>>fromRepositoryAsCompletable(() -> repository.updateCustomer())
          

All these methods are non-blocking. Typically the legacy repository method does the exception handling.

Injecting a Repository into a Prototype

In order to inject a Repository (implementing either Repository or LinksRepository) use the @Inject annotation in combination with the @ResourceRepository annotation in the method signature:

@Inject
public MyPrototype(@ResourceRepository Repository<MyEntity, MyIdentifier> repository) {
      

Make sure to type the generic type parameter of the Repository classes to the appropriate ResourceEntity and ResourceIdentifier.