Skip to content

devon4j components

devonfw-core edited this page Dec 16, 2021 · 20 revisions

Components

Architecture

When working with devon4j the recommended approach for designing an applications is Component Oriented Design. Each component will represent a significant part (or feature) of our application related to CRUD operations. Internally, the components will be divided into three layers (service, logic, and dataaccess) and will communicate in two directions: service with database or — in the logic layer — a component with another component.

Principles

The benefits of dividing our application into components are:

Component Structure

A component consists of three packages, which correspond to the three layers defined by the devon4j architecture: service, logic and dataaccess.

Component Layers
  • Service layer: exposes the REST API to exchange information with client applications

  • Logic layer: in charge of hosting the business logic of the application

  • Data Access layer: communicates with the database

Apart from that, most components will have a fourth package — common — to store shared elements, which will be used by all layers of the component. It will contain common interfaces, constants, exceptions or enumerations.

Component Core

As we mentioned earlier, each component will be related to a functionality. This functionality will be represented in code by an Entity that defines all the properties needed to wrap the logic of that feature.

This Entity represents the "core" of the component and will be located in the dataaccess.api package.

The naming convention for these entities in devon4j is:

[Target]Entity

"Target" should match the name of the related table in the database — although this is not mandatory.

Basically, each Entity is a POJO (plain old Java object) that will be mapped to a table in the database and represent each column via a suitable property.

Example Entity
Example: An entity and its corresponding table in the DB (taken from another application).

Create your Components

We are now going to create our first app components. Our example application needs to provide two basic functionalities:

  • register a user (returning an access code)

  • show registered queue members

To accomplish this we are going to work with three entities; Visitor, Queue and AccessCode:

JumpTheQueue Entities

The components will be defined as follows:

Visitor Access Code Daily Queue

username

ticketNumber

name

name

creationTime

logo

password

startTime

password

phoneNumber

endTime

currentNumber

acceptedCommercial

 — 

attentionTime

acceptedTerms

 — 

minAttentionTime

userType

 — 

active

 — 

 — 

customers

In addition, we will have to represent two relationships:

  1. The one to one relation between Visitor and Access Code.

  2. The one to many relation between Daily Queue and Access Code.

Now is the moment to decide about the components of our app. The low complexity of the functionality would allow us to create only one component for managing all entities. In order to clarify the example we are going to create three managing components however; one for Visitor, one for Access Code and one for Daily Queue.

ℹ️

If you feel more comfortable managing all the entities in a single component, you could also do it this way. The result will be the same, the only difference will be the structure of some elements and the distribution of code inside the packages.

The Database

Projects created with the devon4j archetype already contain a pre-defined database schema, which we can use as a basis to create our own. We are going to utilize the H2 Database Engine, because our generated devon4j application uses it by default.

There are four pre-defined database schemas:

jtqj-core/src/main/resources/db/type/h2/V0001__Create_Sequence.sql
jtqj-core/src/main/resources/db/type/h2/V0002__Create_RevInfo.sql
jtqj-core/src/main/resources/db/type/h2/V0003__Create_BinaryObject.sql
jtqj-core/src/main/resources/db/migration/1.0/V0004__Add_blob_data.sql
ℹ️

May be you need to install some SQL editor from eclipse marketplace, or use an external one.

Visitor Table

We are going to create our own table for Visitor(s) by right-clicking the folder /jtqj-core/src/main/resources/db/migration/1.0 and selecting New > File. Following the naming scheme we are going to call it:

V0005__Create_Visitor.sql

A visitor will provide: username, name, password, phoneNumber, acceptedCommercial and acceptedTerms in order to obtain an Access Code. We need to represent this data in our table:

