Preface
1. 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 6 is not tailored to any specific application, but designed with a wide area of applications in mind.
Models and design patterns employed in Salespoint 6 are inspired by Enterprise Patterns and MDA [epam]. An overview of the functionality of the new features in Salespoint 6 are detailed in this document. We would like to thank all Salespoint users who submitted their feedback and encourage future users of Salespoint 6 to do the same.
2. 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. static.olivergierke.de/spring-webapps/index.html[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.
3. The Videoshop sample project
To give you an example of waht 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. |
4. Typographic Conventions
Two typographic conventions are employed throughout this document to highlight specific phrases. The following paragraphs describe when and why these highlightings are used:
Keywords
The monospace font is used to denote variable names, class names, type names, Java keywords, Java package names and so forth.
Termini
Proper names and termini are printed in termini font.
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].
5. 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.
5.2. 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
6. 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).
7. Spring
In contrast to earlier versions of the Salespoint Framework, Salespoint 6 obeys the MVC pattern. Salespoint 6 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 6 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 4.
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).
8. Configuration of Salespoint 6.0.1.RELEASE
The configuration for an application can be modified on the application class in the root package (e.g. videoshop.Videoshop for the videoshop project). Methods, annotated with @Configuration
, will be scanned at the beginning of the deployment on the application server (in this case Spring Boot). This configuration files will tell the application server the settings for the application. By overriding the configuration method, you can specify the login behaviour or security functions. For a basic login strategy the videoshop is a good start. There you can see, that with authorizeRequests() an authorization will be set. Following by matchers, you can specify the pages, this authorization is made on. Further, you can easily set an login page with formLogin() and the path to the login page with loginProcessingUrl("/login"). Analogue the logout settings works like login system.
Technical Stack
11. Spring Data JPA
Spring module to easily build data acess layers using JPA 2.1 (Java Persistence API).
13. JodaMoney
With Salespoint 6 money class and its related components were replaced by the Joda-Money [7] project. Prices and other money values are represented as org.joda.money.Money
or for more precisison as org.joda.money.BigMoney
objects. Due to the fact, that all representing objects are immutable, all arithmetic functions produce a new instance.
Money first = Money.parse(“USD 23.07”);
Money second = Money.parse(“USD 18.07”);
Money sum = first.plus(second);
Joda-Money also supports Currency
(see Example for JodaMoney operations). A set of loaded currencies is provided by an instance of CurrencyUnitDataProvider
. But new and funky CurrencyUnits
can be created to. So with this currencies, money values can be converted from one to another currency.
Business modules
14. Users
To manage system accounts, Salespoint 6 has a notation of a user in the form of the User
class. Users are managed by the UserAccountManager
. Every user is uniquely identified by a org.salespointframework.useraccount.UserAccountIdentifier
, which also serves as primary key attribute for the database.
14.1. Roles
org.salespointframework.useraccount.Role`s in conjunction with authorization tag `hasRole()
can be used to change the appearance of a View, depending on a user’s status. For example, a View for a user having an “administrator” role may display different content, for example delete buttons, than for a user not having that role. Thus, roles allow for flexibility and assist in code reuse, when designing the View
.
14.2. Login
To reduce code repetition, Salespoint 6 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.
14.3. Limitation
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.
@Entity
public class UserAccountExtension {
private String string;
private Integer integer;
@OneToOne
private UserAccount userAccount;
…
}
15. Quantity
Quantity
is used to represent amaounts of anything. Quantity
objects are immutable and the class implements the Comparable
interface.
15.1. Attributes of Quantity
-
a numerical value → BigDecimal - Representing numerical values
-
a (measurement) unit or metric → Metric - What is represented
-
a type specifing the rounding of the numerical type → Rounding
15.2. BigDecimal - Representing numerical values
BigDecimal
was chosen as datatype for the amount
attribute over float
or double
because of its arbitraty precision. Moreover, objects of BigDecimal
are immutable
and the BigDecimal
class provides operations for including, but not limited to: arithmetic, rounding, and comparison.
15.3. Metric - What is represented
The composite type Metric
contains all information pertaining to the unit or metric of the represented object. Examples for units or metrics are: m (meter), s (second), pcs (pieces). For example consider the unit of length "meter": represented by an object of the class Metric
the symbol would be set to m
and the name to meter
. 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.".
15.4. Rounding
There are many rounding strategies, for money operations and so on. For this case, Salespoint ships with a RoundingStrategy
interface providing a round()
method. This implementation of this RoundingStrategy
, BasicRoundingStrategy
offers two ways to describe a rounding operation: using the decimal places after (or before) the decimal delimiter and using a rounding step.
16. Catalog
Salespoint 6 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.
17. Accountancy
The accountancy package contains functionality supporting book keeping. AccountancyEntry
is a representation of an accounting entry. Accountancy
aggregates AccountancyEntry
s. Every AccountancyEntry
is uniquely identified by an AccountancyEntryIdentifier
. AccountancyEntry
extends AbstractEntity
and serves as persistence entity, while PersistentAccountancy
implements Accountancy
and provides transparent access to the JPA layer. AccountancyEntryIdentifier
is used as primary key attribute, when entities are stored in the database.
By implementing and sub-classing Accountancy
, the notion of different accounts, as known from double-entry bookkeeping, can be realised.
To create a new account, AccountancyEntry
has to be sub-classed. Every object of such a class belongs to the same account. Accessing per-account entries is facilitated by specifiying the desired class type when calling get()
or find()
methods of Accountancy
.
18. Payments
For the paying transaction, Salespoint provides some payment strategies.
-
DebitCard
— representation of e.g. EC-card or MaestroCard -
CreditCard
— equal toDebitCard
but with creditLimit-Attribute -
Cheque
— represents a written order of payment in a digital form -
Cash
— represents a payment, made direct in cash
19. Order
An Order
can be considered as a sheet of paper which basically consists of lines, each representing an ordered product. An order can be uniquely identified by an OrderIdentifier
. Every product of an order is stored in a separate OrderLine
. An OrderLine
in turn is uniquely identified by an OrderLineIdentifier
. An OrderLine
contains all information to identify a Product
(see Section Catalog ).
A ChargeLine
represents additional costs or discounts and can be applied to an OrderLine
or an Order
. A ChargeLine
is uniquely identified by a ChargeLineIdentifier
.
For example, ChargeLine s can be used to handle special taxes or handling fees.
|
19.1. Order status lifecycle
Order
s are entities. The lifecycle covers four states which are defined by enumeration type OrderStatus
. The lifecycle state cannot be arbitrarily changed, but follows a fixed scheme.
orderStatus
in the class Order
@Enumerated(EnumType.STRING)//
private OrderStatus orderStatus = OrderStatus.OPEN;
State transistions are automatically carried out when certain methods are called on an Order
object, for example cancelOrder()
.
A Order
can only be modified in state OPEN
. PAID
, CANCELLED
and COMPLETED
Order
s are immutable. Calling the payOrder()
method changes the state to PAID
, makes the Order
immutable and creates an ProductPaymentEntry
object. Ordered objects will only be removed from inventory when the completeOrder()
method is called. COMPLETED
is one of the final states and it is not possible to change the state of such orders.
Completing an order causes product instances to be removed from the inventory. Because product instances may not be present anymore in the inventory, or their number may not be suffice to fulfill an order, completing an order requires special attention. To handle these situations, the OrderCompletionResult
interface was introduced. First of all, these OrderCompletionStatus
are possible:
-
SUCCESSFUL
— The order was completed successfully, and all products were removed from the inventory. -
FAILED
— An error from which recovery is impossible occured.
The OrderManager
aggregates Order
s. The implementation of OrderManager
interface, PersistentOrderManager
, is used to persist, update, find and remove orders to/from the database. In Order
aggregated objects, like OrderLine
s and ChargeLine
s will also be persisted, updated or removed with the Order
object.
20. Business time
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.
21. 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.
21.1. 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.
Appendix
Appendix A: FAQ
-
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.
-
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
ConsoleWritingMailSender
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 B: 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 C: Bibliography
-
[ddd] Eric Evans. Domain-Driven Design: Tackling Complexity in the Heart of Software. Prentice Hall. 2003
-
[epam] Jim Arlow, Ila Neustadt. Enterprise Patterns and MDA: Building Better Software with Archetype Patterns and UML. Addison-Wesley. 2004.
-
[di] Martin Fowler. Inversion of Control Containers and the Dependency Injection pattern. 2004. http://www.martinfowler.com/articles/injection.html
Appendix D: Asciidoc template
Some random snippets to show how Asciidoc is used. For more examples see the user manual.
class SomeClass { }
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Salespoint.class)
@Transactional
public abstract class AbstractIntegrationTests {
}