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 theSolrIndexSearcherSolrIndexSearchResultImpl- 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
SearchControllerImplhandleRequestInternal()method.SearchControllerImplcreates aSearchRequestobject.SearchControllerImplcalls theSearchServicewith 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
SearchServicechecks theCOMMERCE/STORE/SEARCH/searchCategoriesFirstsystem setting whether it should search for categories before products.If
SearchServicefinds a category, the search stops and returns the category page.If no categories are found,
SearchServicecreates aProductCategorySearchCriteriafrom theSearchRequest.SearchServicedispatches theProductCategorySearchCriteriato the injectedSolrIndexSearchService, which returns an emptySolrIndexSearchResultthat has been injected with aSolrIndexSearcher.SearchServiceapplies pagination to theSolrIndexSearchResultand delegates to theSolrIndexSearcher.SolrIndexSearcherconverts theProductCategorySearchCriteriainto aProductQueryComposer.SolrIndexSearcheruses theProductQueryComposerto generate a Solr query and runs a search.If no results are found,
SolrIndexSearcheruses theProductQueryComposerto generate a Fuzzy query and runs a Fuzzy search.SearchServiceregains control with the populatedSolrIndexSearchResult.If not enough results are returned,
SearchServiceattempts to generate suggestions, as described by Search Suggestions.SearchServicereturns the results to theSearchControlleralong 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
SolrIndexSearcherA data access object for Lucene searching. This class contains logic for executing fuzzy searches when no exact matches are found.
ProductQueryComposerImplements a method,
composeFuzzyQuery(..), that creates a newFuzzyQueryLucene 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
SolrSpellIndexSearcherImplParses a supplied query, and checks the spelling index to see if there are any suggestions for terms in that query.
SolrIndexSearchServiceImplA service for dispatching search requests to the SolrIndexSearcher.
ProductIndexBuilderCreates a spelling index using the product index’s content field.
SearchServiceImplA 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:
QueryResultQueryServiceimplementationQueryCriteria
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.
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:
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")