create table Visitor(
  id BIGINT NOT NULL AUTO_INCREMENT,
  modificationCounter INTEGER NOT NULL,
  username VARCHAR(255),
  name VARCHAR(255),
  password VARCHAR(255),
  phoneNumber VARCHAR(255),
  acceptedCommercial BOOL DEFAULT '0',
  acceptedTerms BOOL NOT NULL DEFAULT '0',
  userType BOOL DEFAULT '0',
  CONSTRAINT PK_Visitor PRIMARY KEY(id)
);
  • id: The ID of each visitor.

  • modificationCounter: Used internally by JPA to take care of optimistic locking for us.

  • username: The visitors email address.

  • name: The visitors name.

  • password: The visitors password.

  • phoneNumber: The visitors phone number.

  • acceptedCommercial: A boolean to denote if the visitor has the accepted commercial agreements.

  • acceptedTerms: A boolean to denote if the visitor has accepted the terms & conditions.

  • userType: Denotes the type of user.

Daily Queue Table

In a second table we will represent the Daily Queue, which will contain name, logo, currentNumber, attentionTime, minAttentionTime, active and customers. This table will be created in /jtqj-core/src/main/resources/db/type/h2, and is called:

V0006__Create_Queue.sql

It will contain the following declarations:

create table DailyQueue(
  id BIGINT NOT NULL AUTO_INCREMENT,
  modificationCounter INTEGER NOT NULL,
  name VARCHAR(255),
  logo VARCHAR(255),
  currentNumber VARCHAR(255),
  attentionTime TIMESTAMP,
  minAttentionTime TIMESTAMP NOT NULL DEFAULT '60000',
  active BOOL NOT NULL DEFAULT '1',
  customers INTEGER NOT NULL DEFAULT '0',
  CONSTRAINT PK_DailyQueue PRIMARY KEY(id)
);
  • id: The ID of each queue.

  • modificationCounter: Used internally by JPA to take care of optimistic locking for us.

  • name: The queues name.

  • logo: The queues logo.

  • currentNumber: the queue’s number being attended.

  • attentionTime: Average time required to attend a visitor.

  • minAttentionTime: Minimum time required to attend a visitor, set by default.

  • active: A boolean to denote if the queue is active.

  • customer: The queues total number of customers.

Access Code Table

The third table will represent the Access Code and contain the ticketNumber, creationTime, startTime and endTime. This table will be created in /jtqj-core/src/main/resources/db/type/h2, and is called:

V0007__Create_Access_Code.sql

It will contain the following declarations:

CREATE TABLE AccessCode(
  id BIGINT NOT NULL AUTO_INCREMENT,
  modificationCounter INTEGER NOT NULL,
  ticketNumber VARCHAR(5),
  creationTime TIMESTAMP,
  startTime TIMESTAMP,
  endTime TIMESTAMP,
  idVisitor BIGINT NOT NULL,
  idQueue BIGINT NOT NULL,
  CONSTRAINT PK_AccessCode PRIMARY KEY(id),
  CONSTRAINT FK_AccessCode_idVisitor FOREIGN KEY(idVisitor) REFERENCES Visitor(id),
  CONSTRAINT FK_AccessCode_idQueue FOREIGN KEY(idQueue) REFERENCES DailyQueue(id)
);
  • id: The ID of each code.

  • modificationCounter: Used internally by JPA to take care of optimistic locking for us.

  • ticketNumber: The number of the ticket for a queue.

  • creationTime: The date and time of creation.

  • startTime: The date and time, from which the code is valid.

  • endTime: The date and time, when the code expires.

  • idVisitor: The relation with the Visitor table.

  • idQueue: The relation with the DailyQueue table.

Mock Data

Finally we are going to provide a certain amount of mock data, which will be available right from the start in our application. Create a new SQL script in /jtqj-core/src/main/resources/db/migration/1.0/, called:

V0008__Master_data.sql

Copy and paste the following data into it:

