Preface

Introduction

The Salespoint Framework is intended to minimize developing effort of point-of-sale applications. Salespoint 2010 users complainted about complexity, missing features and bugs. Thus, the decision was made to re-design and re-implement the framework from scratch. In 2013 some parts again were re-implemented with components of the Spring framework. Our development goal was an easy-to-use framework primarily targeted for educational purposes. As such Salespoint is not tailored to any specific application, but designed with a wide area of applications in mind.

Models and design patterns employed in Salespoint are inspired by Enterprise Patterns and MDA [epam]. An overview of the functionality of the new features in Salespoint are detailed in this document. We would like to thank all Salespoint users who submitted their feedback and encourage future users of Salespoint to do the same.

Prerequisites

Working with Salespoint requires some basic knowledge of the underlying technologies such as the Spring Framework as well as JPA to implement the persistence layer. This script gives you an introduction into these technologies. Also the Guestbook sample project is a good starting point to get familiar with the technology stack.

The Videoshop sample project

To give you an example of what applications built on top of Salespoint can actually look like, we provide the Videoshop sample project that covers a few key areas:

  • Using and extending the business modules provided by Salespoint to abstract a concrete problem domain.

  • Implementing a web layer (controllers, views) on top of Java based business services of that business domain.

  • Adding the necessary bits and pieces for technical aspects such as security and transactions.

The following sections will refer to code of the sample project here and there so it might be useful to have the repository cloned and the project imported into your IDE of choice to quickly be able to navigate these.

Technical Architecture

Technically Salespoint is a library to build Java based Point of Sales (PoS) applications. Whiles these applications are usually implemented as web applications, large parts of Salespoint are also usable if the application architecture of choice is a rich client implemented in Java.

