Backend

The backend of openVALIDATION-IDE is a REST-API written in Java (version 8) with Spring Boot as the underlaying technology.

API-Documentation

You can find the official OpenAPI documentation through a Swagger-UI at http://openvalidation-ide.azurewebsites.net/swagger-ui.

Simplified data model

Ruleset

Field

Description

rulesetId

Unique identifier of a ruleset

name

Name of the ruleset, shown in UI

description

Description of the ruleset, shown in UI

createdAt

Date of ruleset creation

createdBy

Actually a user name as a string, in future this will be a entity User

lastEdit

Date of last edit

rules

Contains all rules of a ruleset

Schema

Field

Description

schemaId

Unique identifier of a schema

Attribute

Field

Description

attributeId

Unique identifier of an attribute

name

Name of the attribute, shown in UI

attributeType

Type of attribute. Can be BOOLEAN, NUMBER, TEXT, LISTorOBJECT

value

Is a string with a sample value of the attribute (currently not in usage)

children

An attribute can have sub-attributes, if theattributeType is LIST or OBJECT

Architecture & architectural decisions

The architecture of the openVALIDATION-IDE is divided through a Web-, Service- and Repository Layer. The basic idea behind this is that only contiguous layers communicate to each other. Furthermore, method calls always happen only from the upper to the lower layer.

The Web Layer only contains the controllers, whereas these controllers should be as "dumb" as possible. They should just forward the incoming API-calls to the services. Between the Web- and the Service Layer only Data Transfer Objects (DTO) are passed. Through the Service-Layer, entities and controllers are encapsulated. The services contain the business logic, what also ensures that the controllers remain clean. Also exception handling happens in the services. Between the Service- and the Repository Layer, only entities are passed. The Repository Layer only contains the repositories.

Package structure

The structure of packages is inspired by Domain-Driven-Design.

The package domain holds all classes that are part of the domain. Inside of the domain package, there is a sub-package for each aggregate. The packages for the aggregates, which are ruleset and the schema, contain all the classes which belong to this aggregate. We decided, that only entities will get an own package below the aggregate packages. All other classes remain on the root of the aggregate/entity package.

The infrastructure package contains all classes for configuration of the infrastructure. In the package database are the classes for initializing and resetting the database. The package httpclient contains a class CorsConfig for configuring the CORS-Header.

Schema as an own aggregate

We decided to treat the schema as an own aggregate and independently of a ruleset, because it should be possible to share schemas between rulesets.

Currently, a new schema will be created by creating a ruleset. At the moment it is also not possible to manage schemas independent of a ruleset. There is an issue about that on GitHub, this is one of the next items on the agenda.

DTO's

One benefit of a DTO is, that we are free to decide for every special case, which information of our entities we want to reveal through our REST-API. Furthermore we have the freedom of changing our internal data model, without being forced to directly change our endpoints. We decided to implement DTO's inspired by CQRS-principles. We distinguish between three different kinds of DTO's:

DTO-Type

Usage

General-DTO

DTO's which are given back to the user through the REST-API

Create-DTO

DTO's which are received through the request body of a HTTP-POST

Update-DTO

DTO's which are received through the request body of a HTTP-PUT

The General-DTO gives us the opportunity, to only give that amount of information outwards, that we want to give to the consumer of our REST-API. Whereas the benefit of Create-and Update-DTO's are, to clearly specify which information we want to receive if an entity will be created or updated. This saves us a lot of validation work, because we always receive exactly the information we need in every case.

For mapping between DTO's and entities we use the external library MapStruct. Each entity has its own mapper.

Initialize and reset the database

DatabaseInitializer

The DatabaseIntializer is responsible for initializing the database with some sample data. The initialization will be executed at the application startup. Initial data will only be created, if the database is empty. Also, there are some out commented rules in the DatabaseInitializer. We have taken all sample rules from the official playground of openVALIDATION. Especially rules are commented out, where the syntax highlighting is not correctly shown in the openVALIDATION-IDE. The syntax highlighting is shown wrong, because currently (April 18, 2020) the language server does not provide finely granulated information on syntax highlighting, so that we could correctly syntax highlight these rules. Nevertheless, we have already prepared the rules so that they can easily be uncommented as soon as the language server provides the necessary information for syntax highlighting.

Reset-Endpoint

There is a API-endpoint /reset for resetting the database to an initial state. This API-endpoint was implemented, because currently (July 11, 2020) the openVALIDATION-IDE does not provide a user management and everybody can play around with the rules at the official demo of openVALIDATION-IDE. This endpoint will be called once a day by a cron-job, so that the online-data is always clean. This endpoint can be obfuscated through an environment variable.

Unit- & Integration-Tests

The functionality of the backend can be tested by a set of Unit and integration tests.

Since the application logic is encapsulated in the services, these are validated as part of unit tests. The RulesetServiceTest covers the logic behind the ruleset endpoint and thus focuses on the interaction with the Ruleset entity. The SchemaServiceTest includes the logic of the schema endpoint, which describes the interaction with the Schema and Attribute entity. In this case, the repository layer is mocked.

The integration tests include the interaction of the different application layers. The API endpoints, i.e. the controllers in the web layer, are used as the entry point. As a result, the HTTP status codes and content type generated by the endpoint are validated. The entire application process is carried out internally. The controllers in the web layer communicate with the internal services that execute the application logic using the specific DTOs. The services communicate with the repository layer. A temporary in-memory H2 database is used as part of the integration tests. The RulesetIntegrationTest refers to the ruleset endpoint, while the SchemaIntegrationTest covers the schema endpoint.

In both test types, the application behavior is tested on the one hand in the context of valid inquiries. However, the behavior of the application in the event of errors is also validated. These error cases are aimed at invalid arguments within the requests from outside that cannot be intercepted by a simple input validation or syntax check.

Environment variables

The table bellow shows the environment variables which can be configured.

Variable

Default Value

Description

SPRING_PROFILES_ACTIVE

dev

Profile

OPENVALIDATION_IDE_DB

postgres

Database technology

OPENVALIDATION_IDE_DB_NAME

Database name

OPENVALIDATION_IDE_DB_USER

Database username

OPENVALIDATION_IDE_DB_PW

Database password

CORS_HEADERS

Allowed CORS Headers

RESET_SECRET

Secret for obfuscating /reset

SPRING_PROFILES_ACTIVE: There are two Spring Profiles to choose between: default and dev. The only difference between these profiles is the database technology. If the default profile is activated, the data is stored in a h2 in-memory database, which in turn means that the data is only persistent until the application is stopped. With the dev profile active, the data is persistent in a PostgreSQL database. Currently there is no profile for productional usage available, because the openVALIDATION-IDE is still work in progress.

Database configuration: The environment variables OPENVALIDATION_IDE_DB, OPENVALIDATION_IDE_DB_NAME, OPENVALIDATION_IDE_DB_USER and OPENVALIDATION_IDE_DB_PW are used to connect to a database.

CORS_HEADERS: In order to allow Cross-Origin Resource Sharing, this environment variable can be used to configure the allowed URLs. Multiple URLs are specified comma separated one after the other ( for example: https://sample-1.com,https://sample-2.com).

RESET_SECRET: This environment variable can be used to obfuscate the /reset endpoint. For this purpose, any secret is assigned here, so that this endpoint can only be accessed by additionally specifying this secret in the URL: /reset/[secret] . If this environment variable is not set, this endpoint can be reached without specifying a secret by using /reset.

Last updated