INSERT INTO Visitor (id, modificationCounter, username, name, password, phoneNumber, acceptedCommercial, acceptedTerms, userType) VALUES (0, 1, 'mike@mail.com', 'test', '1', '123456789', '0', '1', '1');
INSERT INTO Visitor (id, modificationCounter, username, name, password, phoneNumber, acceptedCommercial, acceptedTerms, userType) VALUES (1, 1, 'peter@mail.com', 'test', '1', '123456789', '1', '1', '0');
INSERT INTO Visitor (id, modificationCounter, username, name, password, phoneNumber, acceptedCommercial, acceptedTerms, userType) VALUES (2, 1, 'pablo@mail.com', 'test', '1', '123456789', '0', '1', '0');
INSERT INTO Visitor (id, modificationCounter, username, name, password, phoneNumber, acceptedCommercial, acceptedTerms, userType) VALUES (3, 1, 'test1@mail.com', 'test', '1', '123456789', '0', '1', '0');
INSERT INTO Visitor (id, modificationCounter, username, name, password, phoneNumber, acceptedCommercial, acceptedTerms, userType) VALUES (4, 1, 'test2@mail.com', 'test', '1', '123456789', '1', '1', '0');
INSERT INTO Visitor (id, modificationCounter, username, name, password, phoneNumber, acceptedCommercial, acceptedTerms, userType) VALUES (5, 1, 'test3@mail.com', 'test', '1', '123456789', '0', '1', '0');
INSERT INTO Visitor (id, modificationCounter, username, name, password, phoneNumber, acceptedCommercial, acceptedTerms, userType) VALUES (6, 1, 'test4@mail.com', 'test', '1', '123456789', '0', '1', '0');
INSERT INTO Visitor (id, modificationCounter, username, name, password, phoneNumber, acceptedCommercial, acceptedTerms, userType) VALUES (7, 1, 'test5@mail.com', 'test', '1', '123456789', '1', '1', '0');
INSERT INTO Visitor (id, modificationCounter, username, name, password, phoneNumber, acceptedCommercial, acceptedTerms, userType) VALUES (8, 1, 'test6@mail.com', 'test', '1', '123456789', '0', '1', '0');
INSERT INTO Visitor (id, modificationCounter, username, name, password, phoneNumber, acceptedCommercial, acceptedTerms, userType) VALUES (9, 1, 'test7@mail.com', 'test', '1', '123456789', '0', '1', '0');

INSERT INTO DailyQueue (id, modificationCounter, name, logo, currentNumber, attentionTime, minAttentionTime, active, customers) VALUES (1, 1, 'Day2', 'C:/logos/Day1Logo.png', 'Q001', NULL, '1970-01-01 00:01:00', TRUE, 9);

INSERT INTO AccessCode (id, modificationCounter, ticketNumber, creationTime, startTime, endTime, idVisitor, idQueue) VALUES (1, 1, 'Q001', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, 1, 1);
INSERT INTO AccessCode (id, modificationCounter, ticketNumber, creationTime, startTime, endTime, idVisitor, idQueue) VALUES (2, 1, 'Q002', CURRENT_TIMESTAMP, '2008-01-01 00:00:01', NULL, 2, 1);
INSERT INTO AccessCode (id, modificationCounter, ticketNumber, creationTime, startTime, endTime, idVisitor, idQueue) VALUES (3, 1, 'Q003', CURRENT_TIMESTAMP, '2008-01-01 00:00:01', NULL, 3, 1);
INSERT INTO AccessCode (id, modificationCounter, ticketNumber, creationTime, startTime, endTime, idVisitor, idQueue) VALUES (4, 1, 'Q004', CURRENT_TIMESTAMP, '2008-01-01 00:00:01', NULL, 4, 1);
INSERT INTO AccessCode (id, modificationCounter, ticketNumber, creationTime, startTime, endTime, idVisitor, idQueue) VALUES (5, 1, 'Q005', CURRENT_TIMESTAMP, '2008-01-01 00:00:01', NULL, 5, 1);
INSERT INTO AccessCode (id, modificationCounter, ticketNumber, creationTime, startTime, endTime, idVisitor, idQueue) VALUES (6, 1, 'Q006', CURRENT_TIMESTAMP, '2008-01-01 00:00:01', NULL, 6, 1);
INSERT INTO AccessCode (id, modificationCounter, ticketNumber, creationTime, startTime, endTime, idVisitor, idQueue) VALUES (7, 1, 'Q007', CURRENT_TIMESTAMP, '2008-01-01 00:00:01', NULL, 7, 1);
INSERT INTO AccessCode (id, modificationCounter, ticketNumber, creationTime, startTime, endTime, idVisitor, idQueue) VALUES (8, 1, 'Q008', CURRENT_TIMESTAMP, '2008-01-01 00:00:01', NULL, 8, 1);
INSERT INTO AccessCode (id, modificationCounter, ticketNumber, creationTime, startTime, endTime, idVisitor, idQueue) VALUES (9, 1, 'Q009', CURRENT_TIMESTAMP, '2008-01-01 00:00:01', NULL, 9, 1);