The technical foundation for Salespoint is the Spring Framework[1] and Spring Boot (as some kind of opinionated layer on top of Spring Framework to ease its applicance to a large degree. Salespoint is built using the Maven build system[2]. The reference documentation and static website are built with Asciidoctor[3].

Here you can find the salespoint class diagram about the basic structure.

Core concepts

Before we dive into the technical details of Salespoint, let’s make sure we understand some core concepts that will help us understand the different modules of the framework a bit better. Each of the modules consists of a bit of code and the classes can usually be grouped into 3 different buckets: entities and value objects, summarized to newables, repositories and services, summarized to injectables as well as Spring container configuration and extension code. The following sections will cast some light on these kinds of types and their special traits.

Repositories, services and controllers

Repositories and services are used ot interact with entities and value objects. Repositories usually abstract data access into an interface that simulates a collection of entities that instances can be added to, or looked up using a parameterized criteria like "all users with a certain lastname like…". As Salespoint is using a relational database as persistence backend it leverages the Java Persistence API (JPA) and Spring Data JPA to implement repositories.

As repositories abstract a collection of an aggregate root (e.g. an order with it’s charge and line items), we usually need a higher level abstraction that exposes a more use-case oriented API: a service. It fulfils a more coarse grained set of operations that might include the interaction with multiple repositories, trigger other services in the system etc.

Repositories, services and controllers are types whose instances are managed by the Spring container. Thus they’re never instantiated manually in the application code, except for unit testing purposes. Application code uses dependency injection to access

Maven

Maven simplifies the management of project dependencies, furthermore it downloads and organizes all dependencies. Another focus of Maven is the management of a project’s build. All settings are placed in the pom.xml file (http://maven.apache.org/pom.html).

Spring

In contrast to earlier versions of the Salespoint Framework, Salespoint obeys the MVC pattern. Salespoint mostly provides the application services to interact with the the model of an MVC application as no parts of the view or the controller are implemented in the framework.

Salespoint is designed as foundation for development of web applications, using the Spring Framework to implement views, controllers and business services abstracting the domain your application-to-be-created is applied to. To ease this development, Salespoint includes a few extensions to the Spring Framework that will avoid you having to implement very tedious tasks over and over again. Read more about this in Spring 5.

As a big new approach in development with JPA, the Spring Framework with its repository interfaces can make the work very fast, clean and easy. The Crudrepository provides the basic methods for working with the database (CRUD stands for Create, Read, Update and Delete).

Business modules

Salespoint ships with a set of business modules that interact with each other. The following component diagram shows the overall structure of the setup. The relationships are used as follows:

  • uses — The module has a Spring component dependency and actively interacts with the target module. Implies a type dependency into the target module as well.

  • listens to — The module contains an event listener for events the target modules publishes. Implies a type dependency into the target module as well.

  • depends on — The module has a general type dependency to the target module, i.e. it uses the target module as library.

Diagram
Figure 1. Salespoint component overview

User accounts

Diagram
Figure 2. User account component

Base package

org.salespointframework.useraccount

Spring components

Services

Others

Aggregate roots

Value types

Published events

Properties

  • salespoint.authentication.login-via-email — java.lang.Boolean, default false. Enables the login procedure to use the email address to lookup a user instead of their username. Defaults to false.

user
Figure 3. User Account
user account
Figure 4. User Account Management

While Spring Security is used as primary technology to actually implement application security, there’s also a domain abstraction of a user acoount in the form of a UserAccount entity. That means that UserAccount instances created within the system are automatically targets for authentication. The primary purpose of a UserAccount besides the security integration is the ability to assign other business entities like an order or a reservation to an account. A UserAccount primarily consists of a UserAccountIdentifier whose String representation is used as the login name and a Password (representing an encrypted or non-encrypted password) value object.

To create UserAccounts, the UserAccountManager's method create(String userName, String password, Role…​ role) can be used. The String given as password will be encrypted using Spring Security. Also, it makes sure that the login names are unique and a password is always set.

Roles

As the name suggests, a Role represents the role a UserAccount has. It is usually used in security expressions with either @Secured or in a Thymeleaf template to restrict access to functionality or change the appearance of a view depending on a user’s status. A view for a user having an administrator role may display different content — e.g. delete buttons — than for a user not having that role.

Role is a value type[4] and thus instances are created using a factory method: Role.of("ROLE_ADMIN");.

Up until Salespoint 7.2.0, roles' names had to start with ROLE_, so that they could be used for verifications in Spring Security expressions in Thymeleaf templates (via sec:authorize attributes) or on methods (using the @PreAuthorize annotation) with their plain name. I.e. users had to be created with Role.of("ROLE_CUSTOMER") but the verifications would have used hasRole("CUSTOMER") expressions. This asymmetry has been fixed with Salespoint 7.2.1 in which the role itself can also be set up using an unprefixed name like CUSTOMER.

Login

To reduce code repetition, Salespoint contains code to automate the user log in. Using a Thymeleaf template, a special login form is generated, which is handled by an interceptor. The interceptor verifies the user password and associates the current session with the user using <login> and <logoff>. The session is required, because multiple users can be logged on at the same time.

As of version 7.1, Salespoint allows the login via a user’s registered email address. To enable that, set salespoint.authentication.login-via-email to true.

Configuring Salespoint to use an email for login purposes (in application.properties)
salespoint.authentication.login-via-email=true

If the option is activated, UserAccountManager.save(…) will reject UserAccount instances that don’t have an email address set. That means you need to provide an email when initially registering, i.e. you’ll need to use the overload of UserAccountManager.create(…) that takes an email address as parameter.

The @LoggedIn annotation

The @LoggedIn annotation can be used to inject the currently logged in UserAccount into controller method parameters. The type of the annotated parameter can either be Optional<UserAccount> for methods that should support non-authenticated access, or UserAccount. With the former variant, Optional.map() can be conveniently used to react to the presence of the user account in a functional programming style:

Example of a controller method parameter annotated with @LoggedIn
@Controller
class ExampleController {

  @GetMapping("/example")
  String example(@LoggedIn Optional<UserAccount> userAccount) {

    // functional style using map and lambda expression:
    return userAccount.map(account -> {
      // things to be done if the user account is present
      return "...";
    }).orElse("redirect:/");  // if the user account is *not* present
  }
}

During web integration tests implemented using MockMvc, the UserAccount handed into the method will be defined by the username configured through @WithMockUser(username = "…"). In other words, the annotation attribute should refer to a UserAccount created in a DataInitializer. See the Videoshop project for an example.

@SpringBootTest
@AutoConfigureMockMvc
class SampleTestCase {

  @Autowired AuthenticationManagement authenticationManagement

  @Test
  @WithMockUser(username = "boss", roles = "BOSS")
  void someTestCase() throws Exception {

    // This will be the UserAccount for the user "boss"
    var currentUser = authenticationManagement.getCurrentUser();

    // This will use the permissions defined in the method's annotation
    mvc.perform(get("…")).andExpect(…);
  }
}

Note, that the roles used to execute the controller method will still have to be defined in the @WithMockUser annotation and are not derived from the UserAccount loaded from the database as the Spring Security Authentication will solely be populated with information from the annotation, not through our UserDetailsService implementation.

Limitation

The org.salespointframework.useraccount.UserAccount is limited to the given attributes and methods. Due to the fact, that Salespoint use the SecurityContext for authentication, the UserAccount cannot be extended. In the background the org.salespointframework.useraccount.UserAccount is converted to an instance of org.springframework.security.core.userdetails.UserDetails.

If these properties don’t meet all requirements, wrap the UserAccount in a new entity. Put all the new features in this new entity and connect this information via a @OneToOne relationship with the UserAccount. An example can be found in the Videoshop project.

Example of a UserAccount extension
@Entity
public class UserAccountExtension {

  private String string;
  private Integer integer;

  @OneToOne
  private UserAccount userAccount;

  …
}

Quantity

Quantity is used to represent amounts of anything. Furthermore a Quantity can be used to calc with (plus, minus). But only Quantities with the same Metric can be combined or compared, so every Quantity has an Metric attribute.

And of course, every Quantity has an amount value, represented as a java.math.BigDecimal.

Metric

The composite type Metric contains all information pertaining to the unit or metric of the represented object. Furthermore, an object of type Metric has a description field, to explain the meaning of the metric in detail. For the example of a meter a possible description could be "The meter is the length of the path travelled by light in vacuum during a time interval of 1/299 792 458 of a second.".

Catalog

Diagram
Figure 5. Catalog component

Base package

org.salespointframework.catalog

Spring components

Repositories

Aggregate roots

Salespoint is intended as framework for point-of-sale applications. Items for sale are called products and represented by instances of the class Product. To represent different kinds of products, Product can be sub-classed. Products are managed by a Catalog implementation (see below). Products are identified using a ProductIndentifier.

The Catalog interface was designed to manage Product instances in the system. It provides functionality to add, remove, and find products. Products can be searched by their name or category. The PersistentCatalog is an implementation of the Catalog interface.

catalog
Figure 6. Catalog

Inventory

Diagram
Figure 7. Inventory component

Base package

org.salespointframework.inventory

Spring components

Aggregate roots

Published events

  • o.s.i.QuantityReduced created by:

    • o.s.i.InventoryItem.decreaseQuantity(…)

  • o.s.i.StockShort created by:

    • o.s.i.InternalInventoryListeners.on(…)

Events listened to

Properties

  • salespoint.inventory.disable-updates — java.lang.Boolean, default false. Disable inventory updates completely, defaults to false.

  • salespoint.inventory.restock-threshold — o.s.q.Quantity. The threshold at which a InventoryEvents.StockShort is supposed to be triggered during inventory updates.

The inventory package contains functionality to keep track of the amount of items we have for a given Product (see Catalog for details). Inventory is a simple Spring Data repository, that allows to access InventoryItem instances and acts as base type for the actual concrete provided inventory interfaces. A Salespoint application usually decides for one of the following models and uses one or the other inventory repositories throughout the entire application.

UniqueInventory — one inventory item per product

This model assumes that there is a single InventoryItem created per Product instance. This approach acts like there is a single warehouse keeping track of the stock for a given Product. It’s the easier mode as the Quantity provided in the InventoryItem is logically equivalent to the number of items available for a given Product.

UniqueInventoryItems will also automatically be deducted for order completions (see Handling order completion events).

MultiInventory — multiple inventory items per product

In case you need to model multiple warehouses that store products, MultiInventory relaxes the relationship between Product and InventoryItem to a 1:n one. This of course results in …findByProductIdentifier(…) returning a InventoryItems (note the plural) instance which is a collection of all InventoryItem instances available. InventoryItems exposes a ….getTotalQuantity() to easily obtain the overall number of items available across all InventoryItems.

MultiInventoryItems are not automatically deduced upon order completions as it’s not clear, which of the MultiInventoryItem instances to reduce the quantity for.

Handling order completion events

The inventory ships with a listener for OrderCompleted events (see The Order lifecycle for details) to update the stock for each line item contained in an Order that is managed by a UniqueInventoryItem. By default, an OrderCompletionFailure is thrown in case any of the OrderLine items aren’t available in the required amount. The exception contains an OrderCompletionReport that can be inspected for the item lacking enough stock.

By default, all OrderLine instances are processed. If you want to disable order updates completely, configure the property salespoint.inventory.disable-updates to true. However, you might want to only exclude some of them as the Product they point to don’t represent a product to keep track of (e.g. some service like "gift wrapping" or the like. The LineItemFilter interface allows defining a strategy to decide which of the OrderLine instances are supposed to be processed by the inventory.

To customize the strategy, simply declare an implementation of LineItemFilter as Spring bean to your application configuration:

Declaring a custom line item filter
class Config {

  @Bean
  LineItemFilter filter() {
    return item -> !item.getProductName().startsWith("to filter:");
  }
}

A declaration like this will cause only OrderLine instances pointing to Product instance with a matching name to be subject for reduction in the inventory.

On OrderCanceled the Inventory will restock if the Order has already been completed. See the Javadoc of the event listener for details.

Accountancy

Diagram
Figure 8. Accountancy component

Base package

org.salespointframework.accountancy

Spring components

Services

Event listeners

Bean references

Aggregate roots

Events listened to

The accountancy package contains functionality supporting book keeping. The Accountancy is an application service that is an append-only log of AccountancyEntrys. This means, that AccountancyEntry instances cannot be changed, once they were added to the Accountancy. To correct a wrong entry, one would typically submit a compensating one. See Accountancy handling of order-related events for details.

AccountancyEntry is an abstract type. The only concrete implementation of that which Salespoint knows about is OrderPaymentEntry. Those are automatically created during the handling of the lifecycle events triggered in the process of handling orders.

It is common to add custom AccountancyEntry types to your application represent other types of incomes and expenses.

import org.salespointframework.accountancy.AccountancyEntry;

@Entity
public class MyCustomAccountancyEntry extends AccountancyEntry {

  // Custom field declarations

  public MyCustomAccountancyEntry(MonetaryAmount amount, String description, …) {
    super(amount, description);
    // assign other parameters
  }

  // Getters, *no* setters.
}

To manage AccountancyEntry instances, you can get access to the Accountancy service via dependency injection.

@Service
class MyApplicationComponent {

  private final Accountancy accountancy;

  // Getting access to Accountancy via dependency injection.
  MyApplicationComponent(Accountancy accountancy) {

    Assert.notNull(accountancy, "Accountancy must not be null!"

    this.accountancy = accountancy;
  }

  void someMethod() {

    var amount = Money.of(50, Currencies.EURO);

    // Add the custom entry to the accountancy
    var entry = accountancy.add(new MyCustomAccountancyEntry(amount, "Some description."));

    // Obtains only the custom accountancy entries
    var entries = accountancy.findAll(MyCustomAccountancyEntry.class);
  }
}

Accountancy handling of order-related events

The accountancy subsystem handles OrderPaid events by creating a OrderPaymentEntry for the order. Reversely, it will create a compensating OrderPaymentEntry on OrderCanceled if theres a revenue OrderPaymentEntry available for Order. See the Javadoc of the event listener for details.

Payments

payment method
Figure 9. Payment domain types

For the paying transaction, Salespoint provides some payment strategies.

  • DebitCard — representation of e.g. EC-card or MaestroCard

  • CreditCard — equal to DebitCard but with creditLimit-Attribute

  • Cheque — represents a written order of payment in a digital form

  • Cash — represents a payment, made direct in cash

Order management

Diagram
Figure 10. Order component

Base package

org.salespointframework.order

Spring components

Services

Bean references

Aggregate roots

Published events

The order management is centered around the Order aggregate that aggregates OrderLines and ChargeLines. An OrderLine refers to a Product from the Catalog in combination with a Quantity. Note, that the OrderLine creates a copy of the Products name and price, so that changes to those attributes of a Product don’t affect existing Orders. A ChargeLine describes any additional charge that can be associated with the Order, e.g. discounts, any general service charges and so on.

AttachedChargeLine are special ChargeLines that are attached to an OrderLine. Just as a standard ChargeLine they can be used to add charged services or discounts but attached to the particular OrderLine instead of the overall Order.

Order exposes a variety of methods to lookup

Example 1. Order domain classes
order

The Order lifecycle

An Order is an aggregate root[5], which means that state changes to it that have important business meaning trigger events.

order lifecycle
Figure 11. Order lifecycle

For every state that is not Open, an event will be published. These events are then used by other business modules to act on them. Currently, the following events are exposed:

  • OrderPaid — the event being thrown if the order gets paid, usually through a call to OrderManagement.pay(…). The Accountancy module makes use of those events as described in Accountancy handling of order-related events.

  • OrderCompleted — the event being thrown when the Order is about to be completed, usually through a call to OrderManagement.completeOrder(…). The Inventory module ships with a listener for the OrderCompleted event to update the inventory and reduce the stock of ordered items. See Handling order completion events for details.

  • OrderCanceled — the event being thrown when an Order was canceled, usually through OrderManagement.cancelOrder(…). Accountancy will react by creating a new AccountancyEntry that compensates for the revenue entry created for the OrderPaid event. Inventory will restore the previously subtracted stock if the order had been canceled already.

Handling events

In case you want to write custom code to react to those events you need to implement an event handler. An event handler is a simple Spring component that has methods annotated with either @EventListener or @TransactionalEventListener.

@Component
class MyEventListener {

  @EventListener
  public void handleEvent(OrderCompleted event) {
    // Your code goes here
  }

  // alternatively

  @TransactionalEventListener
  public void handleEventAfterTransaction(OrderCompleted event) {
    // Your code goes here
  }
}

The difference between the two methods is when they’re actually invoked by the framework. The former method is called before the changes to the database are eventually persisted to the database. That means, the event listener can throw an exception and the action that caused the event to be published in the first place will be canceled, too. On the other hand an @EventListener will not be guaranteed that the original action will succeed as it might be rejected if it violates e.g. database constraints. In contrast, a @TransactionalEventListener will be called after the original action has completed successfully. That in turn means, that the listener has no chance to abort that action.

Business time

Base package

org.salespointframework.time

Spring components

Services

Published events

To be able to simulate the state of the system in a given point in time all components in the system that set a point in time on entities or lookup the current time use the BusinessTime interface.

By default, the implementation will just return the current system date. However, it also allows to "forward" the current business time by calling the BusinessTime.forward(…) method. This will augment the current business time by the duration handed into the method. E.g. forwading the business time by 2 months will return the very same time but two months ahead for each call to BusinessTime.getTime().

This is particularly useful for demoing purposes if e.g. orders can be timed to a certain date, credit cards become invalid after a certain point in time etc.

Timed events

BusinessTime will publish events for particular time progressions that allow business functionality to be triggered via event listeners:

  • DayHasPassed — will be published for each day.

  • MonthHasPasses — will be published at the last day of each month.

The events will be published on calls to BusinessTime.forward(…) if the provided Duration exceeds the day. If you want to see those events also triggered for applications running in production, set salespoint.scheduling.enabled to true. This is disabled by default as it might cause issues when running tests. Note, that the events cannot be "unpublished" when handing negative values to ….forward(…). That means that event listeners have to be implemented assuming that such events could be received multiple times.

Here’s an example of how to implement an event listener that can react to these events:

@Component
class TimedEventListener {

  @EventListener
  void on(DayHasPassed event) {
    // Code to be triggered daily goes here
  }

  @EventListener
  void on(MonthHasPassed event) {
    // Code to be triggered monthly goes here
  }
}
business time
Figure 12. Business time

File Storage

Diagram
Figure 13. File Storage Component

Base package

org.salespointframework.files

Spring components

Services

Value types

Published events

Properties

  • salespoint.storage.location — java.nio.file.Path, default ${user.home}/.salespoint/uploads. Folder location for storing files.

Extending Salespoint

Salespoint provides a lot of functionality out of the box but it’s very likely that a project built on it will have to extend it. The way those extensions have to be implemented depends on which module you’d like to extend, as there are different design and implementation strategies for the modules, because their internal complexity varies in the first place and thus, different ways of encapsulating are in place. We’re going to look at the different ways of extending Salespoint based on the examples of the Catalog and Order management modules. Note, that they’re primarily chosen as examples of the particular implementation strategy. Other modules can be extended the same way.

Extending the catalog

Let’s start with the Catalog module, as it’s a pretty simple one. It consists of the following building blocks:

  • An aggregate root (Product).

  • A repository to manage instances of the aggregate root (Catalog).

Diagram
Figure 14. High-level structure of the catalog module

This very simple approach works as all business logic that we need for Product can be implemented in the aggregate root directly. The reason for that in turn is that we don’t need access to any other Spring beans to implement that logic. The aggregate can solely concentrate on enforcing invariants and providing a high-level API to interact with the it, e.g. through supports(Quantity) etc.

Extending Product or Catalog respectively is straight forward:

extending catalog
Figure 15. Extending the Salespoint catalog
  1. Create a dedicated MyProduct extending Product and add the additional attributes. — Be sure to check that the additional attributes do not shadow properties of the parent class. I.e. your custom entity should not have attributes named name, price etc. Also, the attributes must not be named after SQL keywords.

  2. Create a dedicated repository interface that extends Catalog. — Declare custom query methods that can refer to custom attributes as needed.

  3. In your application code, use MyCatalog instead of Catalog. — This is possible as MyCatalog inherits all basic CRUD functionality from Catalog.

Extending the order module

Let’s move on to a slightly more complex case. The Order management module is also centered around an aggregate root but there are business rules that cannot solely be implemented on it. The reason for that is, that we need to obtain the date at which an Order was created from the BusinessTime component that allows to customize the current time from a system’s point of view. Thus, the structure of the module looks as follows:

Diagram
Figure 16. High-level structure of the orders module
  • It exposes an aggregate root (Order) and a service interface (OrderManagement, not a repository).

  • It contains a package protected implementation of that service interface (PersistentOrderManagement) and repository (OrderRepository), used by the service implementation.

In that arrangement, the repository is hidden inside the package and not usable — and thus not extendable — from the outside. We need to do this, to avoid code being able to save Order instances directly which would bypass the interaction with BusinessTime to gather the correct date to be set on the Order. If we cannot extend the repository directly, how do we create a custom MyOrder? Let’s have a look at the steps involved:

extending order
Figure 17. Extending order
  1. Create a dedicated MyOrder extending Order and add the additional attributes. — Be sure to check that the additional attributes do not shadow properties of the parent class. I.e. your custom entity should not have attributes named paymentMethod, orderStatus etc.

  2. Create a custom MyOrderRepository extending Spring Data’s org.springframework.data.repository.Repository<T,ID> interface. — Type it to MyOrder (domain type) and OrderIdentifier (identifier type). Make sure you choose the right Repository interface from the Spring Data package, because there are other types that are named the same. Declare custom query methods that can refer to custom attributes as needed.

  3. Use MyOrderRepository right besides OrderManagement in controllers or other client code. — Operations available on the service interface like save(…) can be still be used as is. Queries for custom attributes would then be executed on the newly introduced repository.

Technical APIs

Besides the business modules, Salespoint containes a few SPIs that can be used to extend the functionality of the core framework. These SPIs usually exist to make repeated use-cases easier to implement and free you from writing too much boilerplate code.

DataInitializer

Every application should be tested, so an easy way is, to use test data. Salespoint provides an SPI called DataInitializer for you to implement. If your implementation is registered as Spring bean (e.g. by annotating it with @Component), it will automatically be found and initialize() will be called at application startup. As shown in the Videoshop project, a DataInitializer class is registered and creates a lot of data and finally adds this data to the application.

Entity binding in WebMVC controller methods

Controller methods can bind request parameters and path variables to entity instances directly if the value to be bound is an identifier of the entity to bind. Assuming a controller method displaying a form registers an entity with the model:

@Controller
class MyController {

  @GetMapping("/myform")
  String displayForm(Model model) {

    Order order = … // Order with id 4711.

    model.addAttribute("order", order);

    return "myform";
  }
}

The identifier of the order can be used in anchors and form values like this.

<!-- Expands to /orders/4711 -->
<a th:href="@{/orders/{id}(id=${order.id})}" …>…</a>

<form th:object="${myForm}" …>
  <input type="hidden" name="order" th:value="${order.id}" />
  …
</form>

When following the link or submitting the form the identifier can be bound to the backing entity type directly:

@Controller
class MyController {

  @GetMapping("/order/{order}")
  String displayForm(Order order) { … }
}

Note how we do not use a String or e.g. Long parameter type as controller method parameter but the entity type directly. This is achieved by Spring MVC trying to convert the source String value submitted through the request into the identifier type of the entity in the first place as well as a subsequent repository lookup using the identifier value just created. In the above example we’d convert "4711" to a Long 4711 (assuming the identifier of Order is declared as Long). That in turn gets converted into an Order instance by calling OrderRepository.findById(Long) with the just converted value.

Technical Stack

Spring Boot

Rapid application development framework based on Spring (see below).

Spring 5

The de-facto standard Java application framework.

Spring Data JPA

Spring module to easily build data acess layers using JPA 2.1 (Java Persistence API).

Thymeleaf

Library to build HTML web views using natural templating.

Java Money

With Salespoint Money class and its related components were replaced by the Java Money library. Prices and other monetary values are represented as org.javamoney.moneta.Money objects. Due to the fact that all representing objects are immutable, all arithmetic functions produce a new instance.

Example for Money operations
Money first = Money.parse(“USD 23.07”);
Money second = Money.parse(“USD 18.07”);
Money sum = first.plus(second);

When used in domain objects, Money instances need to be mapped using @Lob.

Appendix

Appendix A: New and Noteworthy

What’s new in Salespoint 7.1?

Upgrade to Spring Boot 2.1 GA

#225 — Simple maintenance upgrade as 7.0 still referred to a milestone release of Spring Boot 2.0.

Allow deletion of user accounts and orders

#218, #219 — Note that when deleting the former, all other aggregate that keep a reference to the UserAccount have to be deleted manually first, e.g. all orders associated with this account.

Allow login via email address

#222 — Added the ability to use a user’s email address for login purposes. For more details, see this section.

Improvements in the order aggregate and event handling

#226 — We added the ability to register ChargeLine instances for a dedicated OrderLine. Read more about that in the updated section of Order management.

New order canceled event and fixes in event handling in general

#230 — The creation of a PaymentAccountancyEntry has been properly attached to the OrderPaid event now. An OrderCanceled event is now published on order cancellation which triggers a rollback of the inventory updates as well as a compensating OrderPaymentEntry. Read more on that in the updated section The Order lifecycle.

What’s new in Salespoint 6.1?

Upgrade to Spring Boot 1.2 and Spring 4.1

For Salespoint 6.1 we upgraded to Spring Boot 1.2, which transitively updates a few third party dependencies, most notably Spring 4.1. Read more on the new features of Spring Boot in its reference documentation and skim through the upgrade notes as well. The new features of Spring 4.1 are described here.

New @EnableSalespoint annotation to simplify configuration

Activating Salespoint in a web application has been quite cumbersome so far and required a dedicated set of annotations and imports of configuration classes. To simplify this, Salespoint 6.1 introduces @EnableSalespoint to take care of the previously manual steps. For more information on what gets acivated and imported, see the JavaDoc of the annotation.

Usage of the mail autoconfiguration shipping with Spring Boot 1.2

As Salespoint 6.1 upgrades to Spring Boot 1.2 we now use the email auto-configuration settings shipping with that release. This most notably means that you slightly have to alter your properties to configure the mail user, password and host. See Boot’s reference documentation and the list of current application properties for details.

Appendix B: FAQ

  1. Why do I see warning log statements about database tables not being accessible at startup?

    This is due to a bug in Hibernate in combination with HSQLDB and constraints in databases. Hibernate tries to drop those on application startup but fails to as the (in-memory database) is empty. See this bug report for reference.

  2. How do I send emails from a Salespoint application?

    Salespoint has everything prepared for you in terms of necessary dependencies. All you need to do is configure the following properties in application.properties:

    spring.mail.host=
    spring.mail.username=
    spring.mail.password=

    If these properties get real values assigned, Salespoint will automatically create a Spring component of type JavaMailSender for you that you can inject into your clients and use to send emails. See the Spring reference documentation on details for that.

    Never push your email credentials into the version control system.

    Alternatively for testing purposes, configure the RecordingMailSender as a Spring bean to simply write emails to be sent to the console instead of sending them to a real SMTP server. To automatically switch between the two setups have a look at how to use Spring’s profile support with Boot.

Appendix C: Glossary

Dependency injection

Dependency injection is a software design pattern that implements inversion of control and allows a program design to follow the dependency inversion principle. The term was coined by Martin Fowler. See Wikipedia.

SPI

Service Provider Interface — interfaces to be implemented to extend the functionality of the core framework. See Wikipedia for details.

Appendix D: Bibliography

  • [] Eric Evans. Domain-Driven Design: Tackling Complexity in the Heart of Software. Prentice Hall. 2003

  • [] Jim Arlow, Ila Neustadt. Enterprise Patterns and MDA: Building Better Software with Archetype Patterns and UML. Addison-Wesley. 2004.

  • [] Martin Fowler. Inversion of Control Containers and the Dependency Injection pattern. 2004. http://www.martinfowler.com/articles/injection.html


1. Spring Framework - Wikipedia
2. Apache Maven - Wikipedia
3. Asciidoctor - Website