Broadleaf Microservices
  • v1.0.0-latest-prod

Customer Segment Guide

Overview

Customer Segments provide the ability to support fixed and dynamic customer segments. Segments enables the ability to create rule or list based groups of Customers that can be targeted for Offers, PriceLists, or ContentTargeters. This allows the creation or promotions or pricing that targets a precise subset of a customer base.

There are 3 primary types of segments

  • Customer Set: A manually curated list of customers.

  • Rule-Based: A dynamic list of customers.

  • External: A reference to an externally defined segment.

Customer Set

Represents a manually curated list of Customers that belong to a segment. The id of the segments a customer is added to is synced to Auth in order to be added as a claim on the auth token and made available to various microservices when the user logs in.

The SegmentMemberPersistenceHandler in auth handles the event when a customer is added and adds the new segment ID to the customer_segment_ids attribute on the User entity. The CustomerSegmentsTokenEnhancer then adds that attribute as a claim to the auth token with the same name so that it is accessible to other microservices. Claims can be accessed using the AuthenticationUtils from the Security Common library or from the Spring SecurityContext.

Rule-Based

Represents a generated list of Customers who are part of the Segment based on some rule such as email, username, phone, is registered, etc. There are two sub-types of rule-based segments determined by when the rule is evaluated.

  • Offline: The rule is evaluated offline in an asynchronous process.

  • Real-Time: The rule is evaluated in real-time for incoming commerce requests against the user and request context.

Offline Evaluation

When the segment uses offline evaluation, the rule is evaluated offline in an asynchronous process. This essentially produces a customer set based on a rule and requires the Customers to exist in Broadleaf’s system.

The match rule will use RSQL format and be run against the database. The id of the segments a customer is added to is synced to Auth in order to be added as a claim on the auth token and made available to various microservices when the user logs in.

Processing of these rules is handled by using the Bulk Operations common library using the DefaultRuleBasedSegmentBulkProcessor.

Real-Time Evaluation (since 1.8.6)

Rule-based segments can be configured to be evaluated in real time against the current user and request context rather than just offline in an asynchronous process. For these, the match rule will use Spring Expression Language (SpEL) format and be run against a request object that consolidates various information about the current user and the request, see ResolveCustomerSegmentsRequest for more information.

Typical Usage

Typically, this type of segment is evaluated in the CustomerSegmentContextInfoCustomizer in an orchestration service, which then hydrates the matching segment ids on to the ContextInfo. The customizer will make a request to Customer Service to read all of the real-time, rule-based customer segments and cache them in the calling service, which will also evaluate their rules rather than sending another request to evaluate them in Customer Services to allow more efficient caching.

To aid in that process, we have introduced the Customer Client common library to Cart Operations and Catalog Browse Services that contains common components.

Targeting Custom Request Parameters

It is common to want to target custom request parameters with Customer Segments. Out of box, no request parameters are used, but targeting them requires little customization.

Let’s say you are passing a special parameter in the request URL such as a campaign tracking code or geographic parameters. In order to have a Customer Segment target these parameters you would need to do the following:

  1. Set the following in the respective flexpackages for Catalog Browse and Cart Ops to whitelist the custom parameters to include in the context object against which rules are evaluated:

    broadleaf:
      cartoperation:
        context:
          customer-segment-customizer:
            request-attribute-list:
              - campaign-code
      catalogbrowse:
        context:
          customer-segment-customizer:
            request-attribute-list:
              - campaign-code
  2. Then update the metadata for Rule-Based Segments' match rule:

    import org.springframework.boot.autoconfigure.AutoConfigureAfter;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import com.broadleafcommerce.customer.metadata.autoconfigure.CustomerServicesMetadataAutoConfiguration;
    import com.broadleafcommerce.customer.metadata.segments.support.CustomerSegmentGroups;
    import com.broadleafcommerce.customer.metadata.segments.support.CustomerSegmentIds;
    import com.broadleafcommerce.customer.metadata.segments.support.CustomerSegmentProps;
    import com.broadleafcommerce.metadata.dsl.core.extension.fields.DynamicField;
    import com.broadleafcommerce.metadata.dsl.core.extension.fields.RuleBuilderField;
    import com.broadleafcommerce.metadata.dsl.core.extension.views.details.EntityFormView;
    import com.broadleafcommerce.metadata.dsl.core.extension.views.details.EntityView;
    import com.broadleafcommerce.metadata.dsl.core.utils.Fields;
    import com.broadleafcommerce.metadata.dsl.registry.ComponentSource;
    
    import lombok.RequiredArgsConstructor;
    import lombok.experimental.UtilityClass;
    
    @Configuration
    @RequiredArgsConstructor
    @AutoConfigureAfter(CustomerServicesMetadataAutoConfiguration.class)
    public class DemoCustomerMetadataAutoConfiguration {
    
        @UtilityClass
        public class DemoCustomerProps {
            public final String CAMPAIGN_CODE = "attributes[campaign-code]";
        }
    
        @Bean
        public ComponentSource customizeCustomerMetadata() {
            return registry -> {
                EntityView<?> create = (EntityView<?>) registry.get(CustomerSegmentIds.CREATE);
                addCampaignCodeTargeting(create);
                EntityView<?> update = (EntityView<?>) registry.get(CustomerSegmentIds.UPDATE);
                addCampaignCodeTargeting(update);
            };
        }
    
        private void addCampaignCodeTargeting(EntityView<?> view) {
            EntityFormView<?> form = view.getGeneralForm();
            form.findGroup(CustomerSegmentGroups.MATCH_RULES)
                    .flatMap(group -> group.findField(CustomerSegmentProps.SEGMENT_MATCH_RULE))
                    .map(matchRule -> (DynamicField<?>) matchRule)
                    .ifPresent(matchRule -> matchRule.configureFields(fields -> {
                        fields.stream()
                                .filter(field -> field instanceof RuleBuilderField)
                                .findFirst()
                                .map(dynamicRules -> (RuleBuilderField<?>) dynamicRules)
                                .ifPresent(dynamicRules -> {
                                    dynamicRules.addField(DemoCustomerProps.CAMPAIGN_CODE, Fields.string()
                                            .label("Campaign Tracking Code")
                                            .order(6000));
                                });
                        return fields;
                    }));
        }
    }

Customer Segment Resolver Endpoint

Customer Service also has an endpoint (POST /segment-resolver) that can be used to resolve customer segments matching a request and user context in case it is not possible to do so in an orchestration service. This may be the case if the orchestration service does not have enough information about the user to evaluate all the rules such as a customer’s home address. However, this is generally discouraged since it is harder to cache the results of these requests given the complexity of the cache keys. Instead, most information should be provided either on the Auth token as a claim (it already includes user’s email, username, name, account, phone number, and some other information) or in a request parameter.

External (since 1.8.6)

Represents a segment that is a placeholder for one in an external system. This is useful for allowing other Broadleaf concepts like Offers and Price Lists to reference externally defined segments.

Using this type requires additional customization in order to contact the external system and determine if the current user and request context matches any external customer segments. The approach to accomplish this can be similar to what was used to support Real-time, Rule-based Segments: Building a ContextInfoCustomizer to send a request to the external system with request data and hydrate the matching external segments onto the ContextInfo. Also see CustomerSegmentContextInfoCustomizer.