Grails vs CUBA
Round 1 - Data access and business logic
Since I started a transition from Grails to CUBA some time ago, I thought it would be a good idea to give you a technical comparison between those two frameworks. In this blog post series I go though different aspects, starting with data access and business logic in this post, to conclude why I finally decided to give CUBA a shot in more and more of my projects.
If we would have a gartner magic quadrant for web frameworks, technically CUBA and Grails would be definitively nearby (I will not tell you in which quadrant though ;)), so it is fairly easy to compare them. Both are meta-frameworks in the JVM world. Since both are full stack frameworks we can go from layer to layer to see the differences and similarities.
Entities and OR Mapping
Lets get started with the mapping between the database and the object model. This is an important part for a lot of backend developers, so I’ll go into a little more detail about the approaches both frameworks take.
Grails and GORM
Grails has a built in OR-Mapper called GORM which has been extracted from the core in recent versions. When we look at SQL based databases, GORM uses Hibernate under the covers, which is a very powerful OR-Mapper. It basically adds a lot of syntactic sugar on top of it.
GORM is a place where a lot of the innovation of the framework happens. The reason is not so much the mapping to relational databases but to non-relational ones like Neo4j or MongoDB. It supports a variety of NoSQL databases to map it to the domain types in a form that makes sense depending on the database you are working with.
Also GORM shifts slightly to non-blocking IO when it comes to the actual access of the database. Turns out to be quite hard for the relational databases, since JDBC is blocking by nature. But NoSQL databases do not have the restriction of the JDBC standard and therefore the driver support for asynchronous operations is much better over there (see the RxGORM docs for more info).
Generally you will create a domain type by executing the command line task
grails create-domain-class Person or using the IDE to accomplish the same thing. After that you add some properties to the person class. You’ll end up with something like this:
CUBA loves JPA
CUBA took a slightly different route in this regard. CUBA basically does not do much around the topic of the OR-Mapper. It doesn’t have an explicit part in the framework that deals with database access. Instead, CUBA uses the Java persistence API in order to fulfill its job. Entities are just POJOs with
An equivalent to the Person example above would look like this:
So it is basically the same. CUBA is a little bit more noisy in this regard, but from a features perspective they are pretty much the same.
Data access patterns
Executing queries against the datastore is a little more different.
First of all, they differ in the way the data retrieval gets called. Grails uses a pattern called Active record in order to fetch data from the database. Active record means that the entity class itself is responsible for fetching the data.
Person.list() will in this case get all the entities of the Person table.
In JPA there is the
EntityManager that has to be used for data retrieval, while the Entity objects are more data access objects (DAO) that act more in a passive manner. You can see it more or less as a repository pattern. CUBA additionally has some more options to access data. Normally instead of using the
EntityManager CUBA has a thin layer on top of that called
DataManager. The DataManager cares about all the stuff that JPA is not really aware of but is part of CUBA like row level security or views (see this diff for details).
Both patterns have some advantages and disadvantages. Active model sometimes leads to a slightly messier code when you have a big application because the entities have more responsibilities, but in smaller applications it is easier to read.
Making data access calls
In CUBA the main data access call would look like this:
You use the
dataManager and pass it a
loadContext. The load context defines what has to be loaded. In this case we use a JPQL expression to define that we want all Pets with a certain name.
When it comes to the UI, CUBA has an additional concept called “datasource” that will do the heavy lifting to get the data in the UI components.
Grails has different options to access data. Let’s look at the outstanding ones. As the example we want to fetch all pets that have a certain name. You can use dynamic finders in Grails like this:
Pet.findAllByName("Long John"). Pretty easy, right? Actually the method findAllByName is created dynamically and interpreted by the execution engine do create the correct SQL statement. This means that it has never been defined by the application developer. Instead, Grails understands all combinations of those method names.
Pet.findAllByNameLikeAndFirstnameLikeAndAgeGreaterThan("Long", "John", 21) would also work (but the readability will go down fast for complicated queries).
As these methods are only suitable for simple queries, there is the opportunity to create a so called ‘where query’ that does the same thing:
Where queries have compile time checks, which is a huge win, compared to what we have seen in CUBA with plain JPQL.
Lazy loading vs. eager fetching
One additional thing that is very important and is something where both frameworks have a different approach is the question what data gets loaded and when this is done.
Grails uses lazy loading by default
In Grails your database requests will return instances of the Entity, with all attributes of the table loaded with it. It basically makes a “SELECT * FROM Pet”. When you want to traverse a relationship between entities you do that afterwards. Here’s an example:
It is a single line of code that will do the traversal here:
it.owner.name. Owner is the relationship that has not been loaded in the first request (
Pet.findAll). So for each call of this line, GORM will something like “SELECT * FROM Person WHERE id=’…’”. This is called lazy loading.
Lazy loading is one of GORMs greatest strength and probably one of its greatest weaknesses
Writing and interacting with lazy loaded data is so easy, because you just forget about it. It is so transparent, because from an app developer point of view it exactly looks like traversing an object graph.
Although lazy loading is default in Grails, it is not your only choice. You can define eager fetching globally on the entity attribute level or on a case-to-case basis when you actually trigger the loading.
But for me, since the lazy loading is default and it is so convinient, I often felt myself in the trap of not thinking about it at all and only remember it, when the application acts really slow (which probably tells you more about myself as a programmer instead of Grails as a framework ;)).
CUBA views make loading explicit
CUBA instead has the notion of “views”. Views are basically a definition of what attributes have to be fetched and will be loaded in the entity instances. Views are defined in the corresponding views.xml file where you give the view a name and define what attributes will be loaded with this view. Here’s an example of a CUBA view for the Role entity:
In this example we take some direct attributes of the Role (name, type etc.) as well as a 1:N composition (permission).
I found using views a very explicit way of dealing with the situation. It forces the developer to think about this topic. This can sometimes be cumbersome but it leads to a better place I think compared to making this whole loading story implicit.
If you are interested in more detail on this topic, I created an article around CUBA views. You can find it here: Views - the uncharted mystery.
Database migrations are oftentimes part of modern full stack frameworks. Ruby on Rails pionered with this idea to automatically generate deltas between two database schema versions.
Grails and CUBA both have features in place that make that happen as well. For Grails there is a commonly used plugin called Grails database migration which uses a Java library called Liquibase under the covers. CUBA instead comes out of the box with SQL scripts that get generated via their RAD tool CUBA Studio. Both approaches recognize changes in the entities and try to do their best in order to generate an SQL schema delta (like
ALTER TABLE...) to reflect the changes in the entity model.
With this we’ve basically covered all stuff related to data access. Next, we will look at the next layer which is where and how to put the business logic.
business logic & constraints
When it comes to business logic, both frameworks are actually pretty close to each other. This has mainly to do with the fact, that Grails as well as CUBA use the Spring framework under the covers. This means that both frameworks make fairly heavy use of the dependency injection pattern.
In Grails you usually create services via the command line. These classes are spring beans that represent some kind of business logic and have certain characteristics. Normally the UI layer either interacts directly with the entity instances to talk to the database (in easier cases), or uses services to execute some kind of business logic, that itself might call the database.
In CUBA there is as well the notion of services (in the same sense). You most likely create a service via CUBA studio, which will generate a Service interface as well as a Class that implements this interface and is annotated with the Spring
One thing to notice is that in CUBA, since it uses an four-tier application architecture, there is an optional separation between the client tier (the web server) and the middle tier (the backend application server) (see the docs on app-tiers for more information). This means that you as a developer have to think a little bit more where to put your spring beans, because this has implications where you can call them.
In both frameworks you can additionally create any kind of spring bean and use it throughout the application.
Defining constraints on entities
The last part in this blog post is about validation. Normally every application has some kind of validation logic (mostly on entities), that have to be fulfilled in order to e.g. save them in the database. This is why both frameworks have solutions for the problem built-in.
Let’s look at an example for what a validation in this context means. Taking the Person - Pet example from above, let’s say that the name of the person has to be there in order to create a person instance. Another one would be that at least every Person has to have one Pet.
In Grails we can define the constraints in the entity class in a special closure called ‘constrains’ like this:
While in CUBA there is an explicit usage of the Bean validation annotations:
In both framework you have options to create custom validations as classes etc. So basically the difference is just in syntax but not in the semantics.
With this, we are at the end of the first part of the comparison. In the next blog post, we will talk more about UI, security, APIs & platform features. These are actually the areas where the big differences are, so stay tuned.