Hi all,
First of all, thanks to @avi.fatal for touching such an interesting subject. We also were thinking a lot about the problems, which have been highlighted in the topic. @knstvk has mentioned that we have developed a prototype for testing a brand new approach making data manipulations safe.
Hibernate-like approach of “lazy load everywhere” without partial load is a big pain when you develop a big enterprise app, because it causes unpredictable issues with performance and db load, despite the fact that it is very convenient way for developers - no need to think what you are going to use in business logic, just call it. CUBA views have been designed in response to this problem and, generally, it solves the problem by a clear declaration of what when will be loaded.
As it has been mentioned in this thread, using CUBA views causes some problems and the biggest one that there is no convenient way to understand what fields of an entity are loaded in a certain part of the code. The problem grows when there is a long chain of calls passing entity from one method to another.
After some analysis, we came up with an approach to define views as an interface, like:
public interface SampleWithUserView extends BaseEntityView<SampleEntity> {
String getName();
UserMinimalView getUser();
interface UserMinimalView extends BaseEntityView<User>{
String getName();
String getLogin();
}
}
BaseEntityView
is not just a marker interface, it provides a couple of very useful methods:
-
transform(targetView)
- returns the same entity in another View wrapper (works lazy)
-
getOrigin()
- returns the entity it wraps.
In short, the advantages are:
- No unfetched attribute exceptions - working with this views you will not be able to even call a field that is not loaded
- Views can be used as an API, e.g. your service consumes SampleWithUserView, so you can be sure that all the fields you call are loaded
- You can mix views, as your view interface can implement multiple interfaces (like multiple inheritance)
- You can introduce a @MetaProperty in views interfaces by implementing default methods, e.g. for calculating subtotals.
- You can define true read-only properties on the view level, just declare only a getter without a setter.
You are welcome to see the PoC of this approach here. Its readme.md provides quite detailed information. It is developed as an application component, so you can even try it with minimum efforts. To see ViewInterfaces in action, have a look at the implemented tests.
Regards,
Aleksey
P.S. As @pfurini question, at first we also wanted to organize inheritance model this way, but it didn’t work. One reason is a number of views per entity. It’s normal to have 20-30 views for the same entity, so it means that you will need to implement them all… or how? Another reason is dealing with references to other entities. In a JPA entity you should operate with JPA entity in getters and setters, however, it will ruin the concept of safety. In our current concept you always operate with views in the absolutely safe way.