Content
[D10] Support of Java8 Optional in API methods
The use of java.util.Optional
within API methods makes it easy to translate 404-Not Found
into Optional.empty()
. Without this pretty cool Spring & Jackson feature we must live with an null
response object, that you might have seen at some point. Not the instance is null
, but all fields of the instance are, because the sender (a Spring Boot app) sends a JSON response with http status == 404 but passes a ResponseBody, and that one is tried to be deserialised into a business object, therefor all fields of the business object are null
.
[D9] Maven Dependency Declaration & Usage
Apache Maven build tool knows about a dependencies
section and a dependencyManagement
section.
Rule We declare dependencies with their version and classifiers but without scope within the dependencyManagement
section and actually define the usage of the dependency within the dependencies
section without the version but with the proper scope. It is not allowed to declare and define the usage in the dependencies
section only.
[D8] Explicit database columns
Issue As we do with JSON properties in [D2] Use JsonProperty explicit we should also be explicit and name every database column. Without doing so we could run into trouble when we refactor field names but don’t want the columns to be renamed.
Rule Instead of ending with a mix of annotated and un-annotated fields, all the fields that are going to be mapped onto database columns, need to be annotated with @Column
and a name must be set. The name must have the prefix C_
assigned. This is very handy to avoid cases where the Java names clashes with database keywords. For example: One could name a field user
that ends in a column USER
that is a keyword in RDBMS and forbidden to use. To avoid such clashes all columns have the C_
prefix.
[D7] Define a Logger
Issue To simply define a Logger instance just use a static definition and don’t add some magic third party library like Project Lombok.
Example
private static final Logger LOGGER = LoggerFactory.getLogger(MyClazz.class);
This is simple, added fast and just uses Java and the logging library of choice. This line of code can be created with an IntelliJ live template dlog
(define logger).
Convention - Always use slf4j as log API - Logger instances must be private to the class and static - Logger instances in OpenWMS.org code must be named as LOGGER
[D6] Direction of mapping between models
Issue In OpenWMS.org we have at least two or more domain models that needs to be mapped from one into the other. The inner business domain model of a microservice is often also the persistence model and thats fine. But outer public API have their own representation, i.e. domain model, like the VO model of the presentation of the MO model of the messaging layer. We could map in bidirectional way that makes often sense but sometime it only make sense to make in one direction.
Problem Our mapping library support the configuration of the mapping direction on attribute level but only with two options: bi-directional
or one-way
. The latter means from source (class-a) to destination (class-b). A explicit switch, like a-b
or b-a
does not exist.
Solution So we define that we always name the inner model as class-a
and the outer model as class-b
. By this convention it is clearly defined that we map between the rich domain model to the more simple domain model without object-graph resolution, just by traversing the source object and dereference the attribute of the destination class.
Example Here an correct example of a mapping from the inner class to the outer class:
<mapping>
<class-a>org.openwms...ReceivingOrderPosition</class-a>
<class-b>org.openwms...ReceivingOrderPositionVO</class-b>
<field type="one-way">
<a is-accessible="true">product.sku</a>
<b is-accessible="true">sku</b>
</field>
</mapping>
[D5] Get Feign work with PATCH
Issue Out of the box a FeignClient
does not support HTTP PATCH
requests.
Solution To get PATCH
work with Feign another Maven dependencies needs to be added:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>10.4.0</version>
</dependency>
The correct version mut be used, that works with OpenFeign.
[D4] Give database constraints an explicit name
For better maintenance use clear and unique names for unique constraints and foreign keys:
@UniqueConstraint(name = "UC_LOC_ID", ...)
@ForeignKey(name = "FK_LOC_ACC")
Unique constraints start with UC
, then the abbreviation for the hosting table and an identifier. Foreign keys start with FK
, then the abbreviation for the hosting table and the referenced table/column.
[D3] (Optional) Use constants as JPA Table names
Example:
@Entity
@Table(name = Location.TABLE)
class Location {
public static final String TABLE = "COM_LOCATION";
}
In case of refactoring the table name change can be done at one single place. In native queries the constant can be referenced instead of a String.
[D2] Use JsonProperty explicit
In classes serialized as JSON use @JsonProperty
on properties explicitly. This ensures that refactoring and renaming the fields keeps the JSON names and the API stable.
[D1] Exception translation in REST Controller
Issue Java Exceptions should be translated into proper representations at the system boundary. @RestController
and @Controller
classes must translate exceptions into proper JSON structures (XML is not in focus).
Solution In order to implement Spring @ExceptionHandler
or ControllerAdvice
in every controller take advantage of a set of those by extending the class org.openwms.core.http.AbstractWebController
. Own exception classes and those of the ameba-lib library are translated properly.