The Core of the Components

Now that we have defined the database for our entities, we should start creating the code of the related components.

We are going to use CobiGen to generate the component structure. That means that — as already commented — we can generate all the structure and layers starting from a core element: a simple Plain Old Java Object that represents our Entity. So, in order to use CobiGen, we have to create our entities in the expected locations (as you will see in the following section): <entitymanagement>.dataaccess.api.

Visitor Component

To implement the component we will need to define a VisitorEntity to connect and manage the data of the Visitor table in the database. The name of this component will be visitormanagement, the entity will be called VisitorEntity.

Right-click on the root folder of the project /jtqj-core/src/main/java, select New > Package and create the following package:

com.devonfw.application.jtqj.visitormanagement.dataaccess.api
New Package Creation Step 1
New Package Creation Step 2

Now create a new Java class in this package and call it VisitorEntity:

New Class Creation

We are going to need fields, which represent the data model, so our entity should contain the following code:

  ...

  private String username;

  private String name;

  private String phoneNumber;

  private String password;

  private Boolean acceptedCommercial;

  private Boolean acceptedTerms;

  private Boolean userType;

  ...
ℹ️

We are not adding id or modificationCounter, because CobiGen will generate these fields for us.

Now we need to declare our entity as a JPA entity with the @Entity annotation (javax.persistence.Entity) at class level. To map the entity to the database table, we will use the @Table annotation (javax.persistence.Table) defining the name of our already created Visitor table (also at class level):

...

@Entity
@Table(name = "Visitor")
public class VisitorEntity {

  ...

Now we have to declare the getter and setter methods for the fields of our entity. We can do this manually or automatically generate them using Eclipse:

Generating Getter and Setter Methods with Eclipse

The resulting implementation of our VisitorEntity class should now look like this:

package com.devonfw.application.jtqj.visitormanagement.dataaccess.api;

import javax.persistence.Entity;
import javax.persistence.Table;

@Entity
@Table(name = "Visitor")
public class VisitorEntity {

  private String username;

  private String name;

  private String phoneNumber;

  private String password;

  private Boolean acceptedCommercial;

  private Boolean acceptedTerms;

  private Boolean userType;

  /**
   * @return the username
   */
  public String getUsername() {
    return username;
  }

  /**
   * @param username the username to set
   */
  public void setUsername(String username) {
    this.username = username;
  }

  /**
   * @return the name
   */
  public String getName() {
    return name;
  }

  /**
   * @param name the name to set
   */
  public void setName(String name) {
    this.name = name;
  }

  /**
   * @return the phoneNumber
   */
  public String getPhoneNumber() {
    return phoneNumber;
  }

  /**
   * @param phoneNumber the phoneNumber to set
   */
  public void setPhoneNumber(String phoneNumber) {
    this.phoneNumber = phoneNumber;
  }

  /**
   * @return the password
   */
  public String getPassword() {
    return password;
  }

  /**
   * @param password the password to set
   */
  public void setPassword(String password) {
    this.password = password;
  }

  /**
   * @return the acceptedCommercial
   */
  public Boolean getAcceptedCommercial() {
    return acceptedCommercial;
  }

