Overall architecture

The Nemesis Platform has a two-level architecture. The respective layers are core and facade. Each module defines separate packages for the core layer and for the facade layer, both of them being clearly separated.

In a brief

Core layer

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.

JPA Entities

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:

io.nemesis.platform.core.definition.catalog.CatalogEntityDefinition.java
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.

Repositories

The entity 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:

io.nemesis.platform.core.repository.jpa.catalog.CatalogRepository.java
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 io.nemesis.platform.core.repository.jpa or io.nemesis.platform.module.**.core.repository.jpa.

Services

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:

io.nemesis.platform.core.service.catalog.CatalogService.java
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 @Bean-s in the respective configuration.

Facade layer

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.

Facades

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.

MapperFactoryConfigurers

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 io.nemesis.platform.core.mapper .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).

Dto objects

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.

asciidoctor diagram process 1.5