Skip to main content
Version: 8.4.x

Extending Domain Model

The Self-Managed Commerce platform is built on a flexible e-commerce domain object model. If your organization has specific needs, you can extend it. Changes to the domain object model affect the platform on many levels, both in terms of functionality and performance. Before making domain changes, you need to carefully consider the impact.

  • Be aware of how Load tuners and FetchGroups optimize the loading of fields for different types of objects

  • If you are thinking about extending catalog-related domain objects (Product, Catalog, etc.), consider whether you can use attributes instead

The basic steps for extending the domain object model are:

  1. Modifying the database schema
  2. Creating the domain class and interface
  3. Modifying the ORM configuration
  4. Add references to the Persistence Unit
  5. Registering new domain classes with the BeanRegistrar

Optional steps you may need are:

  1. Exclude references from the Persistence Unit
  2. Override Persistence Unit Properties

Modifying the database schema​

When creating or modifying a domain object, you need to modify the schema to define the new fields.

If you are creating a completely new domain object, you will need to add a new table.

If you’re extending an existing domain object, we recommended that you use the single table inheritance strategy. With this strategy, the base class’s data is stored in the base table and your extended class’s data is stored in the same table. To implement this, you need to:

  1. Modify the table to define the new fields.
  2. Modify the table to define a discriminator field. Usually this is a column named TYPE with a data type of VARCHAR(50).
note

This discriminator column is used by OpenJPA to identify, for each row, which domain object to instantiate when retrieving the data.

Creating the domain class and interface​

Any new or extended domain object has both an interface defining the methods and a class containing the implementation. To ensure the domain object's data is persisted and loaded correctly, the implementation class needs to contain the appropriate JPA annotations.

Choosing the base interface and abstract class​

When creating a new domain object, the domain interface should extend one of the platform's persistence interfaces and the implementation class should extend the corresponding abstract class. The following class hierarchy is available:

Persistable (interface)
└─ AbstractPersistableImpl
├─ Entity (interface, extends Persistable + GloballyIdentifiable + Initializable)
│ └─ AbstractEntityImpl
│ └─ AbstractLegacyEntityImpl
│ └─ AbstractListenableEntityImpl
│
└─ AbstractLegacyPersistenceImpl

Persistable / AbstractPersistableImpl​

Use when the domain object only requires a database-assigned unique identifier (UIDPK) and does not need a GUID.

  • Interface: com.elasticpath.persistence.api.Persistable
  • Abstract class: com.elasticpath.persistence.api.AbstractPersistableImpl

Provides:

  • getUidPk() / setUidPk(long) — System-dependent unique identifier assigned by the database.
  • isPersisted() — Returns true when UIDPK > 0.

Choose this when: You are creating a simple value object or child entity that is cascade-loaded through a parent and does not need to be independently identified across environments.

Entity / AbstractEntityImpl​

Use when the domain object needs both a database-assigned UIDPK and an environment-independent GUID (globally unique identifier).

  • Interface: com.elasticpath.persistence.api.Entity (extends Persistable, GloballyIdentifiable, Initializable)
  • Abstract class: com.elasticpath.persistence.api.AbstractEntityImpl

Provides everything from AbstractPersistableImpl, plus:

  • getGuid() / setGuid(String) — Environment-independent identifier (from GloballyIdentifiable).
  • initialize() — Deferred initialization hook called by the bean factory for new instances. Auto-generates the GUID.
  • equals() / hashCode() — Implemented based on GUID, ensuring consistent identity across staging and production environments.

Choose this when: You are creating a new top-level domain entity that must be identifiable across environments (e.g., for data sync or import/export) and does not need bean factory access or property change listeners within the domain object itself. This is the recommended base class for new domain objects.

AbstractLegacyEntityImpl​

Extends AbstractEntityImpl and adds the ability to access the Spring bean factory from within the domain object.

  • Interface: Domain interface should extend Entity.
  • Abstract class: com.elasticpath.domain.impl.AbstractLegacyEntityImpl

Provides everything from AbstractEntityImpl, plus:

  • getPrototypeBean(String, Class) / getSingletonBean(String, Class) — Access to Spring-managed beans from within the domain object.

Choose this when: The domain object's internal logic needs to look up other Spring beans at runtime. This is a legacy pattern — prefer injecting dependencies through services rather than looking them up from within domain objects.

AbstractListenableEntityImpl​

Extends AbstractLegacyEntityImpl and adds PropertyChangeListener support.

  • Interface: Domain interface should extend Entity and ListenableObject (com.elasticpath.domain.ListenableObject).
  • Abstract class: com.elasticpath.domain.impl.AbstractListenableEntityImpl

Provides everything from AbstractLegacyEntityImpl, plus:

  • addPropertyChangeListener() / removePropertyChangeListener() — Register listeners that are notified when properties change.
  • firePropertyChange(String, Object, Object) — Protected method to trigger change notifications from setter methods.

Choose this when: Other parts of the application need to observe and react to property changes on the domain object. Many existing core entities (e.g., ProductImpl, CategoryImpl) extend this class.

AbstractLegacyPersistenceImpl​

