In this chapter we are going to create business logic layer for already implemented database queries. We are going to prepare UseCases together with facades and proper component tests.
The goal of this chapter is to implement logic layer for previously defined functionality meaning: Interface / Implementation definition together with tests.
This step will contain manual creation and implementation of business logic layer. Please have in mind that most of described steps can be done by cobigen, which will be explained later on.
During the persistence exercises you have created (or generated) the Entity infrastructure. The entities implement some entity interfaces (Item, Customer and Order). Transport objects must implement the same interfaces. Please implement the transport objects (ItemEto, CustomerEto and OrderEto) by implementing the mentioned interfaces. The transport objects should extend the class AbstractCto and should be placed in the api project in following package
com.devonfw.app.java.order.orderservice.logic.api.toYou can also generate the transport objects with CobiGen by selecting the entity class and selecting the TO’s option.
Please implement at leas one transport object (ItemEto) manually and generate the rest with CobiGen.
Next step is generating proper layer infrastructure. To do so we have to define API for following use cases:
-
UcFindItem
-
UcFindCustomer
-
UcFindOrder
-
UcManageItem
-
UcManageCustomer
-
UcManageOrder
As we can see we simply define two type of usecases related to each entity we have created. Usecases with "Find" in name shall contain all logic related to read operations e.g. Find by id, business key, search criterions e.t.c. Usecases with "Manage" in name shall contain logic for the rest of CRUD functionality which modifies state of our objects and database. For example save, delete or modify methods.
Locate each of created interface in proper package of the api project:
com.devonfw.app.java.order.orderservice.logic.api.usecaseThe usecase interfaces should provide at least the CRUD methods
public interface UcFindItem {
/**
* Returns a Item by its id 'id'.
*
* @param id The id 'id' of the Item.
* @return The {@link ItemEto} with id 'id'
*/
ItemEto findItem(long id);
/**
* Returns a paginated list of Item matching the search criteria.
*
* @param criteria the {@link ItemSearchCriteriaTo}.
* @return the {@link List} of matching {@link ItemEto}s.
*/
Page<ItemEto> findItems(ItemSearchCriteriaTo criteria);
}And Manage usecase:
public interface UcManageItem {
/**
* Deletes a item from the database by its id 'itemId'.
*
* @param itemId Id of the item to delete
* @return boolean <code>true</code> if the item can be deleted,
* <code>false</code> otherwise
*/
boolean deleteItem(long itemId);
/**
* Saves a item and store it in the database.
*
* @param item the {@link ItemEto} to create.
* @return the new {@link ItemEto} that has been saved with ID and version.
*/
ItemEto saveItem(ItemEto item);
}Please implement manually the use case interfaces for Item (UcFindItem, UcManageItem). The interfaces for the Customer and Order will be generated by CobiGen in the next steps.
As the next step you should implement the both interfaces created in the previous step.
Please locate the usecase implementations in following package
com.devonfw.app.java.order.orderservice.logic.impl.usecaseEach of created use case has to be annotated with following annotations:
@Named
@Validated
@TransactionalEach usecase implementation should implement the given interface and should extend the class AbstractUc, which provides some utility methods (e.g. methods to receive the bean mapper).
In such created classes you can @Inject your repositories. As you can see both usecase implementations for Item require injection of the same repository (ItemRepository). Instead of injecting the repository into both usecases you can create abstract class which contains all of mandatory repositories and use it as base class for both implementations. Here’s example:
public class AbstractItemUc extends AbstractUc {
@Inject
private ItemRepository itemRepository;
public ItemRepository getItemRepository() {
return this.itemRepository;
}
}Please locate the abstract class in following package
com.devonfw.app.java.order.orderservice.logic.base.usecaseNow we have to define component fasade. Component fasade will be used when we’d like to access logic defined in specific component from another one. Please define it as follow:
public interface Orderservice extends UcFindItem, UcManageItem {
}Please locate the fasade interface in following package
com.devonfw.app.java.order.orderservice.logic.apiImplementation of this class contains only redirection of functionality to respective usecases. We expect it to have following structure:
@Named
public class OrderserviceImpl extends AbstractComponentFacade implements Orderservice {
@Inject
private UcFindItem ucFindItem;
// ...
@Override
public Page<ItemEto> findItems(ItemSearchCriteriaTo criteria) {
return this.ucFindItem.findItems(criteria);
}
// ...
}Please locate the fasade implementation in following package
com.devonfw.app.java.order.orderservice.logic.implAfter proper structure definition we have to implement logic itself. Please define proper methods in respective use cases (if not done already), and implement logic using previously created repository.
Please notice that each repository method returns Entities. In business logic layer we have to convert them to transport object using bean mappers defined in AbstractBeanMapperSupport. Each of our Use Case implementation shall contain following tree, so we shall be able to use bean mapper functionality:
Example:
@Named
@Validated
@Transactional
public class UcFindItemImpl extends AbstractItemUc implements UcFindItem
/**
* Logger instance.
*/
private static final Logger LOG = LoggerFactory.getLogger(UcFindItemImpl.class);
@Override
public ItemEto findItem(long id) {
LOG.debug("Get Item with id {} from database.", id);
ItemEntity foundEntity = getItemRepository().getOne(id);
return getBeanMapper().map(foundEntity, ItemEto.class);
}
// ....
}Implement the other methods in similar way.
Please notice that we are not obligated to use getters for repositories located in AbstractItemUc – we can simply @Inject necessary beans to our usecase implementation.
In the previous steps you have implemented the CRUD methods for ItemEntity manually. Now you will generate the same logic for CustomerEntity and OrderEntity.
To generate the logic pleas select the CustomerEntity, run the CobiGen and select following options
-
TO’s
-
CTO’s
-
CRUD UC logic
After running the generator following changes will be performed
-
The necessary transport objects and composite transport objects will be generates
-
The use case interfaces and their implementations will be generated
-
The fasade interface
Orderserviceand its implementation will be extended to provide the methods provided by the new usecases
Please repeat the same for OrderEntity
In the previosu steps you have implemented manually or generated the CRUD part of the business logic. Next you have to implement some additional business logic. Please create following logic in proper usecase interfaces:
-
increase price of the item with specified name.
-
remove customer by id.
-
create order with two positions and with a specified owner.
-
find orders from given day with specified status.
-
find items by name match (LIKE) ordered by name.
At first we shall generate some testdata builders. We can do it at entity level to generate entity data builder or at Eto level. For our purposes we will use Eto more often. Builder generation can be done by IDE plugins or using CobiGen:
Now we can prepare some testData. Please create interfaces e.g. ItemTestData and locate them in following package (test scope):
com.devonfw.app.java.order.orderservice.common.baseYou can use this interfaces to define builder that can be used later on in testing of the use cases. Here’s some example:
/**
* The constant CHEESE.
*/
ItemEtoBuilder CHEESE = ItemEtoBuilder.anItemEto()
.withName("cheese")
.withPrice(12.50);In this part we’ll try to create springboot context aware testclasses that will check our businesslogic implementation. For that we’ll use ComponentTest class that will be started without web environment context. We also have to provide some transaction context. To do so please create class OrderserviceImplTest located:
com.devonfw.app.java.order.orderservice.logic.implAnd define proper annotations:
@Transactional
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
public class OrderServiceImplTest extends ComponentTest {
@Inject
private Orderservice orderService;Now we can implement some tests. Please provide some valid testcases for each method defined in our UseCases.
More about testing: https://github.com/devonfw/devon4j/wiki/guide-testing
