Developing with CUBA - a big shift from Spring?

belyaev_6903's picture

Reading requirements for yet another web project for internal corporate use you (at least myself) usually see a pretty common set: well-defined data storage structure (or sometimes it’s an existing legacy DB), lots of forms for data entry, quite complex business logic, reporting and integrations with lots of existing corporate systems from accounting to supply management, thousands of concurrent users. What are your first thoughts?

“OK, I’ll take a well-known RDBMS, Hibernate/JPA+Spring Boot, add REST API and use my favourite/the latest JS framework to implement UI.“

“Ah. And I need to set up Spring Security. And maybe add some custom code to protect data at row level. How will I implement it? Probably database views or virtual private database.“

“And all these DAOs - they are similar and boring but I need to implement them.”

“And use something like ModelMapper to convert JPA Entities to DTOs for REST.”

“And don’t forget to tell John - our new intern - about lazy fetching and JPA joins.”

“Oh boy, can I get rid of all this routine stuff and focus on the critical piece of business logic implementation instead of implementing yet another login form and Entity-to-DTO conversion?“

This article is for developers who have implemented at least a couple of projects from scratch using the Spring framework (including Spring Boot) and now thinking about boosting their productivity. In the article I will show you how to get rid of very common time-killing routine tasks by using CUBA platform.

Another Framework Again?

The number one question from developers when they hear about a new framework is: “Why do I need this when I can take Spring Boot and implement everything from scratch as I used to?”. Well, fair enough - new platform requires learning new principles and dealing with new limitations, leaving all years of your experience behind. Even if your current framework is not brilliant, you know it all, you know all the pitfalls and workarounds for them.

But what if I tell you that CUBA does not require a U-turn (or even right angle turn) from the traditional Spring-way of development, but a slight step aside that allows you to eliminate boilerplate noise as hundreds of lines of DTOs and conversion utils, implementation of data pagination or data filtering components, creating configuration files for Spring Security (JPA, Cache,... you name it).

We will start from the beginning and then show how CUBA application development follows the model that is used for almost all Spring-based applications will let you use all your Spring kung-fu skills that you’ve learned in your developer’s career and deliver more at the end. The article is focused on back-end code to make our story smaller and more concise.

Spring Application Architecture

The typical architecture of a Spring application can be googled easily and in 90% of cases it can be represented as a three-layered application with some cross-cutting areas. Let’s have a look at a “classic” Spring application.

Domain Model - usually created manually. There are some tools for creating a domain model based on a datastore structure though.

Repository Layer - classes that work with a data storage. Also known as “DAOs”, “Repositories”, etc. That’s where all those ORM frameworks (and their siblings) rule. It usually contains classes that perform CRUD operations using only one entity class from a domain model.

Service Layer - sometimes developers create an additional layer to separate business logic and data CRUD operations. This layer is useful if you have a complex business logic involving different types of data sources, external service integrations, etc.

Web/Controllers Layer (REST/MVC) - set of classes that deal with either REST API (that will be consumed by browser-based applications) or views implemented using JSPs, template frameworks (thymeleaf, velocity), or JVM frameworks (GWT, Vaadin, Wicket, etc.). Usually controllers manipulate DTOs rather than entity objects, due to API structure or representation in views. Therefore developers often have to implement bi-directional conversion between an entity model and a DTO model.

If all above sounds familiar (and even like “Captain Obvious” to you) - it is a great sign meaning that you will be able to get started with CUBA with no barrier.



Reference Application – Pet Clinic

They say: “Words are cheap, show me your code”. Spring has its own well-known “reference” application - Pet Clinic, which is available on GitHub. Below we will show how your Spring Developer’s skills can be used when developing a backend for the new fork of the Pet Clinic - with CUBA now. There is a very good and detailed description of the reference application from Antoine Rey here; we will repeat some pieces in this article though.

Data Model

ER diagram of the database is shown on the diagram. The actual object domain model in the application’s code is a bit more complex and includes some inheritance, you can find UML in the presentation mentioned above.

Repository Level

There are four repositories to deal with main entities: Owner, Pet, Visit and Vet. Those repositories are based on Spring JPA framework and contain almost no code thanks to Spring JPA, but you can find a custom query in Owner repository to fetch owners and their pets in one request.

