In our special Java Daily edition, we would like to introduce you to Victor Rentea. He was kind enough to share his experience on 8 Java related questions.
Victor Rentea is a Java Champion, Clean Coder enthusiast, Senior Java Engineer, Technical Team Lead and Java Coach at IBM. Also, he is known as an Independent trainer and technical coach.
Java Daily: If we use JPA, we annotate the persistence entities with annotations like @Entity, @Id, @GeneratedValue. Should those persistence entities be part of the business layer? And if so, aren’t we coupling our business logic too much with the persistence logic?
I must admit, I’ve had this debate numerous times, on so many projects. I’ll try to summarize below the conclusions I’ve reached over the years.
Before we dive further into this topic, I’d like to rule out one possible solution to the problem, that is, moving the ORM metadata out from the Java code and into the orm.xml file, but keeping them targeting the same classes. This is the worst possible solution for a number of reasons. First, it’s superficial in nature: not seeing the metadata in your Java code doesn’t mean your entity model is not coupled to the persistence solution – it’s all still there, just not in your sight. Secondly, the approach of supplying metadata via orm.xml is not as mature (read: bugs), nor as mainstream (read: Stack Overflow support:) as the annotation-based solution. And thirdly, and most important, what binds you most to an ORM is NOT the field-to-column or relations mappings, but the runtime behaviour of the EntityManager: the lazy-loading, auto-flushing, PK-generation, Optimistic Locking, cascading, merging, declarative transactions, just to name a few… These are the things that would give you headaches on the medium-long term if you ever try to switch to plain JDBC, mybatis or, more dramatically, to a NoSQL data store. JPA, just as Spring Framework, as a matter of fact, was specifically designed to be as easy to bootstrap as possible, but not necessarily as convenient to use in XL projects, with tough NFRs and/or loads of business logic. Moving metadata from annotations to XML doesn’t shield you from the REAL issues associated with using an ORM. Therefore I’ll rule this option out from the start.
Two other main solutions to this problem are: (A) implement business logic directly on top of the JPA-managed entities or (B) introduce an additional ‘Domain object’ layer, distinct from the ‘Entity’ layer.
Of course, most developers would have serious doubts when creating both Customer and CustomerEntity classes and then mapping between each other, both ways, with the famous ‘get-set’ pattern 🙂 (B). This is especially painful at the beginning of a project when the corresponding objects in the two layers might be almost identical. Oh, and using an automatic mapping library that automatically maps field to field by name is cheating. As the objects in the two layers will attempt to diverge, the library might discourage this: developers might be tempted to keep the objects identical, which actually renders the entire duplication effort useless.
On the other hand, if you are guided by a through philosophy (like Domain-Driven Design), and the team has read and practised enough in order to *feel* the benefits of ‘manually’-controlling the Domain objects, both in terms of structure as of lifecycle and transactions, the segregation might come useful to some projects with strict NFRs, extensive domain logic, or otherwise large. For such cases, (B) may prove better on the long term, and I can myself think of some of my projects in which (B) would probably have been a better option.
However, for most projects, (A) is the default option for projects built on an RDB. It’s so easy! And let’s admit it, for the vast majority of the projects out there, it’s the best option.
As the project grows, however, the team might feel that JPA is no longer the best option. When they feel that, they should know there’s a middle ground in between: (C) ensure the domain logic only works on *detached* entities. This means that the repository layer should only return ‘free’ objects, on which the ORM runtime doesn’t perform any further action. This means lazy-load, auto-flush, transactions, and many other troublesome features of the ORM are simply disabled. Yes, you will have to call ‘save()’ explicitly, but you avoid many pitfalls of the ORM. The best part is that you can apply this technique only on the most complex business flows first, and gradually move away from ORM to a sort of ‘data mapper’. There is yet another key practice when entity graphs grow too much: introducing primitive foreign keys columns instead of ORM links. For example, instead of a @ManyToOne Country country, one could have Long countryId. Of course, to get the actual country you should query the CountryRepository instead of traversing a link, but this has very powerful applications, such as enforcing Aggregate boundaries and reducing the overhead of automatically fetching associations. If done carefully, this refactoring might leave the underlying DB schema untouched, so it’s something you might want to look into when ORM starts getting you into trouble. There are many other ORM best practices you might not have been following that you ought to learn at that moment.
Java Daily: What is your opinion on JPA and Hibernate. Do you think that we merge a lot of the business logic with persistence logic when we use those ORMs. In your opinion, if we use those ORMs, how easy it will be to switch the persistence logic?
Victor Rentea: JPA allows you to keep your domain logic inside your application. In my opinion, there’s no mix to worry about. The only bit expressed in an ORM-specific way is the query (JPQL). However, from a business-logic point of view, the transaction-delimitation might be interesting to control more, but this can be achieved by carefully designing the links between your Entities, like I tried to sketch in my previous answer.
Of course, besides consistency, an ORM can have a dramatic impact on your application’s performance, so again, you must be aware of the performance best practices when using Hibernate (read: https://vladmihalcea.com).
Now, if you ever decide to switch the persistence logic (happens to less than 5% of the projects, in my experience), the usual candidates are plain JDBC or a kind of noSQL. The former raises no issues, as JPQL can easily be translated to SQL in essence, the approach follows the same philosophy. However, it’s more frequent that a project might want to move to a noSQL (even for a part of its entities perhaps). The pain of this move is comparable if moving from JPA or from JDBC or myBatis. In other words, you are still in deep smelly problem, with or without an ORM. 🙂
But hopefully, you’ll be able to delineate an Aggregate, cut its links, its transactions, have biz accept eventual consistency, reimplement searches, and then reimplement its repository with a noSQL instead.
Java Daily: In a popular lecture from you, it is mentioned that a facade layer can serve the purpose to validate user input, map entities to DTOs and call the business layer. In case we use for example Spring Boot, do you think that the REST Controllers can serve the purpose of a facade or it’s better to keep the controllers simple and just delegate to the facade?
Victor Rentea: That’s a good question. Although I’ve seen both approaches, in all my architectures I prefered to keep the http concerns out the Facade. Thus, you can imagine a RestController sitting in front of each Facade, having one method for each method in the Facade, each delegating all the work to the Facade. Then, the normal question is: why didn’t I merged the RestController with the Facade?
All that I like a Controller to do is to handle any annoying http laundry work: URLs, parameter conversions, file uploads, content types, http return codes (although I discourage using those if the only client of your API is your own frontend team). Controllers are also a good place to bury the feature access control (per URL), via e.g. @PreAuthorize.
This strips the Facade from all the infrastructural non-business logic fluff, so my tests targeting the Facade can test the real stuff. Don’t get me wrong, if you are fortunate to have enough time to test everything thoroughly end-to-end, Wohooo!! Do that! But for many projects I’ve worked on, the focus was very scarce on testing so we had to carefully strike a good cost:benefit ratio with the tests we write. In other words, to test where the bugs most likely are or will appear: the logic WE wrote, not some @RestController annotations etc. Always try to test as little framework code as possible.
Plus, if you ever want a JMS message handler or timer to call a Facade, it would help to be distinct from the RestControllers.
Java Daily: You also mention that initially the use cases should be implemented in the facade and when the logic grows, to extract a business service or to split the facade. For a just starting project do you think it’s acceptable all the business logic to be part of the facade as well?
Victor Rentea: If just starting project means several weeks old, then yes. Very soon after the facades will grow over 200 lines and you’ll be forced to extract. Of course, as any other such “broad” guidelines, it’s interpretable. If “Customer” is a core concept in your application about which you heard a lot of domain logic, you’ll wait less to extract a CustomerService from the CustomerFacade, because you anticipate it will grow. What I mean, however, is that if you are not sure whether that use case you are implementing now will ever grow further, it’s perhaps better to leave it as ‘degenerate’ as a simple method in the facade, of course, after taking the trouble to extract the mapping (DTO-Entity) and repository concerns out.
Recently, I feel that extracting a “CustomerService” is not the best approach. It failed me at times, having a powerful tendency to accumulate too unrelated logic . Instead, extracting a “CreateCustomerService” it’s a much better idea. In other words, extract ONE use-case logic in a very specifically-named class, to fight the continuous entropy growth, and keep as far away as possible from degenerating into a Big Ball of Mud.
Java Daily: To what extent the DTOs should reflect the needs of the UI in your opinion? For example if we have two clients, one mobile application and one web application we will have a lot of different screens. What is your opinion, should we implement REST API endpoints for each screen on the two platforms or we should design more generic the API and let the client applications combine the data that they need.
Victor Rentea: First, let’s make a distinction between the APIs that you expose to clients unknown or that don’t exist at the time you design your API. Then, of course, you don’t have much of a choice: you must design your API taking great
For a mobile vs web client, my first intention would be to design two separate REST APIs. The two can diverge quite a lot, due to the different capabilities and requirements. Mobile apps can keep offline state and also prefer bringing as little data as possible from server-side, over the least number of requests. Furthermore, the two clients may change independently, so the Interface Segregation (the I in SOLID) still applies. I would generally prefer tolerating the duplication of some boilerplate mapping code, than fight the complexity of intertwining API demands.
Java Daily: How do you define “business logic”. What is considered a business logic and what is considered as a detail. Do you have some approach to classify what should go in a business service and what should be left as logic in the facade?
Victor Rentea: Another great, deep question! Indeed, some logic that for a project is “infrastructural” might become central (read: domain) logic for others. A similar debate applies to the distinction between Application Services and Domain Services in DDD, so you can get some inspiration from the red or blue book there (see last question for references)
The default approach is, of course, to place the implementation of the core business logic inside the Domain. But one can legitimately ask what is Core and what is Peripheral? This is even harder when your app has a single client (e.g. a browser Single-Page App), as you cannot play the “common vs client-specific” card.
Often, some logic will be regarded as clearly non-domain logic such as transformation to/from DTO sent/received via your API, error handling, access control, and perhaps validation. In other projects, validation, mapping code or even access control can be regarded as “domain logic”, especially if such an aspect is proportionally more complex than other parts of the implementation. In other words, many teams I’ve seen tend to include in the “domain” the more complex part of their logic. And honestly, it makes pretty much sense: it’s *that* logic that you want to keep as clean and simple implemented. That’s where the complexity of your project resides, after all.
The essential guideline I normally use is: whenever I want to keep a piece of logic as safe as possible, I “promote it” to be part of the domain.
Java Daily: What is your opinion on the statement that there is business logic which is the same for any system in this business area and that there is application specific business logic? Should the application specific business logic be part of the business services or it should stay in the facade from your opinion?
Victor Rentea: I agree that there are indeed a set of features common to most of the systems in a certain business area. If they are almost identical in all systems, they almost don’t deserve to be implemented, and it may be cheaper to buy of-the-shelf solutions. Depending on the context, examples might include: CDNs, ETL solutions, webSSO, indexing engines, DBs…
Other times, however, implementing the “common” part from scratch might offer some competitive advantage, such as more advanced customization, various automations, etc.
But these are decisions on the enterprise-level. When we design and implement an application, we normally don’t discuss too often about buying another piece of software.
Coming back to your question: if some piece of logic is common to an entire business area, you can probably find a Spring starter integrating that solution, or at least some *mature* library out there tackling that problem. Then, you can integrate that jar via the application layer (where the facades are).
If there’s no JAR for it out there already, then you’ll want to read some code of other/older apps facing the same tasks. Learn from them. And then implement it in your Domain layer. Afterall, they are part of your solution.
Later on, after copy pasting that logic in 3 similar applications and after seeing them in prod for a while, not sooner (!), you could extract the common part into a library of your own and integrate it via your application layer.
To summarize: either integrate some external jar, either learn some possible approaches and implement them respectfully in your Domain (extracting them out in a library in a third episode).
Java Daily: What books, videos, lectures and certifications would you recommend for software architecture?
- Book: Head First – Design Patterns
- Book: Clean Code by Robert C Martin
- Book: Rethinking Java EE Patterns (2nd edition) by Adam Bien
- Book: Implementing Domain Driven Design by Vaughn Vernon (aka “the red book”)
- Book: Domain-Driven Design: Tackling Complexity in the Heart of Software by Eric Evans (aka “the blue book”), only AFTER reading the red book , max 5 pag/day, don’t despair. It’s ~ a horror IT book. Super dense and deep.
- Book: Extreme Programming Explained by Kent Beck
- Certification: OCM JEA hard to get, but the prep walks you through sooo many design decisions.
- My youtube playlist of talks that I like
Is there anything else you would like to ask Victor Rentea? What is your opinion on the questions asked? Who would you like to see featured next? Let’s give back to the Java community together!