Broadleaf Microservices
  • v1.0.0-latest-prod

TMF620 - Product Catalog Management

TMForum API Version Support Required Broadleaf Service(s)

v5

Catalog Services, Pricing Services

Supported APIs for Product Catalog Management

TMForum Resource Description Endpoints

ProductSpecification

ProductSpecification describes high level product information that can subsequently be used to create ProductOffering instances. In Broadleaf this conceptually maps to the BusinessType ('product type') domain provided by Catalog Services

POST /productSpecification

GET /productSpecification

GET /productSpecification/{id}

DELETE /productSpecification/{id}

PATCH /productSpecification/{id}

ProductOffering

ProductOffering represents a product that is made available for purchase. In Broadleaf this conceptually maps to either Product or Variant provided by Catalog Services

POST /productOffering

GET /productOffering

GET /productOffering/{id}

DELETE /productOffering/{id}

PATCH /productOffering/{id}

ProductOfferingPrice

ProductOfferingPrice represents a price for a ProductOffering. In Broadleaf this conceptually maps to a PriceData provided by Pricing Services

POST /productOfferingPrice

GET /productOfferingPrice

GET /productOfferingPrice/{id}

DELETE /productOfferingPrice/{id}

PATCH /productOfferingPrice/{id}

Architecture View

Broadleaf supports TMF620 Product Catalog Management via an Extension Model on top of Broadleaf’s existing Catalog Microservice and Pricing Microservice. This extension model works by defining an extension library alongside Broadleaf’s default catalog and pricing services dependencies which will enable additional TMForum specific capabilities.

TMF 620 Architecture

High Level Mapping Strategies

ProductOfferingPrice

The core Broadleaf concept that underpins ProductOfferingPrice is PriceData. In Broadleaf, every PriceData record is associated with a PriceList, which groups together PriceData that need to share common characteristics such as currency.

Broadleaf supports an extended JSON property blcPriceListContextId on the default ProductOfferingPrice payload which can properly identify the Broadleaf Price List a certain price request is associated with. Note that this field is optional and the system will default to a particular value specified in the API Context attributes if not in the request payload.

Key Components

Broadleaf’s TMForum Extensions library provides several Spring utility components that are responsible for assisting in conversions between TMForum Concepts and Broadleaf Concepts. The following components can be extended to support a different mapping strategy if needed.

  • com.broadleafcommerce.tmforum.pricing.tmf620.service.mapping.ProductOfferingPriceIdProcessingUtility

    • ProductOfferingPrice.id is derived from Broadleaf’s PriceData.id, and this component is responsible for handling the conversion from both directions

  • com.broadleafcommerce.tmforum.pricing.tmf620.service.mapping.ProductOfferingPriceMappingUtility

    • The conversion logic between ProductOfferingPrice and Broadleaf’s PriceData is handled in multiple places. For 'read' operations, the mapping is handled by logic in JpaPriceDataProductOfferingPriceView 's ModelMapperMappable implementation to go from the Broadleaf representation to the TMF representation. For modification operations like creates and updates, ProductOfferingPriceMappingUtility is where the conversion from TMF representation to Broadleaf representation is defined.

  • Filtration and Sort

    • com.broadleafcommerce.tmforum.pricing.tmf620.web.attributefiltration.ProductOfferingPriceAttributeFiltersRequestResolver

      • Handles resolving the supported filtration parameters for requests against ProductOfferingPrice

    • com.broadleafcommerce.tmforum.pricing.tmf620.provider.jpa.filtering.JpaPriceDataProductOfferingPriceViewRsqlAndSortTransformer

      • Handles field transformations for filter and sort requests against ProductOfferingPrice

    • The following fields are supported for filtration/sort on ProductOfferingPrice

      • id

      • description

      • version

      • recurringChargePeriodType

      • recurringChargePeriodLength

      • name

      • priceType

      • lastUpdate

      • lifecycleStatus

      • validFor.startDateTime

      • validFor.endDateTime

      • unitOfMeasure.amount

      • unitOfMeasure.units

      • blcPriceListContextId

      • price.value

  • com.broadleafcommerce.tmforum.pricing.tmf620.service.validation.ProductOfferingPriceValidator

    • This component implements first-class validations on ProductOfferingPrice request payloads to ensure API consumers cannot create an invalid state.

  • com.broadleafcommerce.tmforum.pricing.tmf620.service.DefaultProductOfferingPriceService

    • This is the top-level service component responsible for handling management of ProductOfferingPrice. It orchestrates the main flow for CRUD operations, delegating specific responsibilities like validation and mapping to the other components as appropriate.

  • com.broadleafcommerce.tmforum.pricing.tmf620.service.apicontext.PricingCommonTMFApiContextKeys shows which fields can be defaulted via API Context attributes out-of-box

  • com.broadleafcommerce.tmforum.pricing.tmf620.jackson.autoconfigure.TMForumPricingJacksonAutoConfiguration

  • com.broadleafcommerce.tmforum.pricing.tmf620.web.endpoint.AuxiliaryProductOfferingPriceEndpoint

    • Exposes some additional proprietary 'auxiliary' APIs - most notably, an endpoint used internally by the ProductOffering mapping/hydration logic in CatalogServices to find the ProductOfferingPriceRef instances related to a ProductOffering