UI Screens

The application consists of nine screens that allow us to view all data and edit some of it: pet owners, pets and visits. We won’t talk about them now, but I need to mention that those screens are just a simple CRUD forms that are pretty common for the most data-oriented applications.

Additional Features

Apart from simple CRUD functionality the application provides some (not so obvious) functionality that shows the power of the Spring Framework:

  • Caching - vets list is cached, so there are no queries to DB when vet list is refreshed.
  • Validator - checks if all fields are filled during creating a new record about a pet.
  • Formatter - for proper display of a pet type.
  • i18n - the application is available in English and German languages.
  • Transaction management - some db queries are made read-only.

A Side Note

I like this picture very much since it reflects my feelings with 100% accuracy. To use any framework efficiently you need to understand how it works inside. For example Spring Boot hides a lot of things from you and you will be surprised how many classes lay behind one simple JPA interface initialization. Some notes about “magic” happening in Spring Boot Pet Clinic application:

  • There is no cache configuration code apart from @Caсheable annotation, but somehow Spring Boot “knows” how to set up a cache implementation (EhCache in our case).
  • Repositories are not marked as @Transactional (neither is their parent class org.springframework.data.repository.Repository), but all save() methods work just fine there.

But despite of all these implicits Spring Boot is a very popular framework because it’s transparent and predictable. It has a very detailed documentation and it’s open source, so you can read how things work and drill down into any method and see what’s going on there. I guess everyone likes transparent and manageable frameworks - using them makes your application maintainable.

Pet Clinic with CUBA

So, let’s have a look at a Pet Clinic implementation with CUBA Platform, try to look at it in terms of our Spring knowledge and figure out where we can save some efforts.

Source code for the Pet Clinic implementation can be found on GitHub. Apart from that CUBA Platform has very good documentation and you can find almost everything there (most cases are illustrated with examples and code snippets on GitHub). In this article we will refer to the documentation quite often just to avoid explaining things twice.

CUBA Application Architecture

CUBA application consists of the following modules (see the diagram).

Global - contains entities mapped to a database, CUBA views and service interfaces that can be used in other modules.

Core - all service implementations that work with the application’s database and implement business logic should be placed here. Please note that Core classes are not available in other modules, it was done on purpose to provide separate deploy of Core and GUI modules to different servers for better scalability. To inject services from Core module to other modules you should use interfaces declared in Global module.

GUI, Web, Desktop, Portal - these modules contain GUI-related classes (controllers, listeners, etc.) responsible for UI events processing. You can create your custom REST controllers here to complement out-of-the-box REST API that CUBA generates for you.

For a better developer’s performance CUBA has Studio - a nice small GUI to create and register entities that will change all the configs for you, help with creating code stubs for services and has a WYSIWYG editor for GUI forms.

So an application based on CUBA Platform consists of two (or more) separate modules - Core and GUI(s) that can be deployed separately, and a cross-cutting Global module. Let’s have a look at CUBA’s Global and Core modules and their contents in details.

Global Module


Entity Model


Entity model in a CUBA application should be familiar to any developer who worked with JPA-compatible ORM framework and Spring. It is just classes annotated with @Table, @Entity etc. and registered in the persistence.xml file.

In the entity model for the Pet Clinic application you can reuse the code from the Spring version, but you need to remember a couple of things:

  1. CUBA introduces a “namespace” for every application component created with this platform to prevent names clash between different components. That’s why there is a “petclinic$” prefix for every entity name.
  2. It is recommended to use a @NamePattern annotation for entities to get a meaningful instance representation in UI.

The question is - what CUBA gives us apart from prefixes and declarative entity “stringified” representation? Additional features include:

  1. Base classes that support ID generation functionality: from Integer IDs to UUIDs..
  2. A set of helpful (but optional) interfaces:
    • Versioned - to support entity versions.
    • SoftDelete - to support “soft”, a.k.a “logical” delete for an entity.
    • Updatable - adds fields for entity update logging.
    • Creatable - adds fields for entity creation logging.
      You can read more about these interfaces in the documentation.
  3. Database schema creation and update scripts can be generated by CUBA Studio automatically.

