{"id":42005,"date":"2015-07-23T10:00:38","date_gmt":"2015-07-23T07:00:38","guid":{"rendered":"http:\/\/www.javacodegeeks.com\/?p=42005"},"modified":"2015-07-30T09:58:18","modified_gmt":"2015-07-30T06:58:18","slug":"how-to-develop-a-highly-customizable-product","status":"publish","type":"post","link":"https:\/\/www.javacodegeeks.com\/2015\/07\/how-to-develop-a-highly-customizable-product.html","title":{"rendered":"How to Develop a Highly Customizable Product"},"content":{"rendered":"<p>Have you ever heard: &#8216;We really like your product\u2026except for a few minor details.&#8217;? And then the CIO rolls out a list of additional &#8216;must have&#8217; requirements, hundreds of them, to add to your amazing product. Have you ever heard, or even said: &#8216;Team, we are about to sign up a highly-profitable contract but\u2026&#8217;? And then the customer\u2019s wish list for additional functionality becomes a headache for developers.<\/p>\n<p><a href=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2015\/07\/wooden-puzzle.jpg\"><img decoding=\"async\" class=\"alignright size-full wp-image-42009\" src=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2015\/07\/wooden-puzzle.jpg\" alt=\"wooden-puzzle\" width=\"259\" height=\"194\" \/><\/a>So, how can the product be kept a step away from the potentially dangerous ideas of your customers, yet still satisfy them? How can it be possible to maintain the highest levels of performance for a product technically designed to function in a particular way, but now with a layer of numerous add-ins? How much of a challenge will be created by the fundamental need to provide unfailing and outstanding support to the developed solution?<\/p>\n<p>In the commercial world, product customization is an increasingly desirable requirement and a number of common practices have evolved in response to this customer need. Below you can find an overview of the typical approaches; if you are already familiar with them then you are welcome to scroll down straight to \u2018<strong>The Extensions Approach<\/strong>\u2019 paragraph and learn how we resolve these challenges in what we think is a more efficient way.<\/p>\n<h2>All in One<\/h2>\n<p>The most straightforward and obvious solution to customization is to implement everything required in one core product and then employ the \u2018<a href=\"http:\/\/martinfowler.com\/bliki\/FeatureToggle.html\">feature toggle<\/a>\u2019 technique to match the requirements of each particular customer.<\/p>\n<p>The main advantage of the All in One approach is keeping the monolithic product, which appears to be a good way for certain types of products which generally cover business requirements without the need for extensive customization.<\/p>\n<p>The natural limitation of this approach is hidden in the \u2018not much customization required\u2019 assumption. Often, a product development starts with this belief, but after a number of deliveries you realize the true scale of how much customer-specific functionality is required. It\u2019s not uncommon to be caught on the horns of a dilemma; refuse custom development and potentially lose customers, or turn the source code into a <strong>garbage can<\/strong> with single-customer-specific features which are likely to be useless for the majority of end-users.<\/p>\n<p>Which option would you choose? Obviously, choosing between a rock and a hard place is not the way to success.<\/p>\n<p><em><u>Summary:<\/u> The All in One approach can be an appropriate choice only if you are sure that rare and limited customizations will be required. Otherwise, you\u2019ll be faced by the choice between manageable and supportable product vs. customer satisfaction. Let me quote Jerry Garcia, who said: \u2018Constantly choosing the lesser of two evils is still choosing evil\u2019.<\/em><\/p>\n<h2>Branching<\/h2>\n<p>If significant customization is the \u2018must have\u2019 part of a delivery then the <strong>All in One<\/strong> technique cannot be employed. There is another straightforward approach &#8211; <strong>branching<\/strong>. You simply branch the product codebase and change whatever in isolation.<\/p>\n<p>Comparing <strong>Branching <\/strong>with <strong>All in One<\/strong>, the biggest advantage is that there are no limitations applicable to the scope of customization. You use separate branches to meet the specific requirements of different customers and avoid mixing all features in the same codebase.<\/p>\n<p>However, there is a flip side of it that can become a dead end in terms of product evolution. Obviously, the product branch is the main development space: Most of the bugfixes, improvements, new functionality first get pushed to the product. Thus, frequent merging is required to keep all the customized branches synchronised with the core product. Merge is a simple operation as long as the original product source code has not been affected by the customized branch, otherwise it becomes extremely time consuming and can lead to unavoidable regression bugs.<\/p>\n<p>This approach can still work if you are limited to a very few customized branches. However, as the number of delivered instances grows, the likelihood of facing the \u2018torture by merge\u2019 becomes imminent.<\/p>\n<p><em><u>Summary:<\/u><\/em> <em>The <strong>Branching<\/strong> approach is undoubtedly very flexible and straightforward \u2013 any part of a product can be modified. However, the post-delivery stage is potentially very laborious, becoming more difficult over time and it is very unlikely to result in delivering a significant number of manageable customized branches.<\/em><\/p>\n<h2>The Entity-Attribute-Value Model<\/h2>\n<p>The <a href=\"https:\/\/en.wikipedia.org\/wiki\/Entity%E2%80%93attribute%E2%80%93value_model\">Entity-Attribute-Value Model<\/a> (aka object\u2013attribute\u2013value model, vertical database model and open schema) is a well-known and widely-used data model. EAV enables dynamic entity attributes support and is typically used in parallel with the standard relational model.<\/p>\n<p>From productization point of view the main advantage of using EAV is that you can deliver a product \u2018as is\u2019 and then adjust the data model by adding required attributes at runtime, keeping the source code clean.<\/p>\n<p>As ever, there is a downside:<\/p>\n<ul>\n<li>Limited applicability \u2013 The EAV model is limited by only enabling the addition of attributes to entities, which will then be auto-embedded to the UI, according to the pre-programmed logic.<\/li>\n<li>Extra database server load &#8211; The vertical database design often becomes a bottleneck for enterprise applications, which generally operate with large numbers of entities and attributes related to them.<\/li>\n<li>Finally, enterprise systems cannot be imagined without the presence of a sophisticated reporting engine. The EAV model has the potential to bring in numerous complications because of its &#8216;vertical&#8217; database structure.<\/li>\n<\/ul>\n<p><em><u>Summary:<\/u> The <strong>Entity-Attribute-Value<\/strong> model has great value in certain situations, such as when there is a need to provide the flexibility achieved by having additional informative data, which is not explicitly used in business logic. In other words, EAV is good in moderation, e.g. in addition to the standard relational model and <strong>plugin architecture<\/strong>.<\/em><\/p>\n<h2>The Plugin Architecture<\/h2>\n<p>The <strong>Plugin<\/strong> architecture is one of the most popular and powerful approaches \u2013 where functional logic is kept as separate artefacts known as plugins. To override existing out-of-the-box behaviour and run plugins, it is necessary to define the &#8216;Points of Customization&#8217; (aka Extension Points) in the product source code. A &#8216;Point of Customization&#8217; is a certain spot in source code where an application leafs through the attached plugins to check if a plugin contains overridden implementation to be run here. One of the variations of the plugin architecture is external scripting; when functional implementation is implemented and stored externally as a script. Script invocation is also controlled by predefined &#8216;customization points&#8217;.<\/p>\n<p>Using this plugin approach it is possible to keep the product &#8216;clean&#8217; of specific customer requirements, deliver the core product \u2018as is\u2019 and customize the behaviour as required in plugins or scripts. Another advantage of this approach is a well managed update procedure. The full separation of product and plugin functionality enables each to be updated independently of each other.<div style=\"display:inline-block; margin: 15px 0;\"> <div id=\"adngin-JavaCodeGeeks_incontent_video-0\" style=\"display:inline-block;\"><\/div> <\/div><\/p>\n<p>Of course, limitations exist: The principal limitation is that it is impossible to have complete knowledge of which custom requirements could be raised in the future. Consequently, it is only possible to guess where &#8216;points of customization&#8217; should be embedded. Of course, these could be scattered everywhere as a mitigating \u2018Just in case\u2019 plan, but this will lead to poor code readability, hard debug and additionally complicated support.<\/p>\n<p><em><u>Summary:<\/u> The <strong>Plugin architecture<\/strong> does work if the &#8216;customization points&#8217; are easy to predict, but be mindful that customization in between &#8216;customization points&#8217; is impossible.<\/em><\/p>\n<p><strong>The Extensions Approach<\/strong><\/p>\n<p>We have implemented a unique approach in our enterprise software development platform <a href=\"http:\/\/cuba-platform.com\/en\/\">CUBA<\/a>. As articulated in our <a href=\"https:\/\/www.cuba-platform.com\/en\/blog\/2015-06-29\/416\">previous article<\/a>, CUBA is very practical, living organism, created through a process of developer-driven evolution. So, based on our vast experience of off-the-shelf products, we came up with two ultimate requirements:<\/p>\n<ul>\n<li>Customer-specific code should be fully separated from the core product code<\/li>\n<li>Every part of the product code should be available for modifications<\/li>\n<\/ul>\n<p>We managed to satisfy these requirements and achieve even more with our \u2018Extensions\u2019 mechanism.<\/p>\n<p><strong>CUBA Extensions<\/strong><\/p>\n<p>An <strong>extension<\/strong> is a separate CUBA project which inherits all the features of an underlying project (i.e. your core product), using it as a library. This obviously enables developers to implement completely new functionality without affecting the parent project, but thanks to the use of the <a href=\"http:\/\/martinfowler.com\/bliki\/OpenInheritance.html\">Open Inheritance<\/a> pattern and special CUBA facilities, you can also override any part of the parent project. In summary, an extension is the place where you implement the hundreds of \u2018few minor details\u2019 discussed at the beginning of this article.<\/p>\n<p>In fact, every CUBA project is an extension of the CUBA platform itself &#8211; so it can override any platform feature. We adopted this approach ourselves to split out some out-of-the-box features (Full Text Search, Reporting, Charting, etc.) from the core platform. So if you need them in your project, you just add them as parent projects &#8211; that&#8217;s it, kind of multiple inheritance!<\/p>\n<p>In the same fashion, you can build a <strong>hierarchical customization model<\/strong>. This might sound complex, but it makes perfect sense. Let me give a real-world example: <a href=\"http:\/\/www.sherlocktaxi.com\/\">Sherlock<\/a> &#8211; is <a href=\"http:\/\/www.haulmont.com\/\">Haulmont\u2019s <\/a>complete Taxi Management Solution, supporting every aspect of running a taxi business from booking and dispatch to apps and billing. The solution covers many different aspects of the client businesses and quite a few of them are location-related. E.g. all UK taxi companies have the same legal regulations, but many of them are not applicable for the United States, and vice versa. Obviously, we don\u2019t want to implement all those regulations in the core product, because:<\/p>\n<ul>\n<li>this is an \u2018operation area specific\u2019 feature<\/li>\n<li>local regulations may have completely different effects for taxi fleet operations in different countries<\/li>\n<li>some of the customers don\u2019t require regulatory control at all<\/li>\n<\/ul>\n<p>So, we organize the multilevel extensions hierarchy:<\/p>\n<ol>\n<li>Core product contains the generic features of taxi business<\/li>\n<li>The first level of customization implements regional specifics<\/li>\n<li>The second level of customization covers the customer\u2019s wish list (if there is one!)<\/li>\n<\/ol>\n<p><a href=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2015\/07\/cuba-platform_product-schema.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-42011\" src=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2015\/07\/cuba-platform_product-schema.png\" alt=\"cuba-platform_product-schema\" width=\"742\" height=\"614\" srcset=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2015\/07\/cuba-platform_product-schema.png 742w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2015\/07\/cuba-platform_product-schema-300x248.png 300w\" sizes=\"(max-width: 742px) 100vw, 742px\" \/><\/a><\/p>\n<p>Clean and clear.<\/p>\n<p>As you see, by using extensions you neither need <strong>branching<\/strong> nor <strong>integrating all requirements<\/strong> in the core product, the code remains clean and manageable. It sounds too good to be true, so let&#8217;s see how it works in practice!<\/p>\n<h3>Adding a New Attribute to an Existing Entity<\/h3>\n<p>Let\u2019s assume we have the product definition of <em>User<\/em> entity which consists of two fields: login and password:<\/p>\n<pre class=\"brush:java\">@Entity(name = \"product$User\")\r\n@Table(name = \"PRODUCT_USER\")\r\npublic class User extends StandardEntity {\r\n    @Column(name = \"LOGIN\")\r\n    protected String login;\r\n\r\n    @Column(name = \"PASSWORD\")\r\n    protected String password;\r\n\r\n    \/\/getters and setters\r\n}\r\n<\/pre>\n<p>Now we have got an additional requirement from some of our customers to add the \u2018home address\u2019 field to users. To do that we extend the <em>User <\/em>entity in the extension:<\/p>\n<pre class=\"brush:java\">@Entity(name = \"ext$User\")\r\n@Extends(User.class)\r\npublic class ExtUser extends User {\r\n    \r\n    @Column(name = \"ADDRESS\", length = 100)\r\n    private String address;\r\n\r\n    public String getAddress() {\r\n        return address;\r\n    }\r\n\r\n    public void setAddress(String address) {\r\n        this.address = address;\r\n    }\r\n}\r\n<\/pre>\n<p>As you may have already noticed, all annotations, except for @Extends one, are common JPA annotations. The @Extends attribute is a part of the CUBA engine which globally substitutes the <em>User <\/em>entity to <em>ExtUser<\/em>, even across the product functionality.<\/p>\n<p>Using the @Extends attribute we force the platform to:<\/p>\n<ol>\n<li>always create an entity of the \u2018latest child\u2019 type<\/li>\n<\/ol>\n<pre class=\"brush:java\">User user = metadata.create(User.class); \/\/ExtUser entity will be created\r\n<\/pre>\n<ol start=\"2\">\n<li>transform all the JPQL queries before the execution so that they always return the \u2018latest childset\u2019<\/li>\n<\/ol>\n<pre class=\"brush:java\">select u from product$User u where u.name = :name \/\/returns a list of ExtUsers\r\n<\/pre>\n<ol start=\"3\">\n<li>always use the \u2018latest child\u2019 in the associated entities<\/li>\n<\/ol>\n<pre class=\"brush:java\">userSession.getUser(); \/\/returns an instance of ExtUser type\r\n<\/pre>\n<p>In other words, if an extended entity is declared, the base one is abandoned across the entire solution (product and extension) and is globally overridden by the extended one.<\/p>\n<h3>Screens Customization<\/h3>\n<p>So, we have extended the <em>User <\/em>entity by adding the address attribute and now want the changes to be reflected in the user interface. First, let us have a look on the original (product) screen declaration:<\/p>\n<pre class=\"brush:xml\">&lt;window\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 datasource=\"userDs\"\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 caption=\"msg:\/\/caption\"\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 class=\"com.haulmont.cuba.gui.app.security.user.edit.UserEditor\"\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 messagesPack=\"com.haulmont.cuba.gui.app.security.user.edit\"\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &gt;\r\n \r\n \u00a0\u00a0\u00a0 &lt;dsContext&gt;\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;datasource\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 id=\"userDs\"\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 class=\"com.haulmont.cuba.security.entity.User\"\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 view=\"user.edit\"&gt;\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/datasource&gt;\r\n \u00a0\u00a0\u00a0 &lt;\/dsContext&gt;\r\n \r\n \u00a0\u00a0\u00a0 &lt;layout&gt;\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;fieldGroup id=\"fieldGroup\" datasource=\"userDs\"&gt;\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;column&gt;\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;field id=\"login\"\/&gt;\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;field id=\"password\"\/&gt;\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/column&gt;\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/fieldGroup&gt;\r\n \u00a0\u00a0\u00a0\u00a0\u00a0 \r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;iframe id=\"windowActions\" screen=\"editWindowActions\"\/&gt;\r\n \u00a0\u00a0\u00a0 &lt;\/layout&gt;\r\n &lt;\/window&gt;\r\n<\/pre>\n<p>As you can see, a CUBA screen descriptor is represented as an ordinary XML. Obviously, we could simply re-declare the entire screen descriptor in the extension, but this would mean copy-pasting most of it. In result, if something changes in the product screen in the future, we will have to copy these changes to the extension screen manually. To avoid this, CUBA introduces the screen inheritance mechanism and all you need is describe changes to the screen:<\/p>\n<pre class=\"brush:xml\">&lt;window extends=\"\/com\/haulmont\/cuba\/gui\/app\/security\/user\/edit\/user-edit.xml\"&gt;\r\n \u00a0\u00a0\u00a0 &lt;layout&gt;\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;fieldGroup id=\"fieldGroup\"&gt;\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;column&gt;\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;field id=\"address\"\/&gt;\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/column&gt;\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/fieldGroup&gt;\r\n \u00a0\u00a0\u00a0 &lt;\/layout&gt;\r\n &lt;\/window&gt;\r\n<\/pre>\n<p>You define the ancestor screen using the <em>extends<\/em> attribute and only describe the subject to change.<\/p>\n<p>Here you are! Let\u2019s finally have a look at the result:<\/p>\n<p><a href=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2015\/07\/cuba-platform_admin.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-42013\" src=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2015\/07\/cuba-platform_admin.png\" alt=\"cuba-platform_admin\" width=\"335\" height=\"222\" srcset=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2015\/07\/cuba-platform_admin.png 335w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2015\/07\/cuba-platform_admin-300x199.png 300w\" sizes=\"(max-width: 335px) 100vw, 335px\" \/><\/a><\/p>\n<h3>Modification of the Business Logic<\/h3>\n<p>To enable business logic modification the CUBA platform uses Spring Framework, which forms a core part of the platform infrastructure.<\/p>\n<p>For example, you have a middleware component to perform price calculation procedure:<\/p>\n<pre class=\"brush:java\">@ManagedBean(\"product_PriceCalculator\")\r\npublic class PriceCalculator {\r\n    public void BigDecimal calculatePrice() { \r\n        \/\/price calculation\r\n    }\r\n}\r\n<\/pre>\n<p>To override the price calculation implementation we only need to undertake two simple actions.<\/p>\n<p>First, extend the product class and override the corresponding procedure:<\/p>\n<pre class=\"brush:java\">public class ExtPriceCalculator extends PriceCalcuator {\r\n    @Override\r\n    public void BigDecimal calculatePrice() { \r\n               \/\/modified logic goes here\r\n    }\r\n}\r\n<\/pre>\n<p>And as a final touch, register the new class in the Spring configuration using the product bean identifier:<\/p>\n<pre class=\"brush:xml\">&lt;bean id=\"product_PriceCalculator\" class=\"com.sample.extension.core.ExtPriceCalculator\"\/&gt;\r\n<\/pre>\n<p>Now <em>PriceCalculator<\/em> injection will always return the extended class instance. Consequently, the modified implementation will be used across the whole product.<\/p>\n<h3>Upgrading Base Product Version in Extension<\/h3>\n<p>As the core product evolves and new versions are released you will eventually decide to upgrade your extension to the latest product version. The process is quite simple:<\/p>\n<ol>\n<li>Specify the new version of the underlying product in extension.<\/li>\n<li>Rebuild the extension:\n<ol>\n<li>If an extension is built over the stable parts of the product API, then it is ready to run.<\/li>\n<li>If some significant modifications of the product API have taken place and those modifications overlap customization implemented in the extension, it will be necessary to support the new product API in the extension.<\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p>Most of the time a product API does not change significantly from update to update, especially in minor releases. But even if an API \u2018big-bang\u2019 occurs, a product usually keeps downward compatibility for at least a couple of future versions and the old implementation is marked as \u2018deprecated\u2019, allowing all extensions to be migrated to the newest API.<\/p>\n<h2>Conclusion<\/h2>\n<p>As a short summary I would like to illustrate the result of the comparative analysis in a tabular form:<\/p>\n<table width=\"651\">\n<tbody>\n<tr>\n<td width=\"148\"><\/td>\n<td width=\"103\"><strong>All in One<\/strong><\/td>\n<td width=\"97\"><strong>Branching<\/strong><\/td>\n<td width=\"92\"><strong>EAV<\/strong><\/td>\n<td width=\"98\"><strong>Plugins<\/strong><\/td>\n<td width=\"113\"><strong>CUBA Extensions<\/strong><\/td>\n<\/tr>\n<tr>\n<td width=\"148\"><strong>Architecture independent<\/strong><\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"103\">+<\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"97\">+<\/td>\n<td style=\"text-align: center; background-color: red;\" width=\"92\">&#8211;<\/td>\n<td style=\"text-align: center; background-color: red;\" width=\"98\">&#8211;<\/td>\n<td style=\"text-align: center; background-color: red;\" width=\"113\">&#8211;<\/td>\n<\/tr>\n<tr>\n<td width=\"148\"><strong>Dynamic customization<\/strong><\/td>\n<td style=\"text-align: center; background-color: red;\" width=\"103\">&#8211;<\/td>\n<td style=\"text-align: center; background-color: red;\" width=\"97\">&#8211;<\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"92\">+<\/td>\n<td style=\"text-align: center; background-color: yellow;\" width=\"98\">+\/-<\/td>\n<td style=\"text-align: center; background-color: red;\" width=\"113\">&#8211;<\/td>\n<\/tr>\n<tr>\n<td width=\"148\"><strong>Business logic customization<\/strong><\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"103\">+<\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"97\">+<\/td>\n<td style=\"text-align: center; background-color: red;\" width=\"92\">&#8211;<\/td>\n<td style=\"text-align: center; background-color: yellow;\" width=\"98\">+\/-<\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"113\">+<\/td>\n<\/tr>\n<tr>\n<td width=\"148\"><strong>Data model customization<\/strong><\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"103\">+<\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"97\">+<\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"92\">+<\/td>\n<td style=\"text-align: center; background-color: yellow;\" width=\"98\">+\/-<\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"113\">+<\/td>\n<\/tr>\n<tr>\n<td width=\"148\"><strong>UI customization<\/strong><\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"103\">+<\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"97\">+<\/td>\n<td style=\"text-align: center; background-color: yellow;\" width=\"92\">+\/-<\/td>\n<td style=\"text-align: center; background-color: yellow;\" width=\"98\">+\/-<\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"113\">+<\/td>\n<\/tr>\n<tr>\n<td width=\"148\"><strong>Code quality and readability<\/strong><\/td>\n<td style=\"text-align: center; background-color: red;\" width=\"103\">&#8211;<\/td>\n<td style=\"text-align: center; background-color: yellow;\" width=\"97\">+\/-<\/td>\n<td style=\"text-align: center; background-color: yellow;\" width=\"92\">+\/-<\/td>\n<td style=\"text-align: center; background-color: yellow;\" width=\"98\">+\/-<\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"113\">+<\/td>\n<\/tr>\n<tr>\n<td width=\"148\"><strong>Doesn\u2019t affect performance<\/strong><\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"103\">+<\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"97\">+<\/td>\n<td style=\"text-align: center; background-color: red;\" width=\"92\">&#8211;<\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"98\">+<\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"113\">+<\/td>\n<\/tr>\n<tr>\n<td width=\"148\"><strong>Risk of software regression<\/strong><\/td>\n<td style=\"text-align: center; background-color: red;\" width=\"103\"><strong>High<\/strong><\/td>\n<td style=\"text-align: center; background-color: red;\" width=\"97\"><strong>High<\/strong><\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"92\"><strong>Low<\/strong><\/td>\n<td style=\"text-align: center; background-color: yellow;\" width=\"98\"><strong>Medium<\/strong><\/td>\n<td style=\"text-align: center; background-color: yellow;\" width=\"113\"><strong>Medium<\/strong><\/td>\n<\/tr>\n<tr>\n<td width=\"148\"><strong>The complexity of long term support<\/strong><\/td>\n<td style=\"text-align: center; background-color: red;\" width=\"103\"><strong>Extremal <\/strong><\/td>\n<td style=\"text-align: center; background-color: red;\" width=\"97\"><strong>Extremal <\/strong><\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"92\"><strong>Low<\/strong><\/td>\n<td style=\"text-align: center; background-color: yellow;\" width=\"98\"><strong>Medium<\/strong><\/td>\n<td style=\"text-align: center; background-color: yellow;\" width=\"113\"><strong>Medium<\/strong><\/td>\n<\/tr>\n<tr>\n<td width=\"148\"><strong>Scalability<\/strong><\/td>\n<td style=\"text-align: center; background-color: red;\" width=\"103\"><strong>Low<\/strong><\/td>\n<td style=\"text-align: center; background-color: red;\" width=\"97\"><strong>Low<\/strong><\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"92\"><strong>High<\/strong><\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"98\"><strong>High<\/strong><\/td>\n<td style=\"text-align: center; background-color: green;\" width=\"113\"><strong>High<\/strong><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<p>As you can see the Extension approach is powerful, but one thing it lacks is the ability to fine-tune the system on the fly (dynamically customized). To overcome this, CUBA also provides full support for the <strong>Entity-Attribute-Value<\/strong> model and <strong>Plugin\/Scripting <\/strong>approach.<\/p>\n<p>I hope you will find this overview useful and of course your feedback is much appreciated.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Have you ever heard: &#8216;We really like your product\u2026except for a few minor details.&#8217;? And then the CIO rolls out a list of additional &#8216;must have&#8217; requirements, hundreds of them, to add to your amazing product. Have you ever heard, or even said: &#8216;Team, we are about to sign up a highly-profitable contract but\u2026&#8217;? And &hellip;<\/p>\n","protected":false},"author":925,"featured_media":40751,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[1164,1175,1160,1165,1200,1161,1166,1162,1204],"class_list":["post-42005","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-enterprise-java","tag-cuba","tag-customization","tag-enterprise-software","tag-framework","tag-java","tag-platform","tag-rad","tag-rapid-development","tag-software-development"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>How to Develop a Highly Customizable Product - Java Code Geeks<\/title>\n<meta name=\"description\" content=\"Have you ever heard: &#039;We really like your product\u2026except for a few minor details.&#039;? And then the CIO rolls out a list of additional &#039;must have&#039;\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.javacodegeeks.com\/2015\/07\/how-to-develop-a-highly-customizable-product.html\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to Develop a Highly Customizable Product - Java Code Geeks\" \/>\n<meta property=\"og:description\" content=\"Have you ever heard: &#039;We really like your product\u2026except for a few minor details.&#039;? And then the CIO rolls out a list of additional &#039;must have&#039;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.javacodegeeks.com\/2015\/07\/how-to-develop-a-highly-customizable-product.html\" \/>\n<meta property=\"og:site_name\" content=\"Java Code Geeks\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/javacodegeeks\" \/>\n<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/CUBAplatform\" \/>\n<meta property=\"article:published_time\" content=\"2015-07-23T07:00:38+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2015-07-30T06:58:18+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2015\/06\/cuba-platform-logo.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"150\" \/>\n\t<meta property=\"og:image:height\" content=\"150\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"CUBA Platform\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@CubaPlatform\" \/>\n<meta name=\"twitter:site\" content=\"@javacodegeeks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"CUBA Platform\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2015\\\/07\\\/how-to-develop-a-highly-customizable-product.html#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2015\\\/07\\\/how-to-develop-a-highly-customizable-product.html\"},\"author\":{\"name\":\"CUBA Platform\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/470baf310b973af7e1cdcc621c98c1fc\"},\"headline\":\"How to Develop a Highly Customizable Product\",\"datePublished\":\"2015-07-23T07:00:38+00:00\",\"dateModified\":\"2015-07-30T06:58:18+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2015\\\/07\\\/how-to-develop-a-highly-customizable-product.html\"},\"wordCount\":2386,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2015\\\/07\\\/how-to-develop-a-highly-customizable-product.html#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2015\\\/06\\\/cuba-platform-logo.jpg\",\"keywords\":[\"CUBA\",\"Customization\",\"Enterprise Software\",\"framework\",\"Java\",\"Platform\",\"RAD\",\"Rapid Development\",\"Software Development\"],\"articleSection\":[\"Enterprise Java\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.javacodegeeks.com\\\/2015\\\/07\\\/how-to-develop-a-highly-customizable-product.html#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2015\\\/07\\\/how-to-develop-a-highly-customizable-product.html\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2015\\\/07\\\/how-to-develop-a-highly-customizable-product.html\",\"name\":\"How to Develop a Highly Customizable Product - Java Code Geeks\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2015\\\/07\\\/how-to-develop-a-highly-customizable-product.html#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2015\\\/07\\\/how-to-develop-a-highly-customizable-product.html#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2015\\\/06\\\/cuba-platform-logo.jpg\",\"datePublished\":\"2015-07-23T07:00:38+00:00\",\"dateModified\":\"2015-07-30T06:58:18+00:00\",\"description\":\"Have you ever heard: 'We really like your product\u2026except for a few minor details.'? And then the CIO rolls out a list of additional 'must have'\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2015\\\/07\\\/how-to-develop-a-highly-customizable-product.html#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.javacodegeeks.com\\\/2015\\\/07\\\/how-to-develop-a-highly-customizable-product.html\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2015\\\/07\\\/how-to-develop-a-highly-customizable-product.html#primaryimage\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2015\\\/06\\\/cuba-platform-logo.jpg\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2015\\\/06\\\/cuba-platform-logo.jpg\",\"width\":150,\"height\":150},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2015\\\/07\\\/how-to-develop-a-highly-customizable-product.html#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Java\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/category\\\/java\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Enterprise Java\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/category\\\/java\\\/enterprise-java\"},{\"@type\":\"ListItem\",\"position\":4,\"name\":\"How to Develop a Highly Customizable Product\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"name\":\"Java Code Geeks\",\"description\":\"Java Developers Resource Center\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"alternateName\":\"JCG\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.javacodegeeks.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\",\"name\":\"Exelixis Media P.C.\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"width\":864,\"height\":246,\"caption\":\"Exelixis Media P.C.\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/javacodegeeks\",\"https:\\\/\\\/x.com\\\/javacodegeeks\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/470baf310b973af7e1cdcc621c98c1fc\",\"name\":\"CUBA Platform\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/ae4bc5d6ffc84f51ef6c68f164799b97552245e8de25d62557a961e60c6920e9?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/ae4bc5d6ffc84f51ef6c68f164799b97552245e8de25d62557a961e60c6920e9?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/ae4bc5d6ffc84f51ef6c68f164799b97552245e8de25d62557a961e60c6920e9?s=96&d=mm&r=g\",\"caption\":\"CUBA Platform\"},\"description\":\"CUBA Platform is a full stack Java framework for enterprise applications development. Compared to popular frameworks like Grails it offers a higher level of abstraction, still allowing direct access to the low level API. CUBA Studio takes care of project and build files, database scripts, screens scaffolding, visual design and other routine tasks, enabling developer to focus on the application logic. With a wide range of out of the box components this provides massive development time savings, proven by hundreds of delivered projects. CUBA Platform is developed by Haulmont.\",\"sameAs\":[\"https:\\\/\\\/www.cuba-platform.com\",\"https:\\\/\\\/www.facebook.com\\\/CUBAplatform\",\"https:\\\/\\\/x.com\\\/CubaPlatform\"],\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/author\\\/cuba-platform\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"How to Develop a Highly Customizable Product - Java Code Geeks","description":"Have you ever heard: 'We really like your product\u2026except for a few minor details.'? And then the CIO rolls out a list of additional 'must have'","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.javacodegeeks.com\/2015\/07\/how-to-develop-a-highly-customizable-product.html","og_locale":"en_US","og_type":"article","og_title":"How to Develop a Highly Customizable Product - Java Code Geeks","og_description":"Have you ever heard: 'We really like your product\u2026except for a few minor details.'? And then the CIO rolls out a list of additional 'must have'","og_url":"https:\/\/www.javacodegeeks.com\/2015\/07\/how-to-develop-a-highly-customizable-product.html","og_site_name":"Java Code Geeks","article_publisher":"https:\/\/www.facebook.com\/javacodegeeks","article_author":"https:\/\/www.facebook.com\/CUBAplatform","article_published_time":"2015-07-23T07:00:38+00:00","article_modified_time":"2015-07-30T06:58:18+00:00","og_image":[{"width":150,"height":150,"url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2015\/06\/cuba-platform-logo.jpg","type":"image\/jpeg"}],"author":"CUBA Platform","twitter_card":"summary_large_image","twitter_creator":"@CubaPlatform","twitter_site":"@javacodegeeks","twitter_misc":{"Written by":"CUBA Platform","Est. reading time":"13 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.javacodegeeks.com\/2015\/07\/how-to-develop-a-highly-customizable-product.html#article","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/2015\/07\/how-to-develop-a-highly-customizable-product.html"},"author":{"name":"CUBA Platform","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/470baf310b973af7e1cdcc621c98c1fc"},"headline":"How to Develop a Highly Customizable Product","datePublished":"2015-07-23T07:00:38+00:00","dateModified":"2015-07-30T06:58:18+00:00","mainEntityOfPage":{"@id":"https:\/\/www.javacodegeeks.com\/2015\/07\/how-to-develop-a-highly-customizable-product.html"},"wordCount":2386,"commentCount":0,"publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"image":{"@id":"https:\/\/www.javacodegeeks.com\/2015\/07\/how-to-develop-a-highly-customizable-product.html#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2015\/06\/cuba-platform-logo.jpg","keywords":["CUBA","Customization","Enterprise Software","framework","Java","Platform","RAD","Rapid Development","Software Development"],"articleSection":["Enterprise Java"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.javacodegeeks.com\/2015\/07\/how-to-develop-a-highly-customizable-product.html#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.javacodegeeks.com\/2015\/07\/how-to-develop-a-highly-customizable-product.html","url":"https:\/\/www.javacodegeeks.com\/2015\/07\/how-to-develop-a-highly-customizable-product.html","name":"How to Develop a Highly Customizable Product - Java Code Geeks","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.javacodegeeks.com\/2015\/07\/how-to-develop-a-highly-customizable-product.html#primaryimage"},"image":{"@id":"https:\/\/www.javacodegeeks.com\/2015\/07\/how-to-develop-a-highly-customizable-product.html#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2015\/06\/cuba-platform-logo.jpg","datePublished":"2015-07-23T07:00:38+00:00","dateModified":"2015-07-30T06:58:18+00:00","description":"Have you ever heard: 'We really like your product\u2026except for a few minor details.'? And then the CIO rolls out a list of additional 'must have'","breadcrumb":{"@id":"https:\/\/www.javacodegeeks.com\/2015\/07\/how-to-develop-a-highly-customizable-product.html#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.javacodegeeks.com\/2015\/07\/how-to-develop-a-highly-customizable-product.html"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.javacodegeeks.com\/2015\/07\/how-to-develop-a-highly-customizable-product.html#primaryimage","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2015\/06\/cuba-platform-logo.jpg","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2015\/06\/cuba-platform-logo.jpg","width":150,"height":150},{"@type":"BreadcrumbList","@id":"https:\/\/www.javacodegeeks.com\/2015\/07\/how-to-develop-a-highly-customizable-product.html#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.javacodegeeks.com\/"},{"@type":"ListItem","position":2,"name":"Java","item":"https:\/\/www.javacodegeeks.com\/category\/java"},{"@type":"ListItem","position":3,"name":"Enterprise Java","item":"https:\/\/www.javacodegeeks.com\/category\/java\/enterprise-java"},{"@type":"ListItem","position":4,"name":"How to Develop a Highly Customizable Product"}]},{"@type":"WebSite","@id":"https:\/\/www.javacodegeeks.com\/#website","url":"https:\/\/www.javacodegeeks.com\/","name":"Java Code Geeks","description":"Java Developers Resource Center","publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"alternateName":"JCG","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.javacodegeeks.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.javacodegeeks.com\/#organization","name":"Exelixis Media P.C.","url":"https:\/\/www.javacodegeeks.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","width":864,"height":246,"caption":"Exelixis Media P.C."},"image":{"@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/javacodegeeks","https:\/\/x.com\/javacodegeeks"]},{"@type":"Person","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/470baf310b973af7e1cdcc621c98c1fc","name":"CUBA Platform","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/ae4bc5d6ffc84f51ef6c68f164799b97552245e8de25d62557a961e60c6920e9?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/ae4bc5d6ffc84f51ef6c68f164799b97552245e8de25d62557a961e60c6920e9?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/ae4bc5d6ffc84f51ef6c68f164799b97552245e8de25d62557a961e60c6920e9?s=96&d=mm&r=g","caption":"CUBA Platform"},"description":"CUBA Platform is a full stack Java framework for enterprise applications development. Compared to popular frameworks like Grails it offers a higher level of abstraction, still allowing direct access to the low level API. CUBA Studio takes care of project and build files, database scripts, screens scaffolding, visual design and other routine tasks, enabling developer to focus on the application logic. With a wide range of out of the box components this provides massive development time savings, proven by hundreds of delivered projects. CUBA Platform is developed by Haulmont.","sameAs":["https:\/\/www.cuba-platform.com","https:\/\/www.facebook.com\/CUBAplatform","https:\/\/x.com\/CubaPlatform"],"url":"https:\/\/www.javacodegeeks.com\/author\/cuba-platform"}]}},"_links":{"self":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/42005","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/users\/925"}],"replies":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/comments?post=42005"}],"version-history":[{"count":0,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/42005\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media\/40751"}],"wp:attachment":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media?parent=42005"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/categories?post=42005"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/tags?post=42005"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}