The Nemesis Platform has a
two-level architecture. The respective layers are
facade. Each module defines separate packages for the
core layer and for the
facade layer, both of them being
The core layer is the bottom layer, that is very near to the database. In this layer, we usually put our
org.springframework.data.repository.Repository repositories, our services and the database entities.
Everything that is database-related resides here.
The Nemesis Platform uses the latest JPA implementation to interact with the database. However, because of the limitations of the JPA specification, and also to maintain the modularity of the Platform, we are forced to
use interfaces instead of classes for modeling the database layer. These interfaces are called
Definition-s and contain the getter/setter methods of every property we need to have. The getter methods in the
Definition-s must be annotated with the JPA annotations we need to have on a database entity. Here’s an example of a database entity definition:
Unresolved directive in <stdin> - include::/opt/bitnami/apps/jenkins/jenkins_home/workspace/nemesis-platform-master/nemesis-platform-core/src/main/java/com/nemesis/platform/core/definition/catalog/CatalogEntityDefinition.java[tags=class]
Once we have the
Definition-s declared, the Nemesis Platform will perform entity generation at compile time and will produce the final JPA entity classes. To learn more about the
Definition-s and the entity generation please read this article.
Definition-s are good to model the database layer, but we also need a convenient way to perform CRUD operations with them. The Nemesis Platform uses Spring Data repositories to achieve this. Here’s an example of a Spring Data repository:
Unresolved directive in <stdin> - include::/opt/bitnami/apps/jenkins/jenkins_home/workspace/nemesis-platform-master/nemesis-platform-core/src/main/java/com/nemesis/platform/core/repository/jpa/catalog/CatalogRepository.java[tags=class]
Please make sure your database repositories reside in the correct package. There’s a convention over configuration in place which expects the
database repositories to be located under
Spring Data repositories are nice and easy way to perform basic CRUD operations in the database layer. However, there might be a time when these simple CRUD operations are not enough. To leverage this one can use a
Service which abstracts some business logic on top of the repositories. There are no specific requirements as to what a service must contain or how it should be declared.
Here’s an example of a service:
Unresolved directive in <stdin> - include::/opt/bitnami/apps/jenkins/jenkins_home/workspace/nemesis-platform-master/nemesis-platform-core/src/main/java/com/nemesis/platform/core/service/catalog/CatalogService.java[tags=class]
Services are not auto-discovered, so they
must be explicitly declared as
As we’ve already seen, the core layer deals with the database JPA entities - the methods of the Spring Data repositories or the services - accept JPA entity definitions and return JPA entity definitions. However, these objects turn out to be very heavy (for instance, product one may contain localizations in hundreds of languages, even though we only need one - the localization in the currency system locale). To offload these heavy objects, we have introduced the facade layer. It has the following responsibilities:
to get a JPA entity (using either the Spring Data repositories, or the services defined in the core);
to convert the JPA entity into lightweight POJO objects;
to hand the POJOs to the Controllers and to the presentation layer;
On this way we don’t need to keep open the transaction in the view layer (because the view layer now operates with POJOs and not with JPA proxies). Instead, the facade layer uses
@Transactional to open/close the transaction before/after the execution of the facade method. The facade layer normally contains facades, mappers and Dto objects, each of which we will describe below.
As already mentioned, the facade classes have several responsibilities:
to open the transaction;
to load the entities;
to convert the entities to lightweight POJOs;
to return the POJOs and close the transaction.
Opening/closing the transaction is achieved by specifying the
@Transactional annotation on each of the facades methods that needs a transaction to operate. Loading the JPA entities is achieved by calling the approriate methods in some of the Spring Data repositories or services that were defined in the core layer. For converting the heavyweight JPA entity into a lightweight Dto object (POJO), we use converters and populators.
The converters in the Nemesis Platform are Spring beans which are implementations of
org.springframework.core.convert.converter.Converter<S, T>. Here is defined only one method -
convert(S source, T target).
The converters, just as the facades, are Spring beans which need to be declared in the corresponding Spring configuration. Converters specify how an object of one kind can be converted to an object of another kind.
The mappers, on the other hand, are instances of
.MapperFactoryConfigurer which specifies how
fields in one class can be mapped to fields in another class, also allowing default mapping (fields with same name get populated automatically).
The Dto objects are simple Java POJO lightweight classes. They are the result of the facade layer and are handed to the presentation layer. You can learn more about the Dto objects in the respective guide.