Broadleaf Microservices
  • v1.0.0-latest-prod

Money

Overview

Broadleaf makes significant use of Monetary concepts that involve scale, rounding, serialization, and deserialization. As a result, Broadleaf makes use of the javax.money.MonetaryAmount interface to assist with these details, which can be inconsistent and complicated when using raw Number instances such as float, double, or BigDecimal.

Components

Important components to review include:

  • javax.money.MonetaryAmount

    • A high-level interface that defines Money, including Currency and Amount

  • javax.money.CurrencyUnit

    • A high-level interface that defines Currency

  • javax.money.CurrencySupplier

    • A functional interface that returns a javax.money.CurrencyUnit

  • com.broadleafcommerce.money.CurrencyConsumer

    • A functional interface that provides a setter for javax.money.CurrencyUnit. Specifically, components that require Currency to construct a MonetaryAmount

  • com.broadleafcommerce.money.util.MonetaryUtils

    • A collection of utility methods for creating, transforming, and rounding money

  • com.broadleafcommerce.data.tracking.core.mapping.money.CurrentCurrencyUtils

    • A utility to allow for the resolution of preferred currency for a given object

  • com.broadleafcommerce.money.jackson.BroadleafMoneyModule

    • A Jackson extension module that provides components for serializing or deserializing Money

Default Currency

The default system currency is defined by the default Locale of the host operating system. If no default Locale is specified, USD is used as the default system currency.

Additionally, Broadleaf’s Catalogs and Applications each have a default currency. In certain flows that involve currency (e.g. Catalog and Cart), a component can be invoked to set the default currency for the request. These components implement com.broadleafcommerce.data.tracking.core.context.ContextInfoCustomizer. For example:

  • com.broadleafcommerce.catalog.service.context.CatalogCurrencyContextInfoCustomizer

  • com.broadleafcommerce.cart.service.context.CartDefaultCurrencyContextInfoCustomizer

The job of these components is to set the default currency on the ContextInfo for the current request. These components attempt to resolve the currency based on the current Catalog or Application, depending on the flow. If no currency can be found, then the default system currency is used.

These components can can be overridden to change their behavior, if needed.

It is sometimes useful to override the default currency in a specific request scoped process flow. The following code illustrates how to achieve this.

// Example process to set and restore the default currency.
try {
    SystemCurrencyContextHolder.setProcessCurrency(currencyUnit);
} finally {
    SystemCurrencyContextHolder.clearProcessCurrency();
}

Resolving Currency

ContextInfo#getDefaultCurrency() can be used to determine the default currency for the request. If that value is null, MonetaryUtils#defaultCurrency() can be used to determine the default system currency.

Additionally, certain objects such as Cart and Product have a currency field on them. If the currency field is populated, then that currency is used for operations that involve that object and its children.

Use com.broadleafcommerce.data.tracking.core.mapping.money.CurrentCurrencyUtils to resolve the current currency in any situation. The resolveCurrency method takes in an object and a ContextInfo. If the object is not null and is a CurrencySupplier and its currency is not null, then it’s used. Otherwise, if the ContextInfo is not null and its default currency is not null, then it’s used instead. Finally, if no other currency could be determined then the system currency is used.

Persistence

With RDBMS (JPA / Hibernate), data is stored in relational database tables. Some of the columns hold JSON data that is serialized and deserialized by Hibernate.

High-level objects such as Cart and Product contain a persistent currency field. If their children are JPA entities and have monetary concerns (i.e. CartItem) then they contain a Transient field for currency and amount fields are represented as BigDecimal. These objects have mutators (getter/setters) for MonetaryAmount that translates to/from BigDecimal using the currency on the object. The transient currency is set prior to mapping between the business (projection) domain and the persistent domain. This happens in a preFromMe and preToMe method, where the currency is determined and set on the child objects.

For JSON representations that are stored in RDBMS fields (e.g. FulfillmentGroup), they are stored as JSON MonetaryAmount fields, which means that they contain a serialized amount and currency.

Solr

Broadleaf does not currently make use of Solr’s Currency functionality due to the fact that it requires a managed list of currencies and exchange rates. Rather, Broadleaf stores Money in 2 Solr fields defined by the following dynamic fields:

  • *_money (Double)

    • Contains the amount

  • *_currency (String)

    • Contains the currency code

This allows us to uniquely identify each of these necessary pieces of data on a Solr document and construct a MonetaryAmount instance based on the values.

Serialization

In order to properly serialize or deserialize MonetaryAmount fields, the Jackson ObjectMapper must have the BroadleafMoneyModule registered.