SpringBot File Structure
Almost all files written by the bot are inferred from the model itself, with some exceptions where it is more useful to abstract a lot of things into their own standalone components.
The general repository file structure can be seen in File Structure.
Server-Side Directory Structure
See below for the current project structure with simple descriptions.
Source - Top Level Package (src/main/java/com/springbot/basic)
.
├── configs
│ └── security
│ ├── auditing
│ │ ├── entities
│ │ ├── repositories
│ │ └── services
│ ├── authorities
│ ├── evaluators
│ ├── expressions
│ ├── filters
│ ├── helpers
│ ├── meta
│ ├── repositories
│ └── services
├── controllers
├── deserializers
├── entities
│ └── listeners
├── graphql
│ └── resolvers
│ ├── mutation
│ └── query
├── lib
│ ├── graphql
│ │ └── errors
│ └── services
│ └── email
├── repositories
├── serializers
└── services
Package | Purpose |
---|---|
configs | All application configuration is found here. This includes any overriding or new config such as security. |
configs.security | Security config |
controllers | All application controllers, primarily used for all REST API endpoints. |
deserializers | Custom deserializers for the result of each of the controllers. Overrides the default Spring deserializers. These are only used for the REST controllers. |
entities | Annotated entities; these represent the data structures present in the application. |
entities.listeners | Listeners to allow actions to be performed pre-post fetch/save etc. |
graphql | GraphQL configuration, including mutations queries and utils among other graphql specific classes. |
lib | Helpers and services that don’t fall in the above or below packages. |
repositories | JPA repositories, these work similar to a DAO (Data access object) and act as the interface between the database and the entities. |
serializers | Custom serializers for the result of each of the controllers. Overrides the default Spring serializers. |
services | Services for the application. |
Entities
Contains files to declare our entity classes from the Entity Diagram.
If you have a look at an entity, you will see that they heavily use annotations to set up constructors, getters, setters, and database mappings that would normally be done with a fair amount of boilerplate code.
All entities extend AbstractEntity
, which can also be found in this package.
AbstractEntity
contains the following attributes:
- UUID
- created date
- modified date time
- UUID modifiedBy (which user last modified the entity)
- UUID createdBy (which user created the entity)
Each attribute in the class will have several annotations. Here is an example:
@ApiModelProperty(notes = "The Identifications that are related to this entity.")
@OneToMany(mappedBy = "accreditedParty", cascade = {CascadeType.MERGE}, fetch = FetchType.LAZY)
@NotEmpty(message = "Identification is required")
private Set<IdentificationEntity> identifications = new HashSet<>();
- API Model Property only used for Swagger API docs - This takes doco description from the attribute in your Entity Diagram
- OneToMany - describes the type of relationship (if the attribute is a related entity). We recommend this Baeldung article to learn more about relationship types.
- NotEmpty - validation - this can be set by adding validators to your attributes in the entity model. This will prevent entities being saved to the database if this field is not set. It will return the error message through the API.
The rest of the class is custom getters, setters, and functions to add or remove related entities from this entities list of relationships.
E.g.:
public void addIdentification(IdentificationEntity identification) {
addIdentification(identification, true);
}
public void addIdentification(@NonNull IdentificationEntity identification, boolean doReverseAdd) {
if (!identifications.contains(identification)) {
identifications.add(identification);
if (doReverseAdd) {
identification.setAccreditedParty(this, false);
}
}
}
public void setIdentifications(Set<IdentificationEntity> identifications) {
setIdentifications(identifications, true);
}
public void setIdentifications(Set<IdentificationEntity> identifications, boolean doReverseAdd) {
this.identifications.forEach(IdentificationEntity::unsetAccreditedParty);
this.identifications = identifications;
if (doReverseAdd) {
this.identifications.forEach(identificationEntity -> identificationEntity.setAccreditedParty(this, false));
}
}
public void removeIdentification(IdentificationEntity identification) {
this.identifications.remove(identification);
}
public void unsetIdentifications() {
this.identifications.forEach(IdentificationEntity::unsetAccreditedParty);
this.identifications.clear();
}
The entities package also contains some sub-packages:
- enums -simple enumerated entities which have literal values SCREAMING_CASE, as defined in the Entity Diagram. The enumerated have a literal string value which is used for display and readability purposes. In the database, these enumerated keys are stored as Integer values.
- Listeners - triggers that are called before and after persistence and fetch and allow for things to be altered before they are saved and after they are queries.
- Specifications - rarely used but is a place for custom GraphQL configuration.
graphql
This package contains the logic to resolve all GraphQL requests sent to the server-side.
serverside/src/main/java/applicationName/graphql
.
└── src/main/java/applicationName
└── graphql
├──resolvers
│ ├── mutation
│ └── query
├── utils
├── CustomGraphQLErrorHandler
└── GraphQLCombiner
Graphqlcombiner
is the top level resolvers, which is made up of the resolvers found in the resolvers
package.
For more information please see the documentation for GraphQL Java.
Resources (src/main/resources)
.
├── graphql
│ └── schemas
├── logback
├── static
└── application
└── properties
graphql.schemas
Contains GraphQL files which declare JSON structure of our Entities from our Entity Diagram.
These schemas define what queries and mutations our GraphQL API supports.
For more information, please refer to the Java GraphQL library.
logback
Contains settings to change how our logging platform of choice works. In this file, you can set the max file size of the logs or the file name pattern. To make changes to this file remember to turn on the protected region
static
Static is where client-side applications will be stored. If you want to serve a client-side from your server-side, you can compile our client-side application and put it inside the static folder. When you run the server-side and navigate to it, it will try to serve any index.html
that is stored inside the static folder.
application.properties
These property files contain properties for the corresponding environment e.g: application-dev
for development.
This is where you define login info for your database. By default you will have something like the following in your application-dev.properties
:
# % protected region % [Customise your connection details here] off begin
spring.datasource.url=${DATA_SOURCE_URL:jdbc:postgresql://localhost:5432/postgres}
spring.datasource.username=${DATA_SOURCE_USERNAME:user}
spring.datasource.password=${DATA_SOURCE_PASSWORD:}
spring.datasource.platform=postgres
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=${HIBERNATE_DDL:update}
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
springfox.documentation.swagger.v2.host=localhost:8080
graphiql.cdn.enabled=true
voyager.cdn.enabled=true
altair.cdn.enabled=true
# % protected region % [Customise your connection details here] end
To be able to run your server-side application you will need to either set up PostgreSQL and ports to match the default settings in this file, or pass in environment variables
in your run configuration to override them.
e.g: export DATA_SOURCE_URL=jdbc:postgresql://localhost:5432/applicationName
Client-Side Structure
.
└── src
├── app
│ ├── admin
│ │ ├── pages
│ │ └── tiles
│ ├── enums
│ ├── frontend
│ ├── lib
│ │ ├── components
│ │ ├── directives
│ │ ├── enums
│ │ ├── guards
│ │ ├── interceptors
│ │ ├── models
│ │ ├── pipes
│ │ ├── routing
│ │ ├── scss
│ │ ├── services
│ │ ├── tiles
│ │ └── utils
│ ├── models
│ │ └── ...
│ ├── pages
│ │ └── ...
│ ├── services
│ │ └── ...
│ ├── tiles
│ │ ├── crud
│ │ └── custom
│ └── views
│ ├── ...
├── assets
│ ├── fonts
│ └── img
└── environments
Package | Purpose |
---|---|
src.app.admin | Container for the admin section. This contains all admin pages and components. |
src.app.admin.pages | All admin pages and their associated routes. |
src.app.admin.tiles | All admin tiles. |
src.app.admin.tiles.crud | Admin CRUD (Create, Read, Update and Delete) tiles. |
src.app.admin.tiles.crudDashboard | Admin Dashboard tile. |
src.app.enums | Enumerated values that have been modelled for the application. These are application specific enums based on the Entity Diagram. |
src.app.frontend | Core frontend component that wraps the entire frontend of the application. All components that belong to the frontend are contained within this core component. Common frontend elements such as navigation can be found here. |
src.app.lib | Shared components and utils. |
src.app.lib.components | Common application components; these are mostly elements such as the date picker etc. |
src.app.lib.directives | Shared directives used within the application. See Directive for more details. |
src.app.lib.enums | Common global enums that are required by every application. |
src.app.lib.guards | Guards to manage route access, see Routing and Navigation for more details. |
src.app.lib.intercepters | Intercepters used globally within the application, see HttpIntercepter for more details. |
src.app.lib.models | Common models used by every application. |
src.app.lib.pipes | Common pipes available to be used in the application. See Pipes for more details. |
src.app.lib.routing | Constructs used to assist in routing. See Routing and Navigation for more details. |
src.app.lib.scss | SCSS files that can be used to customise the application. Without customisation, these will simply default to Harmony styling. |
src.app.lib.tiles | Core tiles that every application will require. Found among these can be login, logout etc. |
src.app.lib.utils | Common utils. Data factories can be found here. |
src.app.models | Models for all objects in the application, these models also contain state management classes such as reducers, selectors, effects, and actions each given model. These are application specific and are based on the Entity Diagram. |
src.app.pages | Pages as modelled in the UX Diagram. |
src.app.services | Services for each of the objects in the application. These are application specific and are based on the Entity Diagram. |
src.app.tiles | Tiles as modelled in the UX Diagram. |
src.app.tiles.crud | CRUD (Create, Read, Update, Delete) tiles, if they have been added to the UX Diagram. |
src.app.tiles.crud.custom | Custom tiles, if they have been added to the UX Diagram. |
src.app.views | Views as modelled in the UX Diagram. |
src.assets.font | Application fonts. |
src.assets.img | Application images. |
src.environments | Environment settings; these are different for dev and beta. |
Test Target Structure
The Java testing framework is driven by a collection of libraries that aid conducting unit tests, API tests, and integration tests. The integration testing is primarily written through the Selenium library and Cucumber framework to ensure user acceptance criteria are achieved and written in a logical language that is understandable by non- techies. The unit testing and API testing is conducted through the in-built Spring Boot testing framework.
(src/main/test)
.
├── feature
├── stepdefs
├── shared
├── managers
├── runners
├── configuration
└── pom
Package | Purpose |
---|---|
feature | All feature files are located here. |
stepdefs | All step definitions for the feature files |
shared | The test context as well as all other configuration and setup logic |
managers | Managers are classess that adopt the mediator pattern to extract all complex functionality of the testing framework to their respective managers . |
configuration | Configuration classes are used to import configuration files from the resources directory. this directory also contains helper classes and methods that aid in importing these files and configurating the testing framework. |
pom | All custom wrappers for web elements and their configuration are located here . |
runners | Runners are the entry point of the testing target. There are pre-defined suite runners for active developement, production deployment, and more, which define the set of tests that should be run. |
AsSuite of feature files, step definitions, and page object models will be bot-written for all applications for common web components and application processes. These include login and logout, entity CRUD, user CRUD, navigation, and others.
Some of these bot-written tests may or may not be relevant to the system under testing. For that reason, all Cucumber tests have been tagged with @skip
and by default will not be executed by the test runners. To ensure they are run if they are relevant to the application, remove this tag from their scenario.
(src/main/resources)
.
├── output
│ ├── uat
│ └── screenshot
└── config
Package | Purpose |
---|---|
output | All files that are out putted from the testing target should be located here. This includes uat videos for tagged tests and screen shots for failed tests and more. |
config | All configuration arguments are specified within their respective configuration file and placed here. This allows users to tweak their testing environment by simply adding a key-value pair. |
Application Properties
Application properties can be found under serverside/src/main/resources
. Here we have the various profiles that can be run as well as the default properties that are included in every profile.
Most of the properties that need to be changed can be done via environment variables. The following are available to be changed in this manner,
- DATA_SOURCE_URL - Your database connection string
- DATA_SOURCE_USERNAME - Your database username
- DATA_SOURCE_PASSWORD - The password to access your database,
- HIBERNATE_DDL - (Default to update) See Initialize a Database Using Hibernate for the available options and what they mean.
For the options that cannot be set in this manner, you can edit the files directly by first activating the protected regions.
You can also save in a file called env.sh
and source it with source env.sh
to simplify the process.
Was this article helpful?