During the application development I just copied existing entities models from the Spring version and added CUBA-specific features mentioned above, deleting BaseEntity class from the reference version of the application.

Views


CUBA’s “Views” concept may be confusing, but it is quite easy to explain. A View is a declarative way to specify which data (attributes and nested instances/collections) should be extracted.

Let’s assume that you need to fetch owners and their pets or vets with their specialities - for displaying dependent entities along with “parent” data on the same UI screen. In case of pure Spring implementation you need to define JPA joins...

@Query("SELECT owner FROM Owner owner left join fetch owner.pets WHERE owner.id =:id")
public Owner findById(@Param("id") int id);

… or define proper EAGER/LAZY fetch types to get dependent collections for an entity within transaction context.

@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "vet_specialties", joinColumns = @JoinColumn(name = "vet_id"), 
inverseJoinColumns = @JoinColumn(name = "specialty_id"))
private Set specialties;

In CUBA version you can use EntityManager and JPQL or views and DataManager:

1. Define a view that specifies what we want to extract:

<view class="com.haulmont.petclinic.entity.Vet"
	extends="_minimal"
	name="vet-specialities-view">
	<property name="specialities"
		view="_minimal">
	</property>
</view>

2. Use DataManager bean to get this data

public Collection findAll() {
	return dataManager.load(Vet.class)
		.query("select v from cubapetclinic$Vet v")
		.view("vet-specialities-view")
		.list();
}

You can create different views for different tasks choosing which attributes you want to get, whether or not to fetch collections and define how deep your object tree will be. There is a great post about views in Mario David’s blog.

In the Pet Clinic application we defined six views for different cases. Those views are used mostly in UI forms and one of them - for fetching data in the service, the code snippet is shown above.

Services Interfaces


Since Global module is a cross-cutting module of a CUBA-based application, you should define service interfaces in it to be able to use services in other modules by using Spring injections. All you need to do is to register services in “web-spring.xml” file in Web module. The CUBA platform creates proxies in application modules for transparent entities serialization and deserialization using this Spring config XML file. This feature lets us call services implemented in Core from other modules even in case of distributed deploy with a minimum of additional efforts.

So in terms of Entity model development with CUBA it is all the same as in pure Spring, but you shouldn’t care about ID generation and retrieval entity’s ID after insert and don’t need to create extra code for entity versioning, soft delete and entity change log. Also you can save some time on creating views instead of JPA joins.

Core Module


Core module contains service implementations for interfaces declared in Global module. Every service in CUBA application is usually annotated with @Service, but you can use all available Spring annotations to deal with beans. There are a couple of limitations due to CUBA’s architecture though:

  • You need to annotate your service with @Service if you want it to be exposed in Web module.
  • It is recommended to give a name for your service to avoid bean clash from different add-ons.

Other than that, your Core module codebase is a “pure” Spring-based backend application. You can fetch data from data store(s), invoke 3rd party web services, etc. in the same way as you used to. The only significant difference is interaction with database.

Entity Manager and DataManager

The platform uses its own EntityManager that delegates part of its functionality to an actual javax.persistence.EntityManager instance. CUBA’s EntityManager provides mostly low-level entity operations and does not support security features. In most cases it is recommended to use DataManager that gives extra functionality:

  1. Row-level and attribute-level security support.
  2. CUBA’s entity views usage for fetching data.
  3. Dynamic attribures.

More about DataManager and EntityManager you can find in the documentation. Please note that you don’t need to use those beans directly in GUI - there are Datasources for this.

Talking about PetClinic - I (almost) didn’t write a lot of code in the Core module since there was no complex business logic there.

Features from Spring Pet Clinic in CUBA

In the previous section there was a list of extra functionality in the Spring-based Pet Clinic application, the same features are available in CUBA.

Caching

CUBA provides entity and query caches as built-in features. Those caches are described in details in documentation and should be considered first since they support all platform features like distributed deploy. In addition to this, you can enable caching by using Spring’s @Cacheable and enable caching as described in Spring documentation.

Validator

CUBA uses BeanValidation as a standard validation engine. If built-in validation is not enough, you can define custom validation code. And there is always an option to verify data in the UI by defining Validator class as described here.

Formatter

CUBA platform provides several formatters for GUI components, but you can define your own formatter apart from standard formatters. For default entity representation @NamePattern annotation is used.

