Skip to main content
Version: 8.3.x

Service Layer

Overview​

The service layer provides services to various consumers in the web layer as well as web service consumers. There are several types of services that serve different roles in the application.

Persistence Services​

Persistence Services provide the capability to save and retrieve domain objects. Persistence Services extend from AbstractEpPersistenceServiceImpl and offer methods for adding new domain objects, retrieving domain objects by their identifier, and searching for domain objects with specific criteria. PersistenceServices are named XService where X is the class name of the objects that it can save and retrieve.

Domain Services​

Domain services typically implement the logic for a use case that is inappropriate for encapsulation by any one domain object. For example, a service that performs a checkout will contain logic for the flow of the interaction between several domain objects. This domain service logic is typically at a higher level of abstraction than the fine-grained domain logic that is implemented by an individual domain object.

Domain services will often use other services in combination with domain object logic to accomplish a task. For example, the checkout service uses domain logic to check for sufficient inventory while using the InventoryService persistence service to persist inventory levels.

note

In Elastic Path, domain service logic specific to a particular domain object is often implemented within the Persistence Service for that object.

Integration Services​

Integration services implement functionality that is invoked by domain services but considered outside the domain of an ecommerce application and typically integrate with other systems or technologies. The following are examples of integration services.

  • EmailService - Sends email on behalf of other services
  • CustomerIndexBuildService - Constructs a search index used by the Lucene search feature
  • BirtReportService - Provides access to the BIRT (Business Intelligence Reporting Tool) reporting engine

System Services​

System services handle various concerns that cut across many parts of the application. These services are typically provided by Spring and configured in Spring configuration files. Examples of System services include object lifecycle management, caching, transactions, security, and scheduling.

Web Services​

Web services expose service layer functionality to web services clients. These services are ultimately delivered to external systems via SOAP (Simple Object Access Protocol).

Catalog search allows a customer using the front end or a sales representative using the Commerce Manager to find products by keywords. At configured intervals, keywords are extracted from each product in the catalog and stored in a Solr index for fast searching.

Key classes and files​

  • SearchRequestImpl - A search request representation
  • SearchResultImpl - A search result representation
  • SearchServiceImpl - A service for searching the product catalog
  • ProductCategorySearchCriteria - A product search criteria representation
  • ProductQueryComposerImpl - A Solr query composer for products
  • SolrIndexSearchServiceImpl - A service for dispatching search requests to the SolrIndexSearcher
  • SolrIndexSearchResultImpl - A Solr result representation
  • SolrIndexSearcherImpl - A searcher that can read Lucene-based indexes

How it works​

  1. A user inputs a string of keywords, and possibly a category restriction, into the search fields and clicks search.

    This invokes the SearchControllerImpl handleRequestInternal() method.

  2. SearchControllerImpl creates a SearchRequest object.

  3. SearchControllerImpl calls the SearchService with the following input:

    • The SearchRequest
    • The user’s ShoppingCart, which is used to apply promotion rules
    • The ProductLoadTuner, which determines how much of a product’s data is loaded
  4. SearchService checks the COMMERCE/STORE/SEARCH/searchCategoriesFirst system setting whether it should search for categories before products.

    If SearchService finds a category, the search stops and returns the category page.

  5. If no categories are found, SearchService creates a ProductCategorySearchCriteria from the SearchRequest.

  6. SearchService dispatches the ProductCategorySearchCriteria to the injected SolrIndexSearchService, which returns an empty SolrIndexSearchResult that has been injected with a SolrIndexSearcher.

  7. SearchService applies pagination to the SolrIndexSearchResult and delegates to the SolrIndexSearcher.

  8. SolrIndexSearcher converts the ProductCategorySearchCriteria into a ProductQueryComposer.

  9. SolrIndexSearcher uses the ProductQueryComposer to generate a Solr query and runs a search.

    If no results are found, SolrIndexSearcher uses the ProductQueryComposer to generate a Fuzzy query and runs a Fuzzy search.

  10. SearchService regains control with the populated SolrIndexSearchResult.

  11. If not enough results are returned, SearchService attempts to generate suggestions, as described by Search Suggestions.

  12. SearchService returns the results to the SearchController along with any suggestions.

note

By default, the data included in each object in the result set is determined by the productLoadTunerForBrowsingAndSearch. For more information, see Load tuners.