  /**
   * @param acceptedCommercial the acceptedCommercial to set
   */
  public void setAcceptedCommercial(Boolean acceptedCommercial) {
    this.acceptedCommercial = acceptedCommercial;
  }

  /**
   * @return the acceptedTerms
   */
  public Boolean getAcceptedTerms() {
    return acceptedTerms;
  }

  /**
   * @param acceptedTerms the acceptedTerms to set
   */
  public void setAcceptedTerms(Boolean acceptedTerms) {
    this.acceptedTerms = acceptedTerms;
  }

  /**
   * @return the userType
   */
  public Boolean getUserType() {
    return userType;
  }

  /**
   * @param userType the userType to set
   */
  public void setUserType(Boolean userType) {
    this.userType = userType;
  }

}

AccessCode component

We are going to repeat the same process for the AccessCode component. Create these packages in /jtqj-core/src/main/java:

com.devonfw.application.jtqj.accesscodemanagement.dataaccess.api

... and create a class called AccessCodeEntity inside of them.
We will end up with the following structure:

`AccessCode` Entity

The contents of AccessCodeEntity before using CobiGen will be:

package com.devonfw.application.jtqj.accesscodemanagement.dataaccess.api;

import java.sql.Timestamp;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.Size;

import com.devonfw.application.jtqj.visitormanagement.dataaccess.api.VisitorEntity;

@Entity
@Table(name = "AccessCode")
public class AccessCodeEntity {

  @Size(min = 2, max = 5)
  private String ticketNumber;

  @Temporal(TemporalType.TIMESTAMP)
  private Timestamp creationTime;

  @Temporal(TemporalType.TIMESTAMP)
  private Timestamp startTime;

  @Temporal(TemporalType.TIMESTAMP)
  private Timestamp endTime;

  private VisitorEntity visitor;

  private QueueEntity queue;

  /**
   * @return the ticketNumber
   */
  public String getTicketNumber() {
    return ticketNumber;
  }

  /**
   * @param ticketNumber the ticketNumber to set
   */
  public void setTicketNumber(String ticketNumber) {
    this.ticketNumber = ticketNumber;
  }

  /**
   * @return the creationTime
   */
  public Timestamp getCreationTime() {
    return creationTime;
  }

  /**
   * @param creationTime the creationTime to set
   */
  public void setCreationTime(Timestamp creationTime) {
    this.creationTime = creationTime;
  }

  /**
   * @return the startTime
   */
  public Timestamp getStartTime() {
    return startTime;
  }

  /**
   * @param startTime the startTime to set
   */
  public void setStartTime(Timestamp startTime) {
    this.startTime = startTime;
  }

  /**
   * @return the endTime
   */
  public Timestamp getEndTime() {
    return endTime;
  }

  /**
   * @param endTime the endTime to set
   */
  public void setEndTime(Timestamp endTime) {
    this.endTime = endTime;
  }

  /**
   * @return the visitor
   */
  @OneToOne(cascade = CascadeType.DETACH, fetch = FetchType.EAGER)
  @JoinColumn(name = "idVisitor")
  public VisitorEntity getVisitor() {
    return visitor;
  }

  /**
   * @param visitor the visitor to set
   */
  public void setVisitor(VisitorEntity visitor) {
    this.visitor = visitor;
  }

  /**
   * @return the queue
   */
  @ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.EAGER)
  @JoinColumn(name = "idQueue")
  public QueueEntity getQueue() {
    return queue;
  }

  /**
   * @param queue the queue to set
   */
  public void setQueue(QueueEntity queue) {
    this.queue = queue;
  }

}
⚠️

Eclipse will report some errors related to QueueEntity.
These will be resolved, when we create the corresponding class in the next step.

Queue Component

Finally, we are going to repeat the same process for our last entity QueueEntity component. Create these packages in /jtqj-core/src/main/java:

com.devonfw.application.jtqj.queuemanagement.dataaccess.api