I18n

CUBA platform supports internationalization in the same way as other java applications: by using message.properties files, so nothing new here.

Transaction management

CUBA platform provides the following transaction management options:

  • Familiar Spring’s @Transactional annotation
  • CUBA’s Persistent interface if you need a fine-grained transaction management in some complex cases.
When I was developing the Pet Clinic, I thought about transactions only once - during development of the form that allowed editing owners, pets and adding visits on the same screen. I needed to understand when to commit a transaction and refresh a UI to display data in a consistent way.

Pet Clinic in a few hours. Really

I was able to create an application with the same functionality as Spring’s Pet Clinic with a “standard” CUBA UI in less than a day. I wouldn’t say I’m an expert in CUBA (it’s been just several weeks since I started), but I have a long history of using Spring. Let’s have a look at a CUBA-based app with Spring architecture in mind:
Domain Model - entities in Global module. Creating an entity model was a well-known routine. Kudos to BaseIntegerIdEntity class for saving some time on ID generation.
Repository Layer - I didn’t need repositories. Not even an interface. I just created some views using CUBA Studio GUI. With this tool I didn’t need to write XML in configs.
Service Layer - In our application we have only two services to export vets in JSON and XML format with cacheable result. I put interfaces to Global and implementations to Core as per documentation. Then it was just a “normal” development apart from reading about DataManager a bit to get familiar with its API.
Controllers Layer - The CUBA Pet Clinic contains only one custom REST controller for JSON and XML feed in Web module. No surprises here, it was just a Spring controller with familiar annotations.
Application GUI - creating “standard” CRUD forms with CUBA Studio was a breeze.
I didn’t think about passing entities to web UI and form submission - no controllers and repositories. CUBA provided me a proper grid and a component for data filtering, so no more parsing query strings and fuss with Pageable. I spent most of the time implementing proper UI flow, renderers and applying styles.

My personal experience is shown in the table:

Easy to Understand and Develop Need to Read Documentation
Entities Entity Modelling
DB Creation Scripts
Standard base classes
Additional features for soft delete etc.
Repositories EntityManager
Views
DataManager
Services Beans management
Transaction management
Security and user management
Persistent interface
Controllers Custom REST Controllers
Request URL mapping
Service methods publishing
UI Standard forms UI customization

Obviously Pet Clinic application does not use all CUBA features, the full list can be found on the site where you will see other common tasks that can be solved by the platform.

My personal opinion - CUBA simplifies back-end implementation and doing great if you use its “standard” GUI. Even if you need a fancy UI, CUBA will save your time on back-end development for sure.

So many pros! What about cons?

Well, there are some things I would like to mention in this section. These things are not game changing, however I found them quite unwanted at first steps of getting familiar with CUBA.

  • In the introduction section it was told that CUBA platform comes with its own IDE that simplifies project creation and management. Sometimes switching between Studio and your IDE might be a bit irritating, but we’re redeveloping it now, so Studio will transform into IDEA’s plugin soon.
  • In CUBA we use a bit more XML config files than in typical Spring Boot application because of more services provided by the platform.
  • There are no “friendly” URLs for each of the application’s UI form yet. You can access screens directly using screen links, but they are not very “human-readable”.
  • You have to deal with CUBA’s DataManager and EntityManager and learn their API rather than Spring JPA or JDBC (but can still use them if needed).
  • You will achieve the best development performance with CUBA when using relational databases. As for NoSQL - CUBA performs as well as Spring does, it is the same amount of coding work.

Conclusion

If you have a task for implementing data-centric intranet application that uses RDBMS as a data storage you may want to try CUBA platform as a basis because:

  1. CUBA is transparent. Source code is available and you can debug everything.
  2. CUBA is flexible (up to some limit). You can inherit and inject your own beans instead of standard CUBA beans, publish custom REST API and use your own UI framework to interact with user.
  3. CUBA is Spring. 80% of your back end code will be a pure Spring application.
  4. You can start fast. An application is ready for use right after first entity and UI screen creation.
  5. A lot of routine work is done for you.

So by using CUBA you will save some time on routine tasks for the real fun dealing with complex business-related algorithms and non-trivial integrations with other applications.

Read more

Comments