Lucene includes functionality for performing fuzzy searches. A fuzzy search searches for terms that are similar but not exactly equal to the terms specified by the user. Elastic Path uses fuzzy search functionality to look for possible results when exact matches are not found.

Fuzzy search uses the Search Settings system setting.

The minimum similarity value is a value between zero and one that sets the required similarity between the query term and the matching terms. For example, if COMMERCE/SEARCH/minimumSimilarity is set to 0.5:

  • A term of the same length as the query term is considered similar to the query term if the edit distance between both terms is less than length(term)*0.5.

The edit distance is a measure of similarity between two strings and distance is measured as the number of character deletions, insertions, or substitutions required to transform one string to the other string.

Another example, the edit distance between "kitten" and "sitting" is 3, since these three edits change one into the other, and there is no way to do it with fewer than three edits:

  1. Kitten ? Sitten (substitution of ‛k’ for ‛s’)
  2. Sitten ? Sittin (substitution of ‛e’ for ‛i’)
  3. Sittin ? Sitting (insert ‛g’ at the end)

Key classes and files​

  • SolrIndexSearcher

    A data access object for Lucene searching. This class contains logic for executing fuzzy searches when no exact matches are found.

  • ProductQueryComposer

    Implements a method, composeFuzzyQuery(..), that creates a new FuzzyQuery Lucene object. The object is populated with the search terms and configuration options specified in the search settings

Search Suggestions​

Search suggestions enable the user to select from one or more alternate search phrases if their original search phrase returned no results. The alternate search phrases attempt to correct any potential typos or misspellings that the original search phrase contains.

Key classes and files​

  • SolrSpellIndexSearcherImpl

    Parses a supplied query, and checks the spelling index to see if there are any suggestions for terms in that query.

  • SolrIndexSearchServiceImpl

    A service for dispatching search requests to the SolrIndexSearcher.

  • ProductIndexBuilder

    Creates a spelling index using the product index’s content field.

  • SearchServiceImpl

    A service for searching the product catalog

Configuration​

To configure Fuzzy Search and Search Suggestions, see the Search Settings page.

QueryService​

The QueryService interface simplifies the process for creating new domain object queries. QueryService utilizes the Query Object Pattern, to enable users to generate customized queries without having to understand the details of the domain object database tables. QueryService automatically generates the Java Persistence Query Language (JPQL) required to access domain objects. QueryService builds the JPQL query from QueryCriteria objects that are mapped to domain object properties, so all a user needs to know are the properties of the domain object they are querying.

QueryService Components​

A call to QueryService has the following syntax:

QueryResult<R> queryResult = queryServiceImpl<T>.query(QueryCriteria<T> criteria);

Three main components to the QueryService:

  • QueryResult
  • QueryService implementation
  • QueryCriteria

QueryResult​

The QueryResult interface provides the methods for getting results from the query.

The generic type <R> depends on the type of result returned by the query. The QueryService interface requires you to specify the result’s type. For more information on specifying the type, see Building a Query with QueryService.

QueryService Implementation​

Each domain object has its own QueryService implementation. Out of the box, Elastic Path has the following QueryService implementations:

Domain ObjectQueryService Implementation
ContentSpaceContentSpaceQueryService
ParameterValueParameterValueQueryService
ProductProductQueryService

QueryCriteria​

The QueryCriteria specifies the statements the query will perform. Refer to Building a Query with QueryService for more details.

Building Query with QueryService​

QueryService saves you from having to create statements to query the database. By creating QueryCriteria objects, you can query the database without needing to know the database schema. The QueryCriteria object is built by a CriteriaBuilder and is automatically converted into JPQL and executed by the QueryService.

Below is a query example that returns products with a particular category UID:

QueryResult<Product> queryResult = queryService.query(
CriteriaBuilder.criteriaFor(Product.class)
.with(CategoryRelation.having().uids(categoryUid))
.usingLoadTuner(loadTuner)
.returning(ResultType.ENTITY)
);

QueryCriteria objects have five different components, as shown in the sample above:

Specify the Domain Class​

To create a QueryCriteria object, you need to call the CriteriaBuilder static criteriaFor() method.

The QueryCriteria first component specifies the domain class. This domain class must match the generic type of the QueryService implementation to properly generate the JPQL query.