Extends AbstractPersistableImpl (not AbstractEntityImpl) and adds bean factory access. This is a separate branch of the hierarchy that provides UIDPK and bean factory access without GUID support.

  • Interface: Domain interface should extend Persistable and Initializable (com.elasticpath.base.Initializable).
  • Abstract class: com.elasticpath.domain.impl.AbstractLegacyPersistenceImpl

Provides everything from AbstractPersistableImpl, plus:

  • getPrototypeBean(String, Class) / getSingletonBean(String, Class) — Access to Spring-managed beans from within the domain object.
  • initialize() — Deferred initialization hook.

Choose this when: The domain object is a child or value object that does not need a GUID but does need to access the bean factory from within its implementation. Like AbstractLegacyEntityImpl, this is a legacy pattern.

Summary​

Base classInterface to extendUIDPKGUIDBean factory accessProperty change listeners
AbstractPersistableImplPersistableYesNoNoNo
AbstractEntityImplEntityYesYesNoNo
AbstractLegacyEntityImplEntityYesYesYesNo
AbstractListenableEntityImplEntity, ListenableObjectYesYesYesYes
AbstractLegacyPersistenceImplPersistable, InitializableYesNoYesNo
tip

For new domain objects, prefer AbstractEntityImpl / Entity unless you have a specific need for bean factory access or property change listeners. The AbstractLegacy* classes exist primarily for backward compatibility with existing domain objects.

JPA annotations​

  • At the class level, include the following annotations:

    • @Entity and @Table - Identifies the object as an Entity, which is an object that can be persisted in the system.
    • @DiscriminatorValue - Required for an extended domain object. Identifies a value that will be stored in the base table’s discriminator column. The value differentiates the domain objects and the extension objects in their common database table.
    • @DataCache - Extended domain objects do not automatically inherit data cache settings. If the parent class specifies data cache settings, the extended class must also include a matching annotation.
  • At the method level, include the following annotations:

    • @Basic and @Column annotations on the getter methods to specify how field data is mapped to table columns.
    • Annotation required to load data from related tables, such as @OneToMany.

The following code snippet shows the table annotations for an extension of the OrderImpl class.

@Entity
@Table(name = OrderImpl.TABLE_NAME)
@DiscriminatorValue("ExtOrderImpl")
@DataCache(enabled = false)
public class ExtOrderImpl extends OrderImpl implements ExtOrder {
/** Serial version id. */
private static final long serialVersionUID = 5000000002L;
}

For more information, see the OpenJPA documentation on metadata.

Modifying the ORM (Object Relational Mapping) configuration​

If you’re extending an existing domain class, JPA needs to be made aware that the base class accepts extensions. This means overriding the JPA metadata for the base class.

To do this, open the extensions/core/ext-core/src/main/resources/META-INF folder and add a new metadata ORM file.

For example, to make OrderImpl extensible, create ext-order-orm.xml and populate it with the following:

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd" version="2.0">
<entity class="com.elasticpath.domain.order.impl.OrderImpl">
<inheritance strategy="SINGLE_TABLE"/>
<discriminator-value>OrderImpl</discriminator-value>
<discriminator-column name="TYPE" discriminator-type="STRING"/>
</entity>
</entity-mappings>

Add references to the Persistence Unit​

New persistence classes and metadata ORM files should be registered in the core extension’s jpa-persistence.xml.

For example, to register the ext-order-orm.xml file, open extensions/core/ext-core/src/main/resources/META-INF/jpa-persistence.xml and add a reference to the new metadata ORM file within the persistence-unit node as follows:

<mapping-file>META-INF/ext-order-orm.xml</mapping-file>

To register ExtOrderImpl, open extensions/core/ext-core/src/main/resources/META-INF/jpa-persistence.xml and add a reference to the extended domain class within the persistence-unit node as follows:

<class>com.elasticpath.extensions.domain.order.impl.ExtOrderImpl</class>

Registering new domain classes with BeanRegister​

New domain classes must be registered through the core bean factories. You can add new bean definitions in the core extension’s ep-core-plugin.xml to include entries for the new classes.

For example, to register the extended domain class, open extensions/core/ext-core/src/main/resources/META-INF/conf/ep-core-plugin.xml, and add the following:

<bean id="order" scope="prototype" class="com.elasticpath.extensions.domain.order.impl.ExtOrderImpl"/>

After you make this change, running mvn clean install from the command line against the core extension project will compile your classes, perform bytecode enhancement, rebuild the jar, and install it in your repository.

Exclude references from the Persistence Unit​

To exclude existing domain classes and/or metadata ORM files, you can add to the exclusion lists persistenceMappingExcludedClasses and persistenceMappingExcludedFiles respectively. These can be found in the core extension’s ep-core-plugin.xml file.

Override Persistence Unit Properties​

Occasionally you may wish to override some of the properties defined in the core persistence unit, or add new properties. To do so, add the properties to the persistencePropertyOverrides map in the core extension’s ep-core-plugin.xml file. Do not add or change properties in any of the jpa-persistence.xml files, as the order in which these files are resolved cannot be guaranteed.