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 servicesCustomerIndexBuildService
- Constructs a search index used by the Lucene search featureBirtReportService
- 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 lifecyle 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
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 representationSearchResultImpl
- A search result representationSearchServiceImpl
- A service for searching the product catalogProductCategorySearchCriteria
- A product search criteria representationProductQueryComposerImpl
- A Solr query composer for productsSolrIndexSearchServiceImpl
- A service for dispatching search requests to theSolrIndexSearcher
SolrIndexSearchResultImpl
- A Solr result representationSolrIndexSearcherImpl
- A searcher that can read Lucene-based indexes
How it works
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.SearchControllerImpl
creates aSearchRequest
object.SearchControllerImpl
calls theSearchService
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
- The
SearchService
checks theCOMMERCE/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.If no categories are found,
SearchService
creates aProductCategorySearchCriteria
from theSearchRequest
.SearchService
dispatches theProductCategorySearchCriteria
to the injectedSolrIndexSearchService
, which returns an emptySolrIndexSearchResult
that has been injected with aSolrIndexSearcher
.SearchService
applies pagination to theSolrIndexSearchResult
and delegates to theSolrIndexSearcher
.SolrIndexSearcher
converts theProductCategorySearchCriteria
into aProductQueryComposer
.SolrIndexSearcher
uses theProductQueryComposer
to generate a Solr query and runs a search.If no results are found,
SolrIndexSearcher
uses theProductQueryComposer
to generate a Fuzzy query and runs a Fuzzy search.SearchService
regains control with the populatedSolrIndexSearchResult
.If not enough results are returned,
SearchService
attempts to generate suggestions, as described by Search Suggestions.SearchService
returns the results to theSearchController
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.
Fuzzy Search
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:
- Kitten ? Sitten (substitution of ‛k’ for ‛s’)
- Sitten ? Sittin (substitution of ‛e’ for ‛i’)
- 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 newFuzzyQuery
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
implementationQueryCriteria
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 Object | QueryService Implementation |
---|---|
ContentSpace | ContentSpaceQueryService |
ParameterValue | ParameterValueQueryService |
Product | ProductQueryService |
QueryCriteria
The QueryCriteria
specifies the statements the query will perform. Refer to Building a Query with QueryService
for more details.
QueryService
Building Query with 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:
ResultType | QueryResult Generic Type | Description |
---|---|---|
ResultType.ENTITY | The domain class. (eg. Product) | Returns the entity objects. For example, a Product query with ResultType.ENTITY returns Product objects. |
ResultType.UID | long | Returns the objects’ uidPk. |
ResultType.GUID | String | Returns the objects’ GUID. |
ResultType.CONDITIONAL | boolean | Returns a boolean result, indicating the queried object exists. |
ResultType.DATE_RESULT | Date | Returns 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 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:
Property | Identifier Method |
---|---|
UID | uids(Long) |
GUID | guids(String) |
Code | codes(String) |
Name | names(String) |
For example, to query for a Store with a specific name, use the StoreRelation
names()
identifier method:
StoreRelation.having().names("SnapItUp")