To understand and explore Rebel Plus features, in this article you incrementally develop a Spring Boot RESTful service that serves as a backend application for an online Italian restaurant.
As you complete this step-by-step guide, you will end up with a working RESTful service that can be used as a basis for an e-commerce shop.
We will create our example using Spring Initializr, a web application available at https://start.spring.io.
A wrapper for Spring Initializr is available in different development environments, such as IntelliJ or Spring Tool Suite for Eclipse.
In our example, the project group will be com.archetypesoftware and the artifact name will be eatalian.
We will add the following dependencies to the project: Spring Web Starter, Spring Data JPA, H2 and Spring Boot Devtools.
Spring Web Starter will provide us with everything we need to develop RESTful services using the Spring MVC. The Spring Data JPA will give us the possibility to persist our entities using Spring Data and Hibernate, while in-memory H2 database will serve as a storage for the data. As stated in the reference documentation, the optional Devtools dependency includes an additional set of tools that can make the application development experience a little more pleasant.
After generating the project, we will download the eatalian.zip file, unpack it and import in our development environment.
Our next step would be to create a UML model for our project.
Let us create a new StarUML project, with a single UML package in the root of our model and name it eatalian, as we have already done when creating a new Spring application.
Now, let us select it in the StarUML model explorer and assign it a stereotype root in the property editor, as shown below.
Now, let us create a package structure common to many Spring applications.
Just under our root package, let us create packages domain, repositories, services, and controllers, as shown in the diagram below.
Finally, let us introduce packages that will contain our domain classes.
Let us add new packages to our domain package: monetary, catalogue, accounts, and orders. Each one of them will be described later.
Before we continue, let us select eatalian package in the StarUML model explorer, select Java profile in the Rebel Property Editor, and use the value of com.archetypesoftware as a Base package, as shown in the screenshot above.
Please note that we are using the same values for the name of the root package (eatalian) and the base package prefix (com.archetypesoftware) that we were using when creating this application in the previous step of this article.
You can find animated example how to setup code generation in the dedicated section of our Getting Started guide.
Before we generate the Java code from the UML model, let us configure code generation properties for our project.
To configure Rebel code generation properties, select either StarUML menu File -> Preferences -> Rebel or Rebel -> Configure. Alternatively, you may use the keyboard shortcut Alt + F5.
Let us switch Use local Rebel settings on. In this way, it will maintain different code-generation configuration for each of your projects. You can read more about Rebel local configuration in this post.
We will use four spaces for indentation of the generated sources.
Also, let us leave the Project Codebase property empty, so the Rebel will prompt us to select destination directory once we are ready.
Let us select Generate fluent setters and Generate fluent link/unlink methods. Fluent-style interfaces give us the possibility to write more readable and more elegant Java code, as you will see later.
Let us switch Generate persistence meta-data on because we want to make our entities persistent using the Java Persistence API.
As we will be using Java 11 and Hibernate 5 in our example, we don't need Use @Temporal annotation.
Finally, let us enable Spring code generation and leave other preferences to their default values.
As you can easily imagine, the Rebel Plus is actually a conglomerate of multiple code-generators that are combined to produce the final, joint output. Using Rebel preferences, we define a pipeline for generating the desired output.
For more information about Rebel preferences, see the Rebel Preferences section of the Getting Started guide.
At this stage, we are ready to generate the application from the UML model of the project. To generate Java code, use Rebel -> Generate from the StarUML menu or use the shortcut key F5 instead.
If this is the first time you are generating sources for this model, the Rebel will prompt you where to save the generated source code.
Browse to the project previously created by the Spring Initializr and select java folder (part of the Maven standard directory layout).
As you progress through developing your UML model, you can re-generate project sources at any time.
As you can see, the generated directory structure completely resembles the organization of UML packages in our model.
Intuitiveness and simplicity of modeling with StarUML is one of the reasons why we enjoy using it and why we decided to extend this particular modeling tool.
In this section, we will introduce the main business concepts of our application, grouped into several packages.
The monetary package contains just a single class, used in other packages: CurrencyAmount.
The CurrencyAmount is a Java synonym for the monetary value: the amount (of money) in a given currency.
As we want to store information about prices, let us mark CurrencyAmount persistent (in the Rebel Property Editor).
As we don't want to store pricing information in a separate database table, but rather use it as an attribute of other, strong entities, let us label it using the "valuetype" stereotype.
The "valuetype" stereotype tells Rebel that the CurrencyAmount is a JPA @Embeddable entity, that does not have its own identity.
You can specify stereotype value either in StarUML Property Editor or by typing <<stereotype name>> directly before the class name in the class diagram, using StarUML quick edit feature.
When you re-generate the code from the model, your codebase should be similar to the picture above.
Have a glance at our Modeling Tips & Tricks to avoid awkward diagram displays that may occur when using Rebel Property Editor.
Catalogue package contains concepts needed to model the catalog of our online restaurant.
The main concept of this package is Product. Products have their unique name, as well as a description that may be displayed to the customers of our online store. Each product has its price.
Multiple Products can be organized into Categories (of Products). Each Category has its name and optional description. Categories themselves may be further organized into Category hierarchies.
For instance, our restaurants may sell different kinds of Sandwiches, Pizzas, and Tortillas, while all of them may be grouped into one parent category: Food.
Have a look at the diagram above to see an example of specifying an optional attribute in the class diagram (description of a Category) and see how the Rebel treats optional values in the generated code (Category.java).
After introducing Categories and Products, our re-generated application should look similar to the image above.
Accounts package contains concepts needed to enable interaction between our application and its customers.
The concept of Account represents a customers account, used to authenticate to our application, see our offer (actually the catalogue that we have previously covered), and make his orders.
The Account contains a unique email attribute as a customer username and his encoded password for authentication purposes. Besides the customer's first and last name, we also keep his contact phone, so that we could call him or send notification messages about the order changes if preferred.
If you pay careful attention to the diagram above, you will notice that Account attribute types are not exactly Java types. For instance, the name is of type string and Account's dateOfCreation is of type dateTime. These are builtin Rebel attribute types that can be mapped to different Java types. For more information, see the section Built-in types of our Getting Started guide.
There are both shipment and billing addresses for each Account. Shipment address is used as a default Order address unless the customer explicitly set different delivery address when placing an Order.
After generating the code from the model, our project should look similar to the picture above.
When you practice model-driven development, you need to specify in the UML model of your project details that would otherwise be irrelevant, such as names of all association ends and their cardinality, as well as types of all class attributes or method parameters. Simply put, now the model has the formal semantics and is directly mapped to the generated Java source code. To see an example, you can visit the Modeling Conventions section of our Getting Started guide.
Each time a customer interacts with our online restaurant, she places an Order.
An Order may contain multiple Requests and has its shipment address. Order processing makes the majority of the business logic of our application. While we will not cover it in this example, the state of an Order is maintained as an enumeration and its possible values are contained within the OrderState enum.
You can easily recall that order is the SQL keyword and we will not be able to use it as the name of the database table (that would be the case if the name of the persistent entity is Order). That is why we customize the name of the table for storing Order objects to customers_orders, as you see in the image above.
When it comes to Requests comprising an Order, our application supports two types of Requests: ProductRequests and GroupRequests.
A ProductRequest is always related to a particular Product. When requesting a Product, the customer may specify the quantity of desired Product, as well as an arbitrary note ("I would like to order two Napoletana pizzas. Make them really spicy please. ").
On the other hand, the concept of GroupRequest makes it possible to treat multiple Requests (both GroupRequests and ProductRequests) as a single Request, that may be useful in some situations.
The GroupRequest is actually an example of Composite design pattern.
After generating the code from the model at this stage of the project, our codebase should look similar to the picture below.
So far, we have introduced domain concepts that make the core of our application. However, some parts are still missing.
What about the useful parts of our application? We need the data access layer, business logic and a couple of REST controllers to expose the functionalities of our application.
The good point is that we do not need to implement them by hand. As already described in our blog, the Rebel Plus can provide them out-of-the-box.
As we have already turned Spring code generation on, let us annotate some of our packages using stereotypes: repository, service and controller, as shown in the picture below. Now, re-generate your project.
And here it is: for entities in our model, we now have CRUD ("Create, Read, Update and Delete") functionality propagated through all three layers.
Let us consider briefly: perhaps we do not need a separate controller and service classes for Requests - they should always be a part of the Order, right?
We tell Rebel not to create Controllers or Services for an entity using Spring profile: Suppress Controller Generation or Suppress Service Generation, as shown in the picture below.
We select the Request class either in the StarUML model explorer or in any of our class diagrams, then select the Spring profile of the Rebel Property Editor and check Suppress Service Generation.
As a practice, you may do the same for ProductRequest and GroupRequest entities.
The only thing to bear in your mind when dealing with auto-generated Spring components is that they are treated differently compared to the classes directly represented in the model. They are not re-generated every time you perform the code generation. That gives you the possibility to edit them completely free without keeping your changes in preserved sections.
Everything we have done so far would lack its purpose if we don't see the application running and performing some (expected) behavior. Let us add some initial data to the application.
In this section, we will create an initial catalogue of products that we are offering to our customers.
Having in mind that we are using an in-memory H2 database, we will create an ApplicationRunner bean that is automatically executed during application startup. This is how our offer will look like:
Looking delicious, right? Feel free to add some of your favorite meals to the offer!
We perform this setup in the main class of our application: EatalianApplication.java, created by the SpringInitializr:
As you can see, besides our catalogue entries, we have created two customer accounts, for our old friends: John Doe along with his wife Jane.
In this example, you can see the advantage of using fluent setters. They enable method chaining and have a positive impact on the readability of our Java code.
When we run the application and point our browser to
http://localhost:8080/accounts, we should be able to see all customer accounts
we have previously created.
However, if we try to do the same for categories and try to open
http://localhost:8080/categories, we will see the 'page not found' message.
Rebel, by default, builds the plural form of nouns by appending s to the end of the concept. Thus, if you give a look at the CategoryController, you will see its RequestMapping set to "categorys".
Let us change this to
re-start the application and give it another try.
This time, the browser gets overloaded with the results, as if we had hundreds of products in our catalogue.
If we try with httpie, things get even worse.
If you give a careful look at the returned data, you will soon realize that some circular dependencies are preventing our domain model serialization into JSON.
The problem is caused by bidirectional associations between Category and the Product: CategoryHierarchy and CategoryProducts, as can be seen in the model diagram.
To fix this issue, we can either make our associations unidirectional, or we can use the Rebel Jackson profile to additionally configure relations between our classes, as we will do now.
Select parent association end of the CategoryHierarchy association and then select Jackson Rebel profile of the Rebel Property Editor. Now, uncheck the Serializable property, as shown in the diagram above.
When you re-generate the code from the model, you will see that the Rebel has placed a
@JsonIgnore annotation at the "Category.parent" Java attribute.
Let us repeat this step for category association end of the CategoryProducts association.
In this way, when we retrieve information about our Categories by invoking localhost:8080/categories GET endpoint, we will receive information about all categories (without their parent categories) as well as information about their products (without information about products' categories).
As we re-start the application and execute GET HTTP request for localhost:8080/categories, we will see the expected result.
Writing automated tests is completely out of the scope of this post. However, there are many excellent resources on that topic and this article may be a good starting point.
Congratulations! Give yourself a pat on the back if you came this far!
Throughout the tutorial, we have developed a complete Spring Boot RESTful service - a backend of the Eatalian application, using model-driven development powered by StarUML and Rebel Plus. If we don't consider creating sample data, we did it without writing a single line of Java code!
The complete source code of this article can be found over on GitHub.
Class diagrams from the UML model can be used to document your work and if there is a need to add a new feature to the application: just change your model and re-generate the solution.
Hope that you have enjoyed the journey! As a small reward for your perseverance, you can use coupon code APPRECIATION2019 to claim a 20% discount off the annual subscription for both individual and business license, until the end of 2019!
In our next post, we will cover how Rebel Plus can help document your RESTful services with Swagger, along with some new features introduced in version 2.2.0. Stay tuned!
To boost your software development capabilities, upgrade to Rebel Plus.