For example, ProductQueryService, which implements the QueryService<Product> interface, needs Product QueryCriteria to create Product queries.

Define the Query Relations​

To filter query results against particular properties, you can define Relations.

The QueryCriteria second component is optional and specifies the properties and values searched by the query. The CriteriaBuilder with() method takes in a Relation object, which identifies a property and value. Relations are explained in further detail in QueryService Relations. You can chain multiple with() statements together to refine your queries.

In the example with() statement, the CategoryRelation object is used to query for Products with a specific category UID.

Define the Date Range​

To filter query results within a specific time frame, you can use either the CriteriaBuilder inDateRange() method or modifiedAfter() method.

The QueryCriteria third component is optional and restricts the results returned by date. The modifiedAfter() method takes in a single Date and returns results that were modified after the given date. The inDateRange() method takes in a start Date and an end Date and returns results that were modified within the given time frame.

Specify a Load Tuner​

To control the amount of associated object data that is retrieved from the database, you can specify a load tuner with CriteriaBuilder``usingLoadTuner() method.

The QueryCriteria fourth component is optional. By using a load tuner, you avoid retrieving large object graphs when you only need a subset of the data. For example, a product has a brand, category, images, and skus. If you are querying products for brand, then you can use a load tuner to avoid retrieving product images.

note

To use a load tuner in QueryService, you must define the load tuner bean in the Elastic Path core. You can define domain level load tuners in ep-core/src/main/resources/spring/service/service.xml and persistence level load tuners in ep-core/src/main/resources/spring/dataaccess/dao.xml.

Query Return Type​

To specify the type of results the query returns, you need to call the CriteriaBuilder returning() method.

The QueryCriteria fifth component specifies the query ResultType. Out-of-the-box, Elastic Path supports the following result types:

ResultTypeQueryResult Generic TypeDescription
ResultType.ENTITYThe domain class. (eg. Product)Returns the entity objects. For example, a Product query with ResultType.ENTITY returns Product objects.
ResultType.UIDlongReturns the objects’ uidPk.
ResultType.GUIDStringReturns the objects’ GUID.
ResultType.CONDITIONALbooleanReturns a boolean result, indicating the queried object exists.
ResultType.DATE_RESULTDateReturns a date result.

When building a query, you must specify the generic type of the QueryResult as results are returned in a List. This generic type depends on the ResultType returned by the query. Queries return an empty list if no results are found.

QueryService Relations​

A QueryService Relation defines a domain object’s query relationships. Relations encapsulate the knowledge QueryService requires for creating JPQL queries, such as column names, aliases, and how joins are handled.

Using Relations​

When building the QueryCriteria, you can use Relations to identify the properties and values filtered by the query. For example, you use a Relation to query for Products with a specific Product UID:

CriteriaBuilder.criteriaFor(Product.class)
.with(ProductRelation.having().uids(productUid))
.returning(ResultType.ENTITY)

Relations are inserted into the QueryCriteria with the CriteriaBuilder with() method. You can insert multiple Relations into the QueryCriteria by chaining with() methods. For example, the following query looks for Products with a specific Brand UID and Category UID:

CriteriaBuilder.criteriaFor(Product.class)
.with(CategoryRelation.having().uids(categoryUid))
.with(BrandRelation.having().uids(brandUid))
.returning(ResultType.ENTITY)

QueryService requires a valid Relation between the object you are querying and the object encapsulated by the Relation. A Relation is valid if the object encapsulated by the Relation is a property of the object being queried.

For example, a BrandRelation is valid when you query a Product since Product has a Brand property. However, a ContentSpaceRelation is invalid since Product and ContentSpace are unrelated.

Building a Relation​

Creating the Relation object​

To create a relation object, you need to call the Relation class’ static having() method.

Identifier Methods​

To specify the object properties a query looks up, you call a Relation’s identifier methods. Each identifier method maps to a property of the Relation’s domain object and provides information on how to query the property.

Out of the box, Elastic Path Relation classes support four identifier methods for querying object properties:

PropertyIdentifier Method
UIDuids(Long)
GUIDguids(String)
Codecodes(String)
Namenames(String)

For example, to query for a Store with a specific name, use the StoreRelation names() identifier method:

StoreRelation.having().names("SnapItUp")