... and create a class called QueueEntity inside of them.
We will end up with the following structure:

Queue Entity

The contents of QueueEntity before using CobiGen will be:

package com.devonfw.application.jtqj.queuemanagement.dataaccess.api;

import java.sql.Timestamp;

import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name = "DailyQueue")
public class QueueEntity {

  private String name;

  private String logo;

  private String currentNumber;

  @Temporal(TemporalType.TIMESTAMP)
  private Timestamp attentionTime;

  @Temporal(TemporalType.TIMESTAMP)
  private Timestamp minAttentionTime;

  private Boolean active;

  private int customers;

  /**
   * @return the name
   */
  public String getName() {
    return name;
  }

  /**
   * @param name the name to set
   */
  public void setName(String name) {
    this.name = name;
  }

  /**
   * @return the logo
   */
  public String getLogo() {
    return logo;
  }

  /**
   * @param logo the logo to set
   */
  public void setLogo(String logo) {
    this.logo = logo;
  }

  /**
   * @return the currentNumber
   */
  public String getCurrentNumber() {
    return currentNumber;
  }

  /**
   * @param currentNumber the currentNumber to set
   */
  public void setCurrentNumber(String currentNumber) {
    this.currentNumber = currentNumber;
  }

  /**
   * @return the attentionTime
   */
  public Timestamp getAttentionTime() {
    return attentionTime;
  }

  /**
   * @param attentionTime the attentionTime to set
   */
  public void setAttentionTime(Timestamp attentionTime) {
    this.attentionTime = attentionTime;
  }

  /**
   * @return the minAttentionTime
   */
  public Timestamp getMinAttentionTime() {
    return minAttentionTime;
  }

  /**
   * @param minAttentionTime the minAttentionTime to set
   */
  public void setMinAttentionTime(Timestamp minAttentionTime) {
    this.minAttentionTime = minAttentionTime;
  }

  /**
   * @return the active
   */
  public Boolean getActive() {
    return active;
  }

  /**
   * @param active the active to set
   */
  public void setActive(Boolean active) {
    this.active = active;
  }

  /**
   * @return the customers
   */
  public int getCustomers() {
    return customers;
  }

  /**
   * @param customers the customers to set
   */
  public void setCustomers(int customers) {
    this.customers = customers;
  }

}

Now we have finished preparing the core of our components and can start using CobiGen to generate the remaining structure (services, layers, DAOs, …​).

Now we can resolve the compilation errors related to QueueEntity in the AccessCodeEntity.java by applying the suggestions of the IDE. To do this, open the offending file, click the first red light bulb on the left border of the editor and select Import 'QueueEntity' (com.devonfw. …​).

Or just manually add this line to your import statements:

import com.devonfw.application.jtqj.queuemanagement.dataaccess.api.QueueEntity;

The Component Structure (using CobiGen)

Once we are finished creating the core of our components we could continue to create the structure and all elements manually, but we are going to use CobiGen for these tasks, since we can save a significant amount of time and effort this way.

First however, we need to make sure that the CobiGen plugin is installed in our Eclipse instance:

CobiGen Plugin Check

If you don’t see this option in the dropdown menu, close Eclipse (remember to save all your progress) and in the jump-the-queue folder right-click to Open a Devon `CMD shell here`.

Now enter and execute:

devon eclipse add-plugin cobigen

... and re-open Eclipse via the eclipse-main.bat script.

CobiGen Health Check

When using CobiGen for the first time it’s recommended to check the health of the tool.
To do so, right-click one of our entities and select CobiGen > Health Check…​.

CobiGen Health Check 1

The next dialogs will show us if there are outdated templates. In that case just click the "Update" button. You can also run an Advanced Health Check to see exactly which CobiGen templates are available for this project.

CobiGen Health Check 2
CobiGen Health Check 3

In case you receive an error like this: image::images/devon4j/4.Components/templates_not_found.png[CobiGen Health Check 3, 400]