Strategy for ProductOfferingPrice

The TMForum ProductOfferingPrice resource conceptually maps to Broadleaf’s PriceData resource, the main difference being that Broadleaf aggregates ProductOfferingPrice instances for different price types into a single PriceData record.

In order to conform to the TMForum’s pagination, filtration, and sorting intent, Broadleaf employs a read-only database view (BLC_PRICE_DAT_PROD_OFFER_PRICE) to expand a single PriceData entry into multiple rows to achieve conformance with TMForum’s API.

The create/update/delete operations follow a tailored flow to preserve the external 'separate entity' abstraction. Internally, the flow will make determinations about whether the operation needs to result in a net-new PriceData record or if it should update an existing PriceData record with the requested state. For example, if a TMF request comes in to create a 'recurring' ProductOfferingPrice for SKU ABC123, and there is already a PriceData for ABC123 that does not have a 'recurring' price, the system may update that existing PriceData record instead of creating a net new one.

Below is a high level diagram describing the concept mapping strategies Broadleaf has employed to support this translation:

TMF 620 ProductOfferingPrice Mapping Strategy
Considerations for ProductOfferingPrice
  • The priceType is the primary field on which a PriceData is split into ProductOfferingPrice. The ProductOfferingPrice.id is built from a combination of the PriceData.id and a priceType.

  • TMF requires that the priceType value be PATCH-able. In Broadleaf, this creates a special case, since priceType is used to identify a ProductOfferingPrice in the first place. Semantically, mutation of the priceType is equivalent to 'deleting' the ProductOfferingPrice of the existing type and then creating a new ProductOfferingPrice for the requested type (potentially migrating the price for the existing type to the new type). In the response, the new ProductOfferingPrice.id will be returned so consumers can see the difference.

  • In Broadleaf, every PriceData must have a non-null 'once' (upfront) price. As a result, the system may automatically set this value to 0 in certain cases. For example, if there is a request to create a ProductOfferingPrice of type 'recurring', and it will result in a net new PriceData, the system will automatically set PriceData.price to 0. This implicitly results in the creation of a ProductOfferingPrice with type 'once'. Since the API caller did not explicitly request this creation, the create, patch, and delete operations in DefaultProductOfferingPriceService are flexible and will allow, for example, subsequent explicit create requests that attempt to overwrite this 0 value with something else.

  • While some fields on PriceData are only used by a particular priceType (ex: only the 'recurring' ProductOfferingPrice will deal with the PriceData.recurringPeriodType field), others like name are shared by all ProductOfferingPrice instances backed by the same PriceData. In the interest of remaining flexible instead of rejecting mutation attempts altogether, any update to such a 'shared' field will implicitly result in updating all ProductOfferingPrice instances backed by the same PriceData.

    • Note that blcPriceListContextId is intentionally not honored as part of ProductOfferingPrice update requests, as modification of this value would implicitly make a significant/fundamental change to all ProductOfferingPrice instances backed by the same PriceData. The same is true for mutation attempts on PriceData.target.

  • In TMF, ProductOfferingPrice itself doesn’t have any fields that would specifically associate it to a ProductOffering. Technically, a ProductOfferingPrice can be associated to 0 or more arbitrary offerings. However, in Broadleaf, this is not the case: you can’t have a PriceData without either a pricing key or a SKU - all PriceData must be associated to exactly one 'thing'. It could be that the 'thing' is a ProductOffering, or it could be a selection in a product option.

    • Broadleaf requires that each ProductOfferingPrice provides a ProductSpecificationCharacteristicValueUse with name == SKU or name == PRICING_KEY with a string value corresponding to the reference.

    • Broadleaf has reversed the TMF behavior for how the relationship is managed. Broadleaf doesn’t have any 'hard XREF' by ID between products and price data - it’s all decoupled soft-refs. Thus, management of ProductOfferingPrice from the ProductOffering.productOfferingPrice field is not allowed - it will be a 'read only' field hydrated at fetch time with all the prices (in all price lists) that are referencing that product/variant. An API caller may not remove/add entries from ProductOffering.productOfferingPrice - instead, they must update the references in the price entities directly via the ProductOfferingPrice API. As they are updated (ex: pricing key is changed, sku changed, price data deleted as a whole, etc), the ProductOffering.productOfferingPrice results themselves will naturally also reflect those additions/removals.

      Note
      An exception to the rule is that ProductOffering.productOfferingPrice will also contain values from Product.defaultPrice/Product.salePrice/etc. These inline fields will not be 'refs' and instead be full inline ProductOfferingPrice values. These can be managed from this field.

