Java and Hibernate: Entity Management in Multi-Client, Single Codebase Apps
Inheritance and Hibernate to the rescue!
Join the DZone community and get the full member experience.Join For Free
When developing an application for only one client or only a single version, the main subjects you focus on are how to implement the features, security, performance, design, time to market, etc. What will happen if the number of clients increases, and each one of them has different requests that contradict each other? Are we going to duplicate our codebase and have different apps, which we have to implement common features to every instance separately? Or are we going to manage them from one codebase?
Diversity in labels might be managed with different property files, but will this be enough for all kinds of requests? The flow in the logic might be directed with control blocks. What will happen to them when you have 100 different clients? What will happen to the readability/manageability of the code? We might decide to extract the logic that differs from one client to another and have multiple microservices. This will allow us to have clean code, but will we be able to turn all the logic that differs from client to client into microservices? Is our structure suitable for this process? How about managing and deploying all those services? Is it worth the cost?
The method(s) we choose depends on the structure of our app, variety and frequency of requests, deployment strategies, time to market, number of development teams and their members, client support strategies, etc. For this reason, there is no single correct method.
What about diversity in core entity objects for single codebase applications? We might add all the attributes to the entity, but then we will end up with entities/database tables with dozens of attributes/columns, and most of them won't be used for each client. Instead of adding all the attributes, let's implement a cleaner solution with inheritance using Java and Hibernate.
Let’s imagine we have an entity named “Account” and a DAO class "AccountDaoImpl"as follows:
Let's again imagine that the request of one of our clients (Client X) leads us to add an attribute named "Currency" to our "Account" entity, and the other request made by our other client (Client Y) leads us to add an attribute named "Type" to the same entity. Think of this solution as the only way. The attributes must be added, but not together, and the current features of the application must not be affected by the change.
To prevent code duplication, we create a copy of our entity and replace all entity-related annotations of the class with "@MappedSuperClass". This will be our abstract class for our entity, which will be extended by other versions of the "Account" entity.
Afterward, we modify our "Account" entity as following:
Now we can add our new versions of our entity.
Code written for Client X's "Currency"-related feature will use the Account client with the "AccountClientX" entity name under the package com.artun.clientx.entity. The Account client with "AccountClientY" will be used for Client Y's "Type"-related features. Existing code that has no relation to "Currency" and "Type" will still be using our original/core "Account" entity.
What about the DAO class? We cannot use the the dao class defined above since we pass the core Account entity to GenericDao. We need separate DAO classes, but we would also like to be able to call the methods in the core DAO class. In order to achieve this, we have to make a few changes similar to the ones we did for the Account entity class.
Let's first make our current DAO class and its interface abstract.
Besides making our class abstract, we also modified our class to accept generic class parameters. We will be passing different entities when extending this abstract class.
We have three different Account entities. For this reason, we will also have three different DAO classes. The first one will be for the core Account entity, the second one will be for the ClientX Account entity, and the last one will be for the ClientY Account entity. Their implementation will be as follows:
Core Account Entity DAO:
ClientX Account Entity DAO:
ClientY Account Entity DAO:
The interfaces will be similar to the implementation classes. The interfaces will extend the interface of the abstract class. Even though there is no such thing as an abstract interface, I have named the interface of the abstract class "AbstractAccountDao" so that it can be understood easily.
Interface for AbstractAccountDaoImpl:
Interface for AccountDaoImpl:
Interface for AccountDaoImplClientX:
Interface for AccountDaoImplClientY:
Opinions expressed by DZone contributors are their own.