You need to force download of templates as in the following image: image::images/devon4j/4.Components/adapt-templates.png[CobiGen Health Check 3, 550]

Now the templates should be downloaded, and you will see a new folder in the workspace: image::images/devon4j/4.Components/cobigen-folder.png[CobiGen Health Check 3, 400]

Queue Component Structure (Entity without Relations)

In order to create the whole structure of a component with CobiGen we only need to right-click our component core entity (QueueEntity) and select CobiGen > Generate.

CobiGen Generate

Now we’ll get to choose which packages we want to generate with the tool.

To get the needed functionalities for our component we are going to select all of the following packages at the same time:

CobiGen Package Selection

By default, all files will be selected for generation (which is what we want in this case), but you could also change which files will be generated by clicking Customize.

For now just click Finish and let CobiGen do its work.

ℹ️

In detail the selected options do the following:

  • CRUD SpringData Repository: Generates the entity repository (that contains the CRUD operations) in the data access layer.

CobiGen CRUD Spring Data Repository
  • CRUD REST Services: Generates a complete service layer with CRUD operations for our entity exposed as a REST service.

CobiGen CRUD REST Services
  • CRUD UC Logic: Generates the logic layer dividing the implementation in different use cases.

CobiGen CRUD UC Logic
  • Entity Infrastructure: Creates the entity main interface and edits (by a merge) the current entity to extend the devon classes.

CobiGen Entity Infrastructure
  • TO’s: Generates the related Transfer Objects, that we will explain in next chapters of this tutorial.

CobiGen Transfer Objects

During the process CobiGen will show a message asking us to review some ambiguous references, which we will get to right away. For now just click Continue.

CobiGen Import Review

Once CobiGen has finished generating the new classes, we will check for and fix those ambiguous references if we need to introduce manual adjustments.

First, we need to adjust manually some imports related to Timestamp in:

jtqj-core:

  • queuemanagement.dataaccess.api.repo.QueueRepository

jtqj-api:

  • queuemanagement.common.api.Queue

  • queuemanagement.logic.api.to.QueueEto

  • queuemanagement.logic.api.to.QueueSearchCriteriaTo

We can fix these errors manually by adding import java.sql.Timestamp to the affected Java files:

CobiGen Manual Import

AccessCode Component Structure (Entity with Relations)

We repeat this process on our AccessCodeEntity, but in this case — since its an entity with relations — we are going to have to select different CobiGen options:

CobiGen New `AccessCode`

After CobiGen has finished generating, fix the issues regarding import java.sql.Timestamp (as you did in the last step) in the following files:

jtqj-core:

  • accesscodemanagement.dataaccess.api.repo.AccessCodeRepository

jtqj-api:

  • accesscodemanagement.common.api.AccessCode

  • accesscodemanagement.logic.api.to.AccessCodeEto

  • accesscodemanagement.logic.api.to.AccessCodeSearchCriteriaTo

There will be some compilation errors left. This is because we have some dependencies on Queue and Visitor component elements, that are not created yet. These compilation errors will be fixed in the next steps.

CobiGen Expected Errors

Visitor Component Structure (Entity without Relations)

Finally we are going to generate the same classes that we generated for the QueueEntity component for our VisitorEntity component:

CobiGen New Visitor

Once CobiGen has finished we can fix the rest of the compilation errors related to VisitorEto by manually importing the class into:

jtqj-core:

  • accesscodemanagement.logic.impl.usecase.UcFindAccessCodeImpl

jtqj-api:

  • accesscodemanagement.logic.api.to.AccessCodeCto

Run the App

If all compilation errors are solved run the app (right-click SpringBootApp.java > Run As > Java Application). The back-end should launch without errors.

Congratulations!
You have created your first devon4j components. You should be able to access the login screen via localhost:8081/jumpthequeue. You can login with the username and password "waiter". In the next chapter we will show and explain each of the created elements in detail.


Next Chapter: devon4j Structure

Clone this wiki locally