ProductSpecification

The core Broadleaf concept that underpins ProductSpecification is BusinessType (AKA 'Product Type').

Key Components

Broadleaf’s TMForum Extensions library provides several Spring utility components that are responsible for assisting in conversions between TMForum Concepts and Broadleaf Concepts. The following components can be extended to support a different mapping strategy if needed.

  • com.broadleafcommerce.tmforum.catalog.tmf620.service.mapping.ProductSpecificationMappingUtility

    • The conversion logic between ProductSpecification and Broadleaf’s BusinessType is handled centrally here.

    • Note that this component also delegates to com.broadleafcommerce.tmforum.catalog.tmf620.service.mapping.CharacteristicValueSpecificationMappingHandler beans to handle mapping of characteristics

  • Filtration and Sort

    • com.broadleafcommerce.tmforum.catalog.tmf620.web.attributefiltration.ProductSpecificationAttributeFiltersRequestResolver

      • Handles resolving the supported filtration parameters for requests against ProductSpecification

    • com.broadleafcommerce.tmforum.catalog.tmf620.service.filtering.ProductSpecificationBusinessTypeFilterAndSortTransformer

      • Handles field transformations for filter and sort requests against ProductSpecification

    • The following fields are supported for filtration/sort on ProductSpecification

      • id

      • description

      • lastUpdate

      • lifecycleStatus

      • version

      • name

      • blcTypeKey

  • com.broadleafcommerce.tmforum.catalog.tmf620.service.validation.ProductSpecificationValidator

    • This component implements first-class validations on ProductSpecification request payloads to ensure API consumers cannot create an invalid state.

    • Delegates to com.broadleafcommerce.tmforum.catalog.tmf620.service.validation.characteristic.CharacteristicSpecificationValidationHelper for validations on characteristics

  • com.broadleafcommerce.tmforum.catalog.tmf620.service.DefaultProductSpecificationService

    • This is the top-level service component responsible for handling management of ProductSpecification. It orchestrates the main flow for CRUD operations, delegating specific responsibilities like validation and mapping to the other components as appropriate.

  • com.broadleafcommerce.tmforum.catalog.tmf620.service.hydration.ProductSpecificationMappingHydrationService

    • Responsible for collecting additional data/entities that are required for successfully mapping between ProductSpecification and BLC BusinessType

  • com.broadleafcommerce.tmforum.catalog.tmf620.jackson.autoconfigure.TMForumCatalogJacksonAutoConfiguration

Strategy for ProductSpecification

The TMForum ProductSpecification resource conceptually maps to Broadleaf’s BusinessType resource, with a fairly straightforward 1:1 mapping between the two.

The only meaningful difference is that in Broadleaf, Characteristic entities are a top-level, independently managed resource, while in TMF they are anonymous inlined values. This distinction typically doesn’t affect TMF API consumers, but it is important to keep in mind.

Below is a high level diagram describing the concept mapping strategies Broadleaf has employed to support this translation:

