If somebody asks you just right now "What do you require most from your business software in general?", what will pop up in your mind first?
To be ...reliable, secure, feature rich? ... Cheaper? – Noo!...
What about being configurable, flexible, or extendable?
Well, it is clear that for business applications, in comparison to other types of
software bundles like HTTP Servers, Databases, Office Packages, etc., it is vital to be adaptable to the particular business processes within a given company.
How much effort do businesses usually spend for customizations? What is the cost of that? What is the timeframe?
Sounds familiar? Then we are on the right track.
How does the product owner usually imagine the adaptable software?
As a single generic screen, fully "responsive" and depending on the user's type, role, use-case, and any other parameter one can imagine. On the other side it results in thousands of configuration entries.
This works! We proudly have enough examples based on this approach.
The product owner is on the safe side - "reusing"! He/she doesn't make any "double efforts"!
Unfortunately, there are several issues with this approach:
- Relatively big effort to develop such a highly configurable screen
- Relatively big effort to configure it - per user, role, etc.
- Relatively big effort to change it once being shipped
- Relatively big effort to support it
- Relatively low performance
- ... And the saddest part is that, usually, it becomes equally complex and difficult for everyone.
(Disclaimer: Everything mentioned above is intentionally exaggerated.)
What to do then?
The other option is to build tailored screens and underlying services for a given role.
Can we build multiple end-to-end vertical stacks easily and fast with Dirigible on SAP HANA Cloud Platform?
You already know that, so we can finish the article now.
Unfortunately, this is not so simple either.
What if we want to reuse the services provided by the vendor of the packaged software,
and want to add just a few fields, but in the way to survive the next update/upgrade of the basis?
You would like to have something like a BAdI or Eclipse plugin mechanisms, right?
Let’s see whether the extensibility feature we have recently added to Dirigible can supply such a need...
To explore the capabilities of extensions in Dirigible, we will create two projects – “products” and “products_discounter”. The first project will be the one with the extension point and the second will be with the extension. The “products” project will display information about products
and the “products_discounter” will show the discounted items.
The final result will be:
In the “products” project we will create
In the “products_discounter” project we will create
We have plenty of work, so let’s get started.
1|Dumbbell|100|http://www.aloyd.com/home/media/catalog/product/cache/1/image/9df78eab33525d08d6e5fb8d27136e95/r/u/rubber-hex-dumbbell.jpg
2|Leg extension|155|http://www.pacillo.com/products/kf_leg_extension_b.jpg
3|Squad rack|153|http://images.monstermarketplace.com/gym-flooring-and-fitness-equipment/valor-bd-19-sawtooth-squat-rack-bench-press-combo-600x600.jpg
4|Shoulder press|300|http://gymstogo.com/commercial-gym-equipment/plate-loaded/precor-fitness/shoulder-press--550-discovery-line.jpg
It should look like this:
var extensions = extensionManager.getExtensions("/products/discounted_products");
for (var i=0;i<extensions.length;i++) {
var extension = require(extensions[i]);
result = extension.getDiscounts();
}
It should look like this:
var menu = [
{
"name": "Products",
"link": "../../web/products/products.html",
"subMenu": []
}
];
var extensions = extensionManager.getExtensions("/products/products_menu");
for (var i=0;i<extensions.length;i++) {
var extension = require(extensions[i]);
menu = extension.getMenuItems(menu);
}
response.setContentType("text/json");
response.getWriter().println(JSON.stringify(menu));
<!DOCTYPE html>
<html lang="en" ng-app>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<title>Home</title>
<link rel="stylesheet"
href="../../components/bootstrap/dist/css/bootstrap.min.css">
</head>
<body>
<div id="wrap">
<div class="container">
<div ng-controller="ListController">
<div class="col-lg-3 col-md-6 hero-feature ng-scope" ng-repeat="entry in data">
<a class="thumbnail btn btn-default" href="#/content" target="_self">
<img src="{{entry.image}}">
<div class="caption">
<h3 class="ng-binding">{{entry.name}}</h3>
<p>$ {{entry.price}}</p>
</div>
</a>
</div>
</div>
</div>
</div>
<script src="../../components/angular/angular.min.js"></script>
<script src="../../components/angular-resource/angular-resource.min.js"></script>
<script type="text/javascript">
function ListController($scope, $http) {
var url = '/dirigible/js/products/products.js';
$http.get(url)
.success(function(data) {
$scope.data = data;
});
}
</script>
</body>
</html>
So far, so good. To complete the UI part in the “products” project we need to make one last change.
$.getJSON("main.menu", function(data) {
with
$.getJSON("../../js/products/menu.js", function(data) {
Now we are ready with the main project.
Let’s add an extension project that will provide the discounted products.
1|0.25
2|0.40
function createEntity(resultSet, data) {
var result = {};
result.id = resultSet.getInt("ID");
result.name = resultSet.getString("NAME");
result.image = resultSet.getString("IMAGE");
result.price = resultSet.getString("PRICE");
var discount = resultSet.getString("DISCOUNT");
if(discount !== null && discount > 0 ){
result.discount_price = result.price - (result.price * discount);
}
return result;
}
exports.getDiscounts = function() {
var discounts = [];
var connection = datasource.getConnection();
try {
var sql = "select p.id, p.name, p.image, p.price, d.discount from products p left join discounted_products d on p.id = d.product_id order by d.discount";
var statement = connection.prepareStatement(sql);
var resultSet = statement.executeQuery();
var value;
while (resultSet.next()) {
discounts.push(createEntity(resultSet));
}
} catch(e){
} finally {
connection.close();
return discounts;
}
};
exports.getMenuItems = function(menu) {
for(var i = 0 ; i < menu.length; i ++ ){
if(menu[i].name == "Products"){
menu[i].link = "../../web/products_discounter/discounted.html";
}
}
return menu;
};
Last but not least, let's add some UI.
<!DOCTYPE html>
<html lang="en" ng-app>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<title>Home</title>
<link rel="stylesheet" href="../../components/bootstrap/dist/css/bootstrap.min.css">
<style>
h1,h2,h3,h4,h5,h6,p {
white-space: normal;
}
.discounted{
border-style: solid;
border-color: red;
border-radius: 10px;
}
.discounted:hover
{
background-color: red;
}
.discounted:hover h3 {
color:white;
}
.discounted:hover span {
color:white;
}
.old-price {
text-decoration:line-through;
font-size: 50%
}
.new-price {
color: red;
font-weight: bold;
}
</style>
</head>
<body>
<div id="wrap">
<div class="container">
<div ng-controller="ListController">
<div class="col-lg-3 col-md-3 col-sm-4 col-xs-6 hero-feature ng-scope" ng-repeat="entry in data">
<a class="thumbnail btn btn-default" ng-class="{discounted: entry.discount_price}" href="#/content" target="_self">
<img src="{{entry.image}}">
<div class="caption">
<h3 class="ng-binding">{{entry.name}}</h3>
<h3 ng-hide="entry.discount_price" >$ {{entry.price}}</h3>
<div ng-show="entry.discount_price">
<h3><span class="old-price">$ {{entry.price}}</span> <span class="new-price">$ {{entry.discount_price}}</span></h3>
</div>
</div>
</a>
</div>
</div>
</div>
</div>
<script src="../../components/angular/angular.min.js"></script>
<script src="../../components/angular-resource/angular-resource.min.js"></script>
<script type="text/javascript">
function ListController($scope, $http) {
var url = '/dirigible/js/products/products.js';
$http.get(url)
.success(function(data) {
$scope.data = data;
});
}
</script>
</body>
</html>
Because the page with discounted products is in another project wherethere is neither "header.html" nor "footer.html", a menu is lacking, too. The first approach is just to copy and paste them, but this is not a good solution. We don't want double maintenance of our code, we just want to reuse the "header" and "footer" files. For that purpose, we’ll make little magic in the “products_discounter” project.
/products/header.html
/products/footer.html
Finally we are done. We have reached our goal! :smile:
Do you think this approach can be helpful for your extensibility story ... or you still prefer the "configurations" way?
The project site: http://www.dirigible.io
The source code is available at GitHub - http://github.com/SAP/cloud-dirigible
Forum: http://forum.dirigible.io
Twitter: https://twitter.com/dirigible_io
Youtube: https://www.youtube.com/channel/UCYnsiVQ0M9iQLqP5DXCLMBA/
Help: http://help.dirigible.io
Samples: http://samples.dirigible.io
Google Group: https://plus.google.com/111171361109860650442/posts
Blog: http://dirigible-logbook.blogspot.com/
Dirigible on SAP HANA Cloud Platform:Dirigible on SAP HANA Cloud Platform
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
26 | |
14 | |
13 | |
13 | |
12 | |
8 | |
8 | |
7 | |
5 | |
5 |