TMF 620 ProductSpecification Mapping Strategy
Considerations for ProductSpecification
  • In Broadleaf, a product’s 'type' can either be a default type (one of the values in com.broadleafcommerce.catalog.domain.product.DefaultProductType), or it can be a BusinessType. To represent the 'default' types, the system will manufacture a synthetic BusinessType with a well-known ID (see com.broadleafcommerce.catalog.service.product.businessType.DefaultProductTypeBusinessTypeBuilder), map it to a ProductSpecification, and implicitly return them in GET /productSpecification/{id} if requested. This way, products that don’t explicitly inherit from a persisted BusinessType can still be represented as referencing a particular ProductSpecification, and its href can actually be resolved by the API if needed.

  • The value for BusinessType.productType can be automatically determined by the system or explicitly provided by the API caller

    • See ProductSpecificationValidator.validateProductSpecificationRelationshipsForCreate and DefaultProductSpecificationRelationshipTypeValues.INHERITS_FROM for more details on how to explicitly specify this value

    • See ProductSpecificationMappingUtility.determineDefaultProductType for more details on how the system will automatically calculate this value

  • Because Characteristic is a separate domain from BusinessType in Broadleaf, CRUD operations on ProductSpecification will also result in creation/deletion of Characteristic instances to match the caller’s request.

    • Please review the Javadocs of ProductSpecificationValidator.validateCharacteristicsForCreate and ProductSpecificationValidator.validateCharacteristicsForUpdate for a thorough description of expected behavior

    • Please review DefaultProductSpecificationService.handleOrphanedCharacteristicsAfterFailedBusinessTypeCreate and DefaultProductSpecificationService.handleOrphanedCharacteristicsAfterFailedBusinessTypeUpdate for behavior engaged when Characteristic persistence succeeds, but the persistence for the BusinessType fails

ProductOffering

The core Broadleaf concept that underpins ProductOffering is Product or Variant (depending on the type of product).

Key Components

Broadleaf’s TMForum Extensions library provides several Spring utility components that are responsible for assisting in conversions between TMForum Concepts and Broadleaf Concepts. The following components can be extended to support a different mapping strategy if needed.

  • com.broadleafcommerce.tmforum.catalog.tmf620.service.mapping.ProductOfferingMappingUtility

    • The conversion logic between ProductOffering and Broadleaf’s Product/Variant is handled in multiple places.

      • For 'read' operations, the mapping is handled by logic in JpaProductOfferingView 's ModelMapperMappable implementation to go from the JpaProductOfferingView representation to a lightweight ProductOfferingViewDTO representation. Then, ProductOfferingMappingUtility transforms the ProductOfferingViewDTO into a ProductOffering.

      • For modification operations like creates and updates, ProductOfferingMappingUtility defines the conversion from TMF ProductOffering to Broadleaf Product/Variant.

    • Note that this component also delegates to com.broadleafcommerce.tmforum.catalog.tmf620.service.mapping.CharacteristicValueSpecificationMappingHandler beans to handle mapping of characteristics

  • com.broadleafcommerce.tmforum.catalog.tmf620.service.hydration.ProductOfferingMappingHydrationService

    • Responsible for collecting additional data/entities that are required for successfully mapping between ProductOffering and BLC Product/Variant

  • Filtration and Sort

    • com.broadleafcommerce.tmforum.catalog.tmf620.web.attributefiltration.ProductOfferingAttributeFiltersRequestResolver

      • Handles resolving the supported filtration parameters for requests against ProductOffering

    • com.broadleafcommerce.tmforum.catalog.tmf620.provider.jpa.filtering.JpaProductOfferingViewRsqlAndSortTransformer

      • Handles field transformations for filter and sort requests against ProductOffering

    • The following fields are supported for filtration/sort on ProductOffering

      • isBundle

      • statusReason

      • lifecycleStatus

      • lastUpdate

      • name

      • description

      • isSellable

      • id

      • version

      • validFor.startDateTime

      • validFor.endDateTime

      • blcProductOfferingSourceEntityType

  • com.broadleafcommerce.tmforum.catalog.tmf620.service.validation.ProductOfferingValidator

    • This component implements first-class validations on ProductOffering request payloads to ensure API consumers cannot create an invalid state.

    • Delegates to com.broadleafcommerce.tmforum.catalog.tmf620.service.validation.characteristic.CharacteristicSpecificationValidationHelper for validations on characteristics

  • com.broadleafcommerce.tmforum.catalog.tmf620.service.DefaultProductOfferingService

    • This is the top-level service component responsible for handling management of ProductOffering. It orchestrates the main flow for CRUD operations, delegating specific responsibilities like validation and mapping to the other components as appropriate.

  • com.broadleafcommerce.tmforum.catalog.tmf620.jackson.autoconfigure.TMForumCatalogJacksonAutoConfiguration

  • com.broadleafcommerce.tmforum.catalog.tmf620.service.apicontext.CatalogCommonTMFApiContextKeys shows which fields can be defaulted via API Context attributes out-of-box

  • com.broadleafcommerce.tmforum.catalog.tmf620.service.autoconfigure.ExternalPricingProviderProperties

    • Defines configuration properties under broadleaf.tmforum.catalog.pricingprovider.*, which are relevant to configuring how CatalogServices will make a request to PricingServices to hydrate ProductOfferingPriceRefs for each ProductOffering

  • com.broadleafcommerce.tmforum.catalog.tmf620.service.autoconfigure.ProductOfferingHydrationProperties

    • Defines configuration properties under broadleaf.tmforum.catalog.product-offering.hydration.*, which are relevant to whether hydration of ProductOfferingPriceRefs from PricingServices should occur when mapping ProductOffering.

Strategy for ProductOffering

The TMForum ProductOffering resource conceptually maps to Broadleaf’s Product and Variant resources.

In order to conform to TMForum’s pagination, filtration, and sorting intent, Broadleaf employs a read-only database view (BLC_PRODUCT_OFFERING_VIEW) to consolidate records from these different tables into one result set to achieve conformance with TMForum’s API.

The create/update/delete operations follow a tailored flow to preserve the external abstraction of a consolidated entity. Internally, the flow will make determinations about whether the operation needs to target a Product or Variant and apply updates accordingly.

Below is a high level diagram describing the concept mapping strategies Broadleaf has employed to support this translation:

TMF 620 ProductOffering Mapping Strategy
Considerations for ProductOffering
  • Broadleaf’s Variant concept is a nice feature to more easily reason about products and how they are surfaced. However, in TMF, there is not enough information available to create Broadleaf’s Variant directly. Thus, while they can be fetched in 'read' operations and some updates are supported, a net new create of a Broadleaf Variant is not possible from the TMF API.

  • In Broadleaf, a product’s 'product type' or BusinessType is immutable after creation. This relationship is represented via the ProductOffering.productSpecification field in TMF, and therefore attempts to change this after creation are not supported.

  • ProductOffering.productSpecification must always have a value set during creates, as knowledge of what the Product.productType should be is critical to ensure mapping is performed correctly.

    • As discussed in the Considerations for ProductSpecification documentation, this can be set to the ID of one of the 'synthetic' BusinessType instances corresponding to a DefaultProductType - it does not necessarily have to point to a separately persisted BusinessType ID.

    • See ProductOfferingValidator.validateProductSpecificationRefForCreate for more details on this validation.

  • Please see the Considerations for ProductOfferingPrice section for details on associations between ProductOfferingPrice and ProductOffering

  • On ProductOffering reads, the ProductOffering.prodSpecCharValueUse field will only include the characteristics that have been explicitly assigned/overridden in Product.characteristics. If a characteristic from the parent BusinessType is not explicitly present in Product.characteristics, it will not be returned in the ProductOffering.prodSpecCharValueUse field. This follows TMF expectations where the caller is expected to get the parent ProductSpecification to fetch all the non-overridden characteristics.

    • Please see ProductOfferingMappingUtility.transformProductCharsToProdSpecCharValUses for more details

  • Certain fields from ProductOffering are persisted in the attributes column of JpaProduct and JpaVariant. These are enumerated in com.broadleafcommerce.tmforum.catalog.tmf620.service.mapping.CommonProductOfferingSourceEntityAttributeKeys. There is logic in JpaProductOfferingViewRsqlAndSortTransformer to support filtration against these values. However, clients using Oracle will not be able to filter on these fields. This is because on Oracle, the attributes column for JpaProduct/JpaVariant exceeds the size threshold for VARCHAR and has a datatype of CLOB (which cannot be queried against).