[CORD-1943] New service graph
- labels
- enforcing service position
- started documentation
- toggling service instances
- toggle fullscreen

Change-Id: I01b71fb2607fb58711d4624f6b5b6479609b2f4f
(cherry picked from commit 8cf33a3881ee15ebf93094f5b24d757af89ee9e9)
diff --git a/src/app/service-graph/components/coarse/coarse.component.scss b/src/app/service-graph/components/coarse/coarse.component.scss
deleted file mode 100644
index fabfb91..0000000
--- a/src/app/service-graph/components/coarse/coarse.component.scss
+++ /dev/null
@@ -1,59 +0,0 @@
-
-/*
- * Copyright 2017-present Open Networking Foundation
-
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
-
- * http://www.apache.org/licenses/LICENSE-2.0
-
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-@import './../../../style/vars.scss';
-@import '../../../../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/variables';
-
-xos-coarse-tenancy-graph {
-  display: block;
-  // background: $color-accent;
-
-  svg {
-    height: 400px;
-    width: 100%;
-    background-color: $panel-filled-bg;
-    border-radius: 3px;
-  }
-
-  .node-group {
-
-    .node {
-      cursor: pointer;
-    }
-
-    .node > rect {
-      stroke: $color-accent;
-      fill: $background-color;
-    }
-
-    .node > text {
-      fill: #fff;
-      font-size: 20px;
-    }
-  }
-
-  .link-group {
-    line {
-      stroke: $color-accent;
-    }
-  }
-  .arrow-marker {
-    stroke: $color-accent;
-    fill: $color-accent;
-  }
-}
\ No newline at end of file
diff --git a/src/app/service-graph/components/coarse/coarse.component.ts b/src/app/service-graph/components/coarse/coarse.component.ts
deleted file mode 100644
index c8e2361..0000000
--- a/src/app/service-graph/components/coarse/coarse.component.ts
+++ /dev/null
@@ -1,265 +0,0 @@
-
-/*
- * Copyright 2017-present Open Networking Foundation
-
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
-
- * http://www.apache.org/licenses/LICENSE-2.0
-
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-import './coarse.component.scss';
-import * as d3 from 'd3';
-import * as $ from 'jquery';
-import * as _ from 'lodash';
-import {IXosServiceGraphStore} from '../../services/service-graph.store';
-import {IXosServiceGraph, IXosServiceGraphNode, IXosServiceGraphLink} from '../../interfaces';
-import {XosServiceGraphConfig as config} from '../../graph.config';
-import {IXosDebouncer} from '../../../core/services/helpers/debounce.helper';
-import {Subscription} from 'rxjs';
-import {IXosGraphHelpers} from '../../services/d3-helpers/graph.helpers';
-import {IXosServiceGraphReducer, IXosServiceGraphExtender} from '../../services/graph.extender';
-
-class XosCoarseTenancyGraphCtrl {
-
-  static $inject = [
-    '$log',
-    'XosServiceGraphStore',
-    'XosDebouncer',
-    'XosGraphHelpers',
-    'XosServiceGraphExtender'
-  ];
-
-  public graph: IXosServiceGraph;
-
-  private CoarseGraphSubscription: Subscription;
-  private svg;
-  private forceLayout;
-  private linkGroup;
-  private nodeGroup;
-  private textSize = 20;
-  private textOffset = this.textSize / 4;
-
-  // debounced functions
-  private renderGraph;
-
-  constructor (
-    private $log: ng.ILogService,
-    private XosServiceGraphStore: IXosServiceGraphStore,
-    private XosDebouncer: IXosDebouncer,
-    private XosGraphHelpers: IXosGraphHelpers,
-    private XosServiceGraphExtender: IXosServiceGraphExtender
-  ) {
-
-  }
-
-  $onInit() {
-    this.renderGraph = this.XosDebouncer.debounce(this._renderGraph, 500, this);
-
-    this.CoarseGraphSubscription = this.XosServiceGraphStore.getCoarse()
-      .subscribe(
-        (graph: IXosServiceGraph) => {
-          this.$log.debug(`[XosCoarseTenancyGraph] Coarse Event and render`, graph);
-
-          // id there are no data, do nothing
-          if (graph.nodes.length === 0) {
-            return;
-          }
-          this.graph = graph;
-
-          _.forEach(this.XosServiceGraphExtender.getCoarse(), (r: IXosServiceGraphReducer) => {
-            graph = r.reducer(graph);
-          });
-          this.renderGraph();
-        },
-        err => {
-          this.$log.error(`[XosCoarseTenancyGraph] Coarse Event error`, err);
-        });
-
-    this.handleSvg();
-    this.setupForceLayout();
-
-    $(window).on('resize', () => {
-      this.setupForceLayout();
-      this.renderGraph();
-    });
-  }
-
-  $onDestroy() {
-    this.CoarseGraphSubscription.unsubscribe();
-  }
-
-  private _renderGraph() {
-    if (!angular.isDefined(this.graph) || !angular.isDefined(this.graph.nodes) || !angular.isDefined(this.graph.links)) {
-      return;
-    }
-    this.addNodeLinksToForceLayout(this.graph);
-    this.renderNodes(this.graph.nodes);
-    this.renderLinks(this.graph.links);
-  }
-
-  private getSvgDimensions(): {width: number, height: number} {
-    return {
-      width: $('xos-coarse-tenancy-graph svg').width() || 0,
-      height: $('xos-coarse-tenancy-graph svg').height() || 0
-    };
-  }
-
-  private handleSvg() {
-    this.svg = d3.select('svg');
-
-    this.svg.append('svg:defs')
-      .selectAll('marker')
-      .data(config.markers)
-      .enter()
-      .append('svg:marker')
-      .attr('id', d => d.id)
-      .attr('viewBox', d => d.viewBox)
-      .attr('refX', d => d.refX)
-      .attr('refY', d => d.refY)
-      .attr('markerWidth', d => d.width)
-      .attr('markerHeight', d => d.height)
-      .attr('orient', 'auto')
-      .attr('class', d => `${d.id}-marker`)
-      .append('svg:path')
-      .attr('d', d => d.path);
-
-    this.linkGroup = this.svg.append('g')
-      .attr({
-        class: 'link-group'
-      });
-
-    this.nodeGroup = this.svg.append('g')
-      .attr({
-        class: 'node-group'
-      });
-  }
-
-  private collide(n: any) {
-    const svgDim = this.getSvgDimensions();
-    const x = Math.max(n.width / 2, Math.min(n.x, svgDim.width - (n.width / 2)));
-    const y = Math.max(n.height / 2, Math.min(n.y, svgDim.height - (n.height / 2)));
-    return `${x}, ${y}`;
-  }
-
-  private setupForceLayout() {
-
-    let svgDim = this.getSvgDimensions();
-
-    const tick = () => {
-
-      this.nodeGroup.selectAll('g.node')
-        .attr({
-          transform: d => `translate(${this.collide(d)})`
-        });
-
-      this.linkGroup.selectAll('line')
-        .attr({
-          x1: l => l.source.x || 0,
-          y1: l => l.source.y || 0,
-          x2: l => l.target.x || 0,
-          y2: l => l.target.y || 0,
-        });
-    };
-
-    this.forceLayout = d3.layout.force()
-      .size([svgDim.width, svgDim.height])
-      .linkDistance(config.force.linkDistance)
-      .charge(config.force.charge)
-      .gravity(config.force.gravity)
-      .on('tick', tick);
-  }
-
-  private addNodeLinksToForceLayout(data: IXosServiceGraph) {
-    this.forceLayout
-      .nodes(data.nodes)
-      .links(data.links)
-      .start();
-  }
-
-  private renderNodes(nodes: IXosServiceGraphNode[]) {
-    const self = this;
-    const node = this.nodeGroup
-      .selectAll('g.node')
-      .data(nodes, n => n.id);
-
-    const svgDim = this.getSvgDimensions();
-    const entering = node.enter()
-      .append('g')
-      .attr({
-        id: n => n.id,
-        class: n => `node ${this.XosGraphHelpers.parseElemClasses(n.d3Class)}`,
-        transform: `translate(${svgDim.width / 2}, ${svgDim.height / 2})`
-      })
-      .call(this.forceLayout.drag)
-      .on('mousedown', () => {
-        d3.event.stopPropagation();
-      })
-      .on('mouseup', (d) => {
-        d.fixed = true;
-      });
-
-    entering.append('rect')
-      .attr({
-        rx: config.node.radius,
-        ry: config.node.radius
-      });
-
-    entering.append('text')
-      .attr({
-        'text-anchor': 'middle',
-        'transform': `translate(0,${this.textOffset})`
-      })
-      .text(n => n.label);
-      // .text(n => `${n.id} - ${n.label}`);
-
-    const existing = node.selectAll('rect');
-
-    // resize node > rect as contained text
-
-    existing.each(function(d: any) {
-      const textBBox = self.XosGraphHelpers.getSiblingTextBBox(this);
-      const rect = d3.select(this);
-      rect.attr({
-        width: textBBox.width + config.node.padding,
-        height: textBBox.height + config.node.padding,
-        x: textBBox.x - (config.node.padding / 2),
-        y: (textBBox.y + self.textOffset) - (config.node.padding / 2)
-      });
-      d.width = textBBox.width + config.node.padding;
-      d.height = textBBox.height + config.node.padding;
-    });
-
-  }
-
-  private renderLinks(links: IXosServiceGraphLink[]) {
-    const link = this.linkGroup
-      .selectAll('line')
-      .data(links, l => l.id);
-
-    const entering = link.enter();
-
-    // TODO read classes from graph links
-
-    entering.append('line')
-      .attr({
-        id: n => n.id,
-        class: l => `link ${this.XosGraphHelpers.parseElemClasses(l.d3Class)}`,
-        'marker-start': 'url(#arrow)'
-      });
-  }
-}
-
-export const XosCoarseTenancyGraph: angular.IComponentOptions = {
-  template: require('./coarse.component.html'),
-  controllerAs: 'vm',
-  controller: XosCoarseTenancyGraphCtrl,
-};
diff --git a/src/app/service-graph/components/fine-grained/fine-grained.component.html b/src/app/service-graph/components/fine-grained/fine-grained.component.html
deleted file mode 100644
index 2e8b92e..0000000
--- a/src/app/service-graph/components/fine-grained/fine-grained.component.html
+++ /dev/null
@@ -1,22 +0,0 @@
-
-<!--
-Copyright 2017-present Open Networking Foundation
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-
-
-<h1>Fine Grained Tenancy Graph</h1>
-
-<svg>
-</svg>
diff --git a/src/app/service-graph/components/fine-grained/fine-grained.component.scss b/src/app/service-graph/components/fine-grained/fine-grained.component.scss
deleted file mode 100644
index 29ea116..0000000
--- a/src/app/service-graph/components/fine-grained/fine-grained.component.scss
+++ /dev/null
@@ -1,96 +0,0 @@
-
-/*
- * Copyright 2017-present Open Networking Foundation
-
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
-
- * http://www.apache.org/licenses/LICENSE-2.0
-
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-@import './../../../style/vars.scss';
-@import '../../../../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/variables';
-
-xos-fine-grained-tenancy-graph {
-  display: block;
-  height: 100%;
-  // background: $color-accent;
-
-  svg {
-    height: 90%;
-    width: 100%;
-    background-color: $panel-filled-bg;
-    border-radius: 3px;
-  }
-
-  .node-group {
-
-    .node {
-      cursor: pointer;
-    }
-
-    .node .symbol {
-        fill-rule: evenodd;
-        stroke: #bbddff;
-        stroke-width: 4.0px;
-        fill: none;
-    }
-    .node .symbol-bg {
-        fill-rule: evenodd;
-        stroke: none;
-        fill: $background-color;
-    }
-
-    .node {
-      rect, circle {
-        stroke: $color-accent;
-        fill: $background-color;
-      }
-
-      &.network > circle{
-        stroke: blue;
-      }
-
-      &.tenant, &.serviceinstance > rect{
-        stroke: green;
-      }
-
-      &.subscriber > rect,
-      &.tenantroot > rect{
-        stroke: red;
-      }
-    }
-
-    .node > text {
-      fill: #fff;
-      font-size: 20px;
-    }
-  }
-
-  .link-group {
-    line {
-      stroke: $color-accent;
-
-      &.ext-service-instance {
-        stroke: green;
-      }
-
-      &.ext-owner {
-        stroke: green;
-        stroke-dasharray: 5;
-      }
-    }
-  }
-  .arrow-marker {
-    stroke: $color-accent;
-    fill: $color-accent;
-  }
-}
\ No newline at end of file
diff --git a/src/app/service-graph/components/fine-grained/fine-grained.component.ts b/src/app/service-graph/components/fine-grained/fine-grained.component.ts
deleted file mode 100644
index 7d21131..0000000
--- a/src/app/service-graph/components/fine-grained/fine-grained.component.ts
+++ /dev/null
@@ -1,388 +0,0 @@
-
-/*
- * Copyright 2017-present Open Networking Foundation
-
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
-
- * http://www.apache.org/licenses/LICENSE-2.0
-
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-import './fine-grained.component.scss';
-import * as d3 from 'd3';
-import * as $ from 'jquery';
-import * as _ from 'lodash';
-import {Subscription} from 'rxjs';
-import {XosServiceGraphConfig as config} from '../../graph.config';
-import {IXosDebouncer} from '../../../core/services/helpers/debounce.helper';
-import {IXosServiceGraph, IXosServiceGraphLink, IXosServiceGraphNode} from '../../interfaces';
-import {IXosSidePanelService} from '../../../core/side-panel/side-panel.service';
-import {IXosGraphHelpers} from '../../services/d3-helpers/graph.helpers';
-import {IXosServiceGraphExtender, IXosServiceGraphReducer} from '../../services/graph.extender';
-import {IXosServiceInstanceGraphStore} from '../../services/service-instance.graph.store';
-import {IXosModeldefsCache} from '../../../datasources/helpers/modeldefs.service';
-
-class XosFineGrainedTenancyGraphCtrl {
-  static $inject = [
-    '$log',
-    'XosServiceInstanceGraphStore',
-    'XosDebouncer',
-    'XosModelDiscoverer',
-    'XosSidePanel',
-    'XosGraphHelpers',
-    'XosServiceGraphExtender'
-  ];
-
-  public graph: IXosServiceGraph;
-
-  private GraphSubscription: Subscription;
-  private svg;
-  private forceLayout;
-  private linkGroup;
-  private nodeGroup;
-  private defs;
-  private textSize = 20;
-  private textOffset = this.textSize / 4;
-
-  // debounced functions
-  private renderGraph;
-
-  constructor(
-    private $log: ng.ILogService,
-    private XosServiceInstanceGraphStore: IXosServiceInstanceGraphStore,
-    private XosDebouncer: IXosDebouncer,
-    private XosModeldefsCache: IXosModeldefsCache,
-    private XosSidePanel: IXosSidePanelService,
-    private XosGraphHelpers: IXosGraphHelpers,
-    private XosServiceGraphExtender: IXosServiceGraphExtender
-  ) {
-    this.handleSvg();
-    this.loadDefs();
-    this.setupForceLayout();
-    this.renderGraph = this.XosDebouncer.debounce(this._renderGraph, 1000, this);
-
-    $(window).on('resize', () => {
-      this.setupForceLayout();
-      this.renderGraph();
-    });
-
-    this.GraphSubscription = this.XosServiceInstanceGraphStore.get()
-      .subscribe(
-        (graph) => {
-          this.$log.debug(`[XosServiceInstanceGraphStore] Fine-Grained Event and render`, graph);
-
-          if (!graph || !graph.nodes || !graph.links) {
-            return;
-          }
-
-          _.forEach(this.XosServiceGraphExtender.getFinegrained(), (r: IXosServiceGraphReducer) => {
-            graph = r.reducer(graph);
-          });
-
-          this.graph = graph;
-          this.renderGraph();
-        },
-        (err) => {
-          this.$log.error(`[XosFineGrainedTenancyGraphCtrl] Error: `, err);
-        }
-      );
-  }
-
-  $onDestroy() {
-    this.GraphSubscription.unsubscribe();
-  }
-
-  private _renderGraph() {
-    if (!angular.isDefined(this.graph) || !angular.isDefined(this.graph.nodes) || !angular.isDefined(this.graph.links)) {
-      return;
-    }
-    this.addNodeLinksToForceLayout(this.graph);
-    this.renderNodes(this.graph.nodes);
-    this.renderLinks(this.graph.links);
-  }
-
-  private getSvgDimensions(): {width: number, height: number} {
-    return {
-      width: $('xos-fine-grained-tenancy-graph svg').width(),
-      height: $('xos-fine-grained-tenancy-graph svg').height()
-    };
-  }
-
-  private handleSvg() {
-    this.svg = d3.select('svg');
-
-    this.defs = this.svg.append('defs');
-
-    this.linkGroup = this.svg.append('g')
-      .attr({
-        class: 'link-group'
-      });
-
-    this.nodeGroup = this.svg.append('g')
-      .attr({
-        class: 'node-group'
-      });
-  }
-
-  private loadDefs() {
-      const cloud = {
-          vbox: '0 0 303.8 185.8',
-          path: `M88.6,44.3c31.7-45.5,102.1-66.7,135-3
-             M37.8,142.9c-22.5,3.5-60.3-32.4-16.3-64.2
-             M101.8,154.2c-15.6,59.7-121.4,18.8-77.3-13
-             M194.6,150c-35.4,51.8-85.7,34.3-98.8-9.5
-             M274.4,116.4c29.4,73.2-81.9,80.3-87.7,44.3
-             M28.5,89.2C3.7,77.4,55.5,4.8,95.3,36.1
-             M216.1,28.9C270.9-13,340.8,91,278.4,131.1`,
-          bgpath: `M22,78.3C21.5,55.1,62.3,10.2,95.2,36
-             h0c31.9-33.4,88.1-50.5,120.6-7.2l0.3,0.2
-             C270.9-13,340.8,91,278.4,131.1v-0.5
-             c10.5,59.8-86.4,63.7-91.8,30.1h-0.4
-             c-30.2,33.6-67.6,24-84.6-6v-0.4
-             c-15.6,59.7-121.4,18.8-77.3-13
-             l-0.2-.2c-20.2-7.9-38.6-36.5-2.8-62.3Z`
-      };
-
-      this.defs.append('symbol')
-          .attr({ id: 'cloud', viewBox: cloud.vbox })
-          .append('path').attr('d', cloud.path);
-
-      this.defs.append('symbol')
-          .attr({ id: 'cloud_bg', viewBox: cloud.vbox })
-          .append('path').attr('d', cloud.bgpath);
-  }
-
-  private setupForceLayout() {
-    this.$log.debug(`[XosFineGrainedTenancyGraphCtrl] Setup Force Layout`);
-    const tick = () => {
-      this.nodeGroup.selectAll('g.node')
-        .attr({
-          transform: d => `translate(${d.x}, ${d.y})`
-        });
-
-      this.linkGroup.selectAll('line')
-        .attr({
-          x1: l => l.source.x || 0,
-          y1: l => l.source.y || 0,
-          x2: l => l.target.x || 0,
-          y2: l => l.target.y || 0,
-        });
-    };
-    const getLinkStrenght = (l: IXosServiceGraphLink) => {
-      return 1;
-    };
-    const svgDim = this.getSvgDimensions();
-    this.forceLayout = d3.layout.force()
-      .size([svgDim.width, svgDim.height])
-      .linkDistance(config.force.linkDistance)
-      .linkStrength(l => getLinkStrenght(l))
-      .charge(config.force.charge)
-      .gravity(config.force.gravity)
-      .on('tick', tick);
-  }
-
-  private addNodeLinksToForceLayout(data: IXosServiceGraph) {
-    this.forceLayout
-      .nodes(data.nodes)
-      .links(data.links)
-      .start();
-  }
-
-  private renderServiceNodes(nodes: any) {
-
-    const self = this;
-
-    nodes.append('rect')
-    .attr({
-      rx: config.node.radius,
-      ry: config.node.radius
-    });
-
-    nodes.append('text')
-      .attr({
-        'text-anchor': 'middle',
-        'transform': `translate(0,${this.textOffset})`
-      })
-      .text(n => n.label);
-    // .text(n => `${n.id} - ${n.label}`);
-
-    const existing = nodes.selectAll('rect');
-
-    // resize node > rect as contained text
-    existing.each(function() {
-      const textBBox = self.XosGraphHelpers.getSiblingTextBBox(this);
-      const rect = d3.select(this);
-      rect.attr({
-        width: textBBox.width + config.node.padding,
-        height: textBBox.height + config.node.padding,
-        x: textBBox.x - (config.node.padding / 2),
-        y: (textBBox.y + self.textOffset) - (config.node.padding / 2)
-      });
-    });
-  }
-
-  private renderTenantNodes(nodes: any) {
-    nodes.append('rect')
-      .attr({
-        width: 40,
-        height: 40,
-        x: -20,
-        y: -20,
-        transform: `rotate(45)`
-      });
-
-    nodes.append('text')
-      .attr({
-        'text-anchor': 'middle',
-        'transform': `translate(0,${this.textOffset})`
-      })
-      .text(n => n.label);
-  }
-
-  private renderNetworkNodes(nodes: any) {
-    const self = this;
-
-    nodes.append('use')
-        .attr({
-            class: 'symbol-bg',
-            'xlink:href': '#cloud_bg'
-        });
-
-    nodes.append('use')
-        .attr({
-            class: 'symbol',
-            'xlink:href': '#cloud'
-        });
-
-    nodes.append('text')
-      .attr({
-          'text-anchor': 'middle',
-          'transform': `translate(0,${this.textOffset})`
-      })
-      .text(n => n.label);
-
-    const existing = nodes.selectAll('use');
-
-    // resize node > rect as contained text
-    existing.each(function() {
-      const textBBox = self.XosGraphHelpers.getSiblingTextBBox(this);
-      const useElem = d3.select(this);
-      const w = textBBox.width + config.node.padding * 2;
-      const h = w;
-      const xoff = -(w / 2);
-      const yoff = -(h / 2);
-
-      useElem.attr({
-          width: w,
-          height: h,
-          transform: 'translate(' + xoff + ',' + yoff + ')'
-      });
-    });
-  }
-
-  private renderSubscriberNodes(nodes: any) {
-    const self = this;
-    nodes.append('rect');
-
-    nodes.append('text')
-      .attr({
-        'text-anchor': 'middle',
-        'transform': `translate(0,${this.textOffset})`
-      })
-      .text(n => n.label);
-
-    const existing = nodes.selectAll('rect');
-
-    // resize node > rect as contained text
-    existing.each(function() {
-      const textBBox = self.XosGraphHelpers.getSiblingTextBBox(this);
-      const rect = d3.select(this);
-      rect.attr({
-        width: textBBox.width + config.node.padding,
-        height: textBBox.height + config.node.padding,
-        x: textBBox.x - (config.node.padding / 2),
-        y: (textBBox.y + self.textOffset) - (config.node.padding / 2)
-      });
-    });
-  }
-
-  private renderNodes(nodes: IXosServiceGraphNode[]) {
-    const node = this.nodeGroup
-      .selectAll('g.node')
-      .data(nodes, n => n.id);
-
-    let mouseEventsTimer, selectedModel;
-    const svgDim = this.getSvgDimensions();
-    const hStep = svgDim.width / (nodes.length - 1);
-    const vStep = svgDim.height / (nodes.length - 1);
-    const entering = node.enter()
-      .append('g')
-      .attr({
-        id: n => n.id,
-        class: n => `node ${n.type} ${this.XosGraphHelpers.parseElemClasses(n.d3Class)}`,
-        transform: (n, i) => `translate(${hStep * i}, ${vStep * i})`
-      })
-      .call(this.forceLayout.drag)
-      .on('mousedown', () => {
-        mouseEventsTimer = new Date().getTime();
-        d3.event.stopPropagation();
-      })
-      .on('mouseup', (n) => {
-        mouseEventsTimer = new Date().getTime() - mouseEventsTimer;
-        n.fixed = true;
-      })
-      .on('click', (n: IXosServiceGraphNode) => {
-        if (mouseEventsTimer > 100) {
-          // it is a drag
-          return;
-        }
-        if (selectedModel === n.id) {
-          // this model is already selected, so close the panel
-          this.XosSidePanel.removeInjectedComponents();
-          selectedModel = null;
-          return;
-        }
-        selectedModel = n.id;
-        const modelName = n.model['class_names'].split(',')[0];
-        const formConfig = this.XosModeldefsCache.get(modelName).formCfg;
-        const model = angular.copy(n.model);
-        delete model.d3Id;
-        this.XosSidePanel.injectComponent('xosForm', {config: formConfig, ngModel: model});
-      });
-
-    this.renderServiceNodes(entering.filter('.service'));
-    this.renderTenantNodes(entering.filter('.serviceinstance'));
-    this.renderNetworkNodes(entering.filter('.network'));
-    this.renderSubscriberNodes(entering.filter('.subscriber'));
-    // this.renderSubscriberNodes(entering.filter('.tenantroot'));
-  }
-
-  private renderLinks(links: IXosServiceGraphLink[]) {
-    const link = this.linkGroup
-      .selectAll('line')
-      .data(links, l => l.id);
-
-    const entering = link.enter();
-
-    entering.append('line')
-      .attr({
-        id: n => n.id,
-        class: n => `link ${this.XosGraphHelpers.parseElemClasses(n.d3Class)}`,
-        'marker-start': 'url(#arrow)'
-      });
-  }
-}
-
-export const XosFineGrainedTenancyGraph: angular.IComponentOptions = {
-  template: require('./fine-grained.component.html'),
-  controllerAs: 'vm',
-  controller: XosFineGrainedTenancyGraphCtrl,
-};
diff --git a/src/app/service-graph/components/coarse/coarse.component.html b/src/app/service-graph/components/graph/graph.component.html
similarity index 69%
rename from src/app/service-graph/components/coarse/coarse.component.html
rename to src/app/service-graph/components/graph/graph.component.html
index c0bb4e5..6510a9f 100644
--- a/src/app/service-graph/components/coarse/coarse.component.html
+++ b/src/app/service-graph/components/graph/graph.component.html
@@ -1,4 +1,3 @@
-
 <!--
 Copyright 2017-present Open Networking Foundation
 
@@ -15,8 +14,12 @@
 limitations under the License.
 -->
 
+<h1>Service Graph</h1>
 
-<h1>Service Dependency Graph</h1>
-
-<svg>
-</svg>
+<div class="graph-container">
+  <div class="loader-container" ng-if="vm.loader">
+    <div class="loader"></div>
+  </div>
+  <a ng-click="vm.closeFullscreen()" class="close-btn"><i class="fa fa-times"></i></a>
+  <svg></svg>
+</div>
diff --git a/src/app/service-graph/components/graph/graph.component.scss b/src/app/service-graph/components/graph/graph.component.scss
new file mode 100644
index 0000000..40b6f11
--- /dev/null
+++ b/src/app/service-graph/components/graph/graph.component.scss
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+@import './../../../style/vars.scss';
+@import '../../../../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/variables';
+
+$svg-size: 600px;
+
+xos-service-graph {
+  display: block;
+  // background: $color-accent;
+
+  .graph-container {
+    position: relative;
+
+    .loader-container {
+      position: absolute;
+      left: 0;
+      top: 0;
+      height: $svg-size;
+      width: 100%;
+    }
+  }
+
+  svg {
+    height: $svg-size;
+    width: 100%;
+    background-color: $panel-filled-bg;
+    border-radius: 3px;
+  }
+
+  .close-btn {
+    display: none;
+  }
+
+  .fullscreen {
+    svg {
+      z-index: 1040;
+      width: 100%;
+      height: 100%;
+      position: fixed;
+      top: 0;
+      left: 0;
+      background-color: $background-color;
+      transition: all .5s;
+    }
+
+    .close-btn {
+      cursor: pointer;
+      display: block;
+      position: fixed;
+      top: 10px;
+      right: 10px;
+      z-index: 1041;
+    }
+  }
+
+  .node-group {
+
+    .node {
+      cursor: pointer;
+    }
+
+    .node.service {
+      > rect {
+        stroke: $color-accent;
+        fill: $background-color;
+      }
+      > .icon {
+        fill: $color-accent;
+      }
+    }
+
+    .node.serviceinstance {
+      > rect {
+        stroke: green;
+        fill: $background-color;
+      }
+      > .icon {
+        fill: green;
+      }
+    }
+
+    .node {
+      >.label {
+        >text {
+          fill: #fff;
+        }
+        >rect {
+          fill: $background-color;
+          stroke: #fff;
+        }
+      }
+    }
+  }
+
+  .link-group {
+    line {
+      stroke: $color-accent;
+    }
+
+    line.ownership {
+      stroke: green;
+      stroke-dasharray: 5;
+    }
+
+    line.serviceinstancelink {
+      stroke: green;
+    }
+  }
+  .arrow-marker {
+    stroke: $color-accent;
+    fill: $color-accent;
+  }
+}
\ No newline at end of file
diff --git a/src/app/service-graph/components/graph/graph.component.ts b/src/app/service-graph/components/graph/graph.component.ts
new file mode 100644
index 0000000..5769b6f
--- /dev/null
+++ b/src/app/service-graph/components/graph/graph.component.ts
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import './graph.component.scss';
+
+import * as d3 from 'd3';
+import * as $ from 'jquery';
+
+import {IXosGraphStore} from '../../services/graph.store';
+import {Subscription} from 'rxjs/Subscription';
+import {XosServiceGraphConfig as config} from '../../graph.config';
+import {IXosGraphHelpers} from '../../services/d3-helpers/graph-elements.helpers';
+import {IXosServiceGraphIcons} from '../../services/d3-helpers/graph-icons.service';
+import {IXosNodePositioner} from '../../services/node-positioner.service';
+import {IXosNodeRenderer} from '../../services/renderer/node.renderer';
+import {IXosSgNode} from '../../interfaces';
+import {IXosGraphConfig} from '../../services/graph.config';
+
+class XosServiceGraphCtrl {
+  static $inject = [
+    '$log',
+    '$scope',
+    'XosGraphStore',
+    'XosGraphHelpers',
+    'XosServiceGraphIcons',
+    'XosNodePositioner',
+    'XosNodeRenderer',
+    'XosGraphConfig'
+  ];
+
+  public loader: boolean = true;
+
+  private GraphSubscription: Subscription;
+  private graph: any; // this is the Graph instance
+
+  // graph element
+  private svg;
+  private linkGroup;
+  private nodeGroup;
+  private forceLayout;
+
+  constructor (
+    private $log: ng.ILogService,
+    private $scope: ng.IScope,
+    private XosGraphStore: IXosGraphStore,
+    private XosGraphHelpers: IXosGraphHelpers,
+    private XosServiceGraphIcons: IXosServiceGraphIcons,
+    private XosNodePositioner: IXosNodePositioner,
+    private XosNodeRenderer: IXosNodeRenderer,
+    private XosGraphConfig: IXosGraphConfig
+  ) {
+    this.$log.info('[XosServiceGraph] Component setup');
+
+    this.XosGraphConfig.setupKeyboardShortcuts();
+
+    this.setupSvg();
+    this.setupForceLayout();
+
+    this.GraphSubscription = this.XosGraphStore.get()
+      .subscribe(
+        graph => {
+          this.graph = graph;
+          if (this.graph.nodes().length > 0) {
+            this.loader = false;
+            this.renderGraph(this.graph);
+          }
+        },
+        error => {
+          this.$log.error('[XosServiceGraph] XosGraphStore observable error: ', error);
+        }
+      );
+
+    this.$scope.$on('xos.sg.update', () => {
+      this.$log.info(`[XosServiceGraph] Received event: xos.sg.update`);
+      this.renderGraph(this.graph);
+    });
+  }
+
+  $onDestroy() {
+    this.GraphSubscription.unsubscribe();
+  }
+
+  public closeFullscreen() {
+    this.XosGraphConfig.toggleFullscreen();
+  }
+
+  private setupSvg() {
+    this.svg = d3.select('xos-service-graph svg');
+
+    this.linkGroup = this.svg.append('g')
+      .attr({
+        class: 'link-group'
+      });
+
+    this.nodeGroup = this.svg.append('g')
+      .attr({
+        class: 'node-group'
+      });
+  }
+
+  private setupForceLayout() {
+    this.$log.debug(`[XosServiceGraph] Setup Force Layout`);
+    const tick = () => {
+      this.nodeGroup.selectAll('g.node')
+        .attr({
+          transform: d => `translate(${d.x}, ${d.y})`
+        });
+
+      this.linkGroup.selectAll('line')
+        .attr({
+          x1: l => l.source.x || 0,
+          y1: l => l.source.y || 0,
+          x2: l => l.target.x || 0,
+          y2: l => l.target.y || 0,
+        });
+    };
+
+    const svgDim = this.getSvgDimensions();
+
+    this.forceLayout =
+      d3.layout.force()
+      .size([svgDim.width, svgDim.height])
+      .on('tick', tick);
+  }
+
+  private getSvgDimensions(): {width: number, height: number} {
+    return {
+      width: $('xos-service-graph svg').width(),
+      height: $('xos-service-graph svg').height()
+    };
+  }
+
+  private renderGraph(graph: any) {
+    let nodes: IXosSgNode[] = this.XosGraphStore.nodesFromGraph(graph);
+    let links = this.XosGraphStore.linksFromGraph(graph);
+    const svgDim = this.getSvgDimensions();
+
+    this.XosNodePositioner.positionNodes(svgDim, nodes)
+      .then((nodes: IXosSgNode[]) => {
+
+        this.forceLayout
+          .nodes(nodes)
+          .links(links)
+          .size([svgDim.width, svgDim.height])
+          .linkDistance(config.force.linkDistance)
+          .charge(config.force.charge)
+          .gravity(config.force.gravity)
+          .start();
+
+        // render nodes
+        this.XosNodeRenderer.renderNodes(this.forceLayout, this.nodeGroup, nodes);
+        this.renderLinks(links);
+      });
+  }
+
+  private renderLinks(links: any[]) {
+
+    const link = this.linkGroup
+      .selectAll('line')
+      .data(links, l => l.id);
+
+    const entering = link.enter();
+
+    entering.append('line')
+      .attr({
+        id: n => n.id,
+        class: n => n.type
+      });
+
+    link.exit().remove();
+  }
+
+}
+
+export const XosServiceGraph: angular.IComponentOptions = {
+  template: require('./graph.component.html'),
+  controllerAs: 'vm',
+  controller: XosServiceGraphCtrl,
+};
diff --git a/src/app/service-graph/graph.config.ts b/src/app/service-graph/graph.config.ts
index abfd415..13a8939 100644
--- a/src/app/service-graph/graph.config.ts
+++ b/src/app/service-graph/graph.config.ts
@@ -27,6 +27,7 @@
 }
 
 export interface IXosServiceGraphConfig {
+  duration: number;
   force: {
     linkDistance: number;
     charge: number;
@@ -35,11 +36,13 @@
   node: {
     padding: number;
     radius: number;
+    text: number;
   };
   markers: ISvgMarker[];
 }
 
 export const XosServiceGraphConfig: IXosServiceGraphConfig = {
+  duration: 750,
   force: {
     linkDistance: 80,
     charge: -60,
@@ -47,7 +50,8 @@
   },
   node: {
     padding: 10,
-    radius: 2
+    radius: 2,
+    text: 14
   },
   markers: [
     {
diff --git a/src/app/service-graph/index.ts b/src/app/service-graph/index.ts
index 2acb259..770862d 100644
--- a/src/app/service-graph/index.ts
+++ b/src/app/service-graph/index.ts
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2017-present Open Networking Foundation
 
@@ -15,32 +14,30 @@
  * limitations under the License.
  */
 
-
-import {xosDataSources} from '../datasources/index';
-import {XosServiceGraphStore} from './services/service-graph.store';
-import {xosCore} from '../core/index';
-import {XosCoarseTenancyGraph} from './components/coarse/coarse.component';
-import {XosFineGrainedTenancyGraph} from './components/fine-grained/fine-grained.component';
 import {XosServiceGraphExtender, IXosServiceGraphExtender} from './services/graph.extender';
-import {XosGraphHelpers} from './services/d3-helpers/graph.helpers';
-import {XosServiceInstanceGraphStore} from './services/service-instance.graph.store';
+import {XosGraphHelpers} from './services/d3-helpers/graph-elements.helpers';
+import {XosServiceGraph} from './components/graph/graph.component';
+import {XosGraphStore} from './services/graph.store';
+import {XosServiceGraphIcons} from './services/d3-helpers/graph-icons.service';
+import {XosNodePositioner} from './services/node-positioner.service';
+import {XosGraphConfig} from './services/graph.config';
+import {XosNodeRenderer} from './services/renderer/node.renderer';
+
 export const xosServiceGraph = 'xosServiceGraph';
 
 angular
-  .module(xosServiceGraph, [xosDataSources, xosCore])
-  .service('XosServiceGraphStore', XosServiceGraphStore)
-  .service('XosServiceInstanceGraphStore', XosServiceInstanceGraphStore)
+  .module(xosServiceGraph, [])
   .service('XosServiceGraphExtender', XosServiceGraphExtender)
   .service('XosGraphHelpers', XosGraphHelpers)
-  .component('xosCoarseTenancyGraph', XosCoarseTenancyGraph)
-  .component('xosFineGrainedTenancyGraph', XosFineGrainedTenancyGraph)
-  .config(($stateProvider) => {
-    $stateProvider
-      .state('xos.fine-grained-graph', {
-        url: 'tenancy-graph',
-        component: 'xosFineGrainedTenancyGraph',
-      });
-  })
-  .run(($log: ng.ILogService, XosServiceGraphExtender: IXosServiceGraphExtender) => {
+  .service('XosGraphStore', XosGraphStore)
+  .service('XosServiceGraphIcons', XosServiceGraphIcons)
+  .service('XosNodePositioner', XosNodePositioner)
+  .service('XosGraphConfig', XosGraphConfig)
+  .service('XosNodeRenderer', XosNodeRenderer)
+  .component('xosServiceGraph', XosServiceGraph)
+  .run((
+    $log: ng.ILogService,
+    XosServiceGraphExtender: IXosServiceGraphExtender
+  ) => {
     $log.info(`[${xosServiceGraph}] Module Setup`);
   });
diff --git a/src/app/service-graph/interfaces.ts b/src/app/service-graph/interfaces.ts
index af55c54..861ba75 100644
--- a/src/app/service-graph/interfaces.ts
+++ b/src/app/service-graph/interfaces.ts
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2017-present Open Networking Foundation
 
@@ -15,95 +14,42 @@
  * limitations under the License.
  */
 
-
 interface Id3Element {
-  d3Class?: string;
-  d3Id?: string;
-}
-
-export interface IXosServiceModel {
-  id: number;
-  d3Id?: string;
-  backend_status: string;
-  kind: string;
-  name: string;
-  class_names: string;
-  service_specific_attributes: string; // this is json stringified
-}
-
-export interface IXosTenantModel extends Id3Element {
-  id: number;
-  d3Id?: string;
-  backend_status: string;
-  kind: string;
-
-  // source
-  provider_service_id: number;
-
-  // destination
-  subscriber_service_id: number;
-  subscriber_tenant_id: number;
-  subscriber_root_id: number;
-  subscriber_network_id: number;
-
-  subscriber_user_id: number;
-
-  // extra informations
-  service_specific_id: string;
-  service_specific_attribute: string;
-  connect_method: string;
-
-  // reverse of subscriber tenants
-  subscribed_tenants_ids: number[];
-}
-
-export interface IXosCoarseGraphData {
-  services: IXosServiceModel[];
-  servicedependencies: any[];
-}
-
-// TODO outdated, remove
-export interface IXosFineGrainedGraphData extends IXosCoarseGraphData {
-  tenants: IXosTenantModel[];
-  subscribers: any[];
-  networks: any[];
-}
-
-export interface IXosServiceInstanceGraphData {
-  serviceGraph: IXosServiceGraph;
-  serviceInstances: any[];
-  serviceInstanceLinks: any[];
-  networks: any[];
-}
-
-export interface IXosServiceGraphNodeBadge {
-  type: 'info'|'success'|'warning'|'danger';
-  text: string;
-}
-
-export interface IXosServiceGraphNode extends Id3Element {
-  id: number | string;
-  label: string;
-  x?: number;
-  y?: number;
-  px?: number;
-  py?: number;
-  width?: number;
-  height?: number;
+  x: number;
+  y: number;
   fixed?: boolean;
-  badge?: IXosServiceGraphNodeBadge; // TODO implement badges
-  model: IXosServiceModel;
-  type: 'service' | 'tenant' | 'network' | 'subscriber';
 }
 
-export interface IXosServiceGraphLink extends Id3Element {
+export interface IXosSgNode extends Id3Element {
   id: string;
+  data: any; // this can be a Service, ServiceInstance or Instance
+
+  // do we need those?
+  type: string;
+  d3Class?: string;
+}
+
+export interface IXosSgLink {
+  id: string;
+  type: string;
   source: number;
   target: number;
-  model: IXosTenantModel;
+  data: any; // this can be a ServiceDependency, ServiceInstanceLink or a representation of ServiceInstance.owner_id
 }
 
-export interface IXosServiceGraph {
-  nodes: IXosServiceGraphNode[];
-  links: IXosServiceGraphLink[];
+export interface IXosSgConfig {
+  labels: boolean;
+}
+
+export interface IXosBaseModel {
+  id: number;
+  class_names: string;
+  name?: string;
+  [x: string]: any; // allow extra properties
+}
+
+export interface IXosOwnershipLink {
+  service: number;
+  service_instance: number;
+  type: 'ownership';
 }
diff --git a/src/app/service-graph/services/d3-helpers/graph-elements.helpers.ts b/src/app/service-graph/services/d3-helpers/graph-elements.helpers.ts
new file mode 100644
index 0000000..bbacbdc
--- /dev/null
+++ b/src/app/service-graph/services/d3-helpers/graph-elements.helpers.ts
@@ -0,0 +1,67 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+import * as d3 from 'd3';
+import {XosServiceGraphConfig as config} from '../../graph.config';
+
+export interface Id3BBox {
+  x?: number;
+  y?: number;
+  width: number;
+  height: number;
+}
+
+export interface IXosGraphHelpers {
+  parseElemClasses (classes: string): string;
+  getBBox(context: any): Id3BBox;
+  getSiblingTextBBox(contex: any /* D3 this */): Id3BBox;
+  getSiblingIconBBox(contex: any /* D3 this */): Id3BBox;
+  getSiblingBBox(contex: any): Id3BBox;
+}
+
+export class XosGraphHelpers implements IXosGraphHelpers {
+  public parseElemClasses (classes: string): string {
+    return angular.isDefined(classes) ? classes.split(' ')
+      .map(c => `ext-${c}`)
+      .join(' ') : '';
+  }
+
+  public getBBox(context: any): Id3BBox {
+    return d3.select(context).node().getBBox();
+  }
+
+  public getSiblingTextBBox(contex: any): Id3BBox {
+    const text: d3.Selection<any> = d3.select(contex.parentNode).select('text');
+    return text.empty() ? {width: 0, height: 0} : text.node().getBBox();
+  }
+
+  public getSiblingIconBBox(contex: any): Id3BBox {
+    return d3.select(contex.parentNode).select('path').node().getBBox();
+  }
+
+  public getSiblingBBox(contex: any): Id3BBox {
+    // NOTE consider that inside a node we can have 1 text and 1 icon
+    const textBBox: Id3BBox = this.getSiblingTextBBox(contex);
+    const iconBBox: Id3BBox = this.getSiblingIconBBox(contex);
+
+    return {
+      width: iconBBox.width + (textBBox.width ? config.node.padding + textBBox.width : 0),
+      height: iconBBox.height
+    };
+  }
+}
diff --git a/src/app/service-graph/services/d3-helpers/graph-icons.service.ts b/src/app/service-graph/services/d3-helpers/graph-icons.service.ts
new file mode 100644
index 0000000..9baeb57
--- /dev/null
+++ b/src/app/service-graph/services/d3-helpers/graph-icons.service.ts
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as _ from 'lodash';
+
+export interface IXosServiceGraphIcon {
+  name: string;
+  path: string;
+  transform: string; // do we need it??
+}
+
+const icons: IXosServiceGraphIcon[] = [
+  {
+    name: 'service',
+    path: 'M.08,15.51V10.7h0L1.5,10.5,3,10.29s0,0,.06,0A10.13,10.13,0,0,1,3.9,8.05a.11.11,0,0,0,0-.12L2.24,5.66s0-.08,0-.12Q3.91,3.91,5.56,2.26a.08.08,0,0,1,.1,0L7.87,3.83a.21.21,0,0,0,.25,0A10,10,0,0,1,10.17,3s0,0,.05-.05l.09-.56c.11-.65.22-1.29.32-1.94a2.57,2.57,0,0,0,.06-.4h4.78a.11.11,0,0,0,0,.08c.15.92.31,1.84.46,2.76a.07.07,0,0,0,.06.06,11.76,11.76,0,0,1,2.11.88.09.09,0,0,0,.12,0l2.34-1.62a.05.05,0,0,1,.08,0l3.28,3.36a.05.05,0,0,1,0,.08L23.66,6c-.47.63-.93,1.27-1.39,1.91,0,0,0,.06,0,.1l.14.25a9.19,9.19,0,0,1,.74,1.88s0,.06.06.06l.8.13,1.76.3a1.17,1.17,0,0,0,.32,0v4.81H26l-1.41.22-1.26.19c-.14,0-.14,0-.18.15v0a10.33,10.33,0,0,1-.84,2,.1.1,0,0,0,0,.12L24,20.56c0,.05,0,.07,0,.11-1.09,1.1-2.18,2.19-3.26,3.29,0,0-.06,0-.09,0L18.3,22.31a.11.11,0,0,0-.16,0,11,11,0,0,1-2.07.86.11.11,0,0,0-.08.09c-.05.33-.11.66-.16,1-.1.59-.2,1.18-.29,1.77,0,.06,0,.07-.09.07H10.83c-.07,0-.09,0-.1-.08-.15-.91-.31-1.82-.46-2.72,0,0,0-.05,0-.06s-.2,0-.3-.09a10.59,10.59,0,0,1-1.87-.79.09.09,0,0,0-.11,0L5.62,24s-.08,0-.12,0L2.24,20.72s0,0,0-.1c.55-.78,1.1-1.56,1.66-2.33a.11.11,0,0,0,0-.12A9.87,9.87,0,0,1,3,16S3,16,3,16l-.72-.12-1.76-.3C.35,15.55.22,15.52.08,15.51Zm13,2.61a5,5,0,1,0-5-5A5,5,0,0,0,13.08,18.12Z',
+    transform: 'translate(-0.08 -0.04)'
+  },
+  {
+    name: 'serviceinstance',
+    path: 'M11.87,19.94v5.47c0,.61-.18.72-.69.42l-9.6-5.55a.62.62,0,0,1-.32-.64c0-3.64,0-7.29,0-10.94,0-.7.16-.79.79-.42,2.89,1.67,5.77,3.39,8.69,5a1.81,1.81,0,0,1,1.14,2C11.8,16.81,11.87,18.38,11.87,19.94Z\n' +
+    'M13,0a1,1,0,0,1,.53.2l9.4,5.45c.54.32.54.45,0,.78C19.78,8.2,16.7,10,13.63,11.74a1.12,1.12,0,0,1-1.24,0C9.28,9.92,6.15,8.12,3,6.31c-.55-.31-.55-.46,0-.78L12.45.19A.86.86,0,0,1,13,0Z\n' +
+    'M24.73,14.16c0,1.81,0,3.61,0,5.42a.8.8,0,0,1-.46.79l-9.36,5.41c-.64.38-.79.29-.79-.47,0-3.53,0-7.07,0-10.6a.92.92,0,0,1,.52-.94q4.63-2.65,9.26-5.35l.24-.14c.43-.2.58-.11.58.36Z',
+    transform: ''
+  }
+];
+
+export interface IXosServiceGraphIcons {
+  get(icon: string): IXosServiceGraphIcon;
+}
+
+export class XosServiceGraphIcons implements IXosServiceGraphIcons {
+  public get(icon: string): IXosServiceGraphIcon {
+    return _.find(icons, {name: icon});
+  }
+}
diff --git a/src/app/service-graph/services/d3-helpers/graph.helpers.ts b/src/app/service-graph/services/d3-helpers/graph.helpers.ts
deleted file mode 100644
index 15988cd..0000000
--- a/src/app/service-graph/services/d3-helpers/graph.helpers.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-
-/*
- * Copyright 2017-present Open Networking Foundation
-
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
-
- * http://www.apache.org/licenses/LICENSE-2.0
-
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-import * as d3 from 'd3';
-
-export interface Id3BBox {
-  x: number;
-  y: number;
-  width: number;
-  height: number;
-}
-
-export interface IXosGraphHelpers {
-  parseElemClasses (classes: string): string;
-  getSiblingTextBBox(contex: any /* D3 this */): Id3BBox;
-}
-
-export class XosGraphHelpers implements IXosGraphHelpers {
-  public parseElemClasses (classes: string): string {
-    return classes ? classes.split(' ')
-      .map(c => `ext-${c}`)
-      .join(' ') : '';
-  }
-
-  public getSiblingTextBBox(contex: any): Id3BBox {
-    return d3.select(contex.parentNode).select('text').node().getBBox();
-  }
-}
diff --git a/src/app/service-graph/services/graph.config.ts b/src/app/service-graph/services/graph.config.ts
new file mode 100644
index 0000000..6c58c3a
--- /dev/null
+++ b/src/app/service-graph/services/graph.config.ts
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as $ from 'jquery';
+import {IXosKeyboardShortcutService} from '../../core/services/keyboard-shortcut';
+import {IXosSgConfig} from '../interfaces';
+import {IXosGraphStore} from './graph.store';
+
+export interface IXosGraphConfig {
+  getUserConfig(): IXosSgConfig;
+  setupKeyboardShortcuts(): void;
+  toggleFullscreen(): void;
+}
+
+export class XosGraphConfig {
+
+  static $inject = [
+    '$log',
+    '$cookies',
+    '$rootScope',
+    '$timeout',
+    'XosKeyboardShortcut',
+    'XosGraphStore'
+  ];
+
+  private defaultUserConfig: IXosSgConfig = {
+    labels: false
+  };
+
+  private userConfig: IXosSgConfig = this.defaultUserConfig;
+
+  constructor (
+    private $log: ng.ILogService,
+    private $cookies: ng.cookies.ICookiesService,
+    private $rootScope: ng.IRootScopeService,
+    private $timeout: ng.ITimeoutService,
+    private XosKeyboardShortcut: IXosKeyboardShortcutService,
+    private XosGraphStore: IXosGraphStore
+  ) {
+    this.userConfig = this.getUserConfig();
+
+
+  }
+
+  public setupKeyboardShortcuts() {
+
+    this.$log.info(`[XosGraphConfig] Setting up keyboard shortcuts`);
+
+    // Setup keyboard shortcuts
+    this.XosKeyboardShortcut.registerKeyBinding({
+      key: 'f',
+      modifiers: ['shift'],
+      cb: () => {
+        this.toggleFullscreen();
+      },
+      label: 'f',
+      description: 'Toggle graph fullscreen'
+    });
+
+    this.XosKeyboardShortcut.registerKeyBinding({
+      key: 's',
+      modifiers: ['shift'],
+      cb: () => {
+        // NOTE anytime the graph change the observable is updated,
+        // no need to manually retrigger here
+        this.XosGraphStore.toggleServiceInstances();
+      },
+      label: 's',
+      description: 'Toggle ServiceInstances'
+    });
+  }
+
+  public toggleFullscreen() {
+    $('.graph-container').toggleClass('fullscreen');
+    this.$timeout(() => {
+      // NOTE wait for the CSS transition to complete before repositioning
+      this.$rootScope.$broadcast('xos.sg.update');
+    }, 500);
+  }
+
+  public getUserConfig(): IXosSgConfig {
+    let config = this.$cookies.get('xos-service-graph-user-config');
+    if (!config || config.length === 0) {
+      this.$cookies.put('xos-service-graph-user-config', JSON.stringify(this.defaultUserConfig));
+      config = this.$cookies.get('xos-service-graph-user-config');
+    }
+    return JSON.parse(config);
+  }
+}
diff --git a/src/app/service-graph/services/graph.extender.ts b/src/app/service-graph/services/graph.extender.ts
index a601e87..467e34b 100644
--- a/src/app/service-graph/services/graph.extender.ts
+++ b/src/app/service-graph/services/graph.extender.ts
@@ -16,7 +16,9 @@
  */
 
 
-import {IXosServiceGraph} from '../interfaces';
+
+
+import {Graph} from 'graphlib';
 
 export interface IXosServiceGraphReducers {
   coarse: IXosServiceGraphReducer[];
@@ -29,7 +31,7 @@
 }
 
 export interface IXosServiceGraphReducerFn {
-  (graph: IXosServiceGraph): IXosServiceGraph;
+  (graph: Graph): Graph;
 }
 
 export interface IXosServiceGraphExtender {
diff --git a/src/app/service-graph/services/graph.store.spec.ts b/src/app/service-graph/services/graph.store.spec.ts
new file mode 100644
index 0000000..a41809c
--- /dev/null
+++ b/src/app/service-graph/services/graph.store.spec.ts
@@ -0,0 +1,334 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as _ from 'lodash';
+import * as angular from 'angular';
+import 'angular-mocks';
+import {IXosGraphStore, XosGraphStore} from './graph.store';
+import {Subject} from 'rxjs/Subject';
+import {Graph} from 'graphlib';
+import {XosDebouncer} from '../../core/services/helpers/debounce.helper';
+
+interface ITestXosGraphStore extends IXosGraphStore {
+
+  // state
+  serviceGraph: Graph;
+  serviceInstanceShown: boolean;
+
+  // private methods
+  getNodeId: any;
+  getModelType: any;
+  addNode: any;
+  addEdge: any;
+  nodesFromGraph: any;
+  toggleServiceInstances: any;
+
+  // observables
+  ServiceInstanceSubscription: any;
+  ServiceInstanceLinkSubscription: any;
+}
+
+let service: ITestXosGraphStore;
+
+let scope: ng.IRootScopeService;
+
+const services = [
+  {
+    id: 1,
+    class_names: 'Service,XOSBase',
+    name: 'Service 1'
+  },
+  {
+    id: 2,
+    class_names: 'Service,XOSBase',
+    name: 'Service 2'
+  }
+];
+
+const servicedependencies = [
+  {
+    id: 1,
+    class_names: 'ServiceDependency,XOSBase',
+    provider_service_id: 2,
+    subscriber_service_id: 1
+  }
+];
+
+const serviceInstances = [
+  {
+    id: 1,
+    class_names: 'ServiceInstance,XOSBase',
+    name: 'ServiceInstance 1',
+    owner_id: 1
+  },
+  {
+    id: 2,
+    class_names: 'ServiceInstance,XOSBase',
+    name: 'ServiceInstance 2',
+    owner_id: 2
+  }
+];
+
+const serviceInstanceLinks = [
+  {
+    id: 1,
+    class_names: 'ServiceInstanceLink,XOSBase',
+    provider_service_instance_id: 1,
+    subscriber_service_instance_id: 2,
+  }
+];
+
+const subject_services = new Subject();
+const subject_servicedependency = new Subject();
+const subject_serviceinstances = new Subject();
+const subject_serviceinstancelinks = new Subject();
+
+let MockModelStore = {
+  query: jasmine.createSpy('XosModelStore.query')
+    .and.callFake((model) => {
+      if (model === 'Service') {
+        return subject_services.asObservable();
+      }
+      else if (model === 'ServiceDependency') {
+        return subject_servicedependency.asObservable();
+      }
+      else if (model === 'ServiceInstance') {
+        return subject_serviceinstances.asObservable();
+      }
+      else if (model === 'ServiceInstanceLink') {
+        return subject_serviceinstancelinks.asObservable();
+      }
+    })
+};
+
+
+describe('The XosGraphStore service', () => {
+
+  beforeEach(() => {
+    angular.module('XosGraphStore', [])
+      .service('XosGraphStore', XosGraphStore)
+      .value('XosModelStore', MockModelStore)
+      .service('XosDebouncer', XosDebouncer);
+
+    angular.mock.module('XosGraphStore');
+  });
+
+  beforeEach(angular.mock.inject((XosGraphStore: ITestXosGraphStore,
+                                  $rootScope: ng.IRootScopeService,
+                                  _$q_: ng.IQService) => {
+
+    service = XosGraphStore;
+    scope = $rootScope;
+
+  }));
+
+  it('should load services and service-dependency and add nodes to the graph', (done) => {
+    let event = 0;
+    service.get().subscribe(
+      (graph: Graph) => {
+        if (event === 1) {
+          expect(graph.nodes().length).toBe(services.length);
+          expect(graph.nodes()).toEqual(['service~1', 'service~2']);
+          expect(graph.edges().length).toBe(servicedependencies.length);
+          expect(graph.edges()).toEqual([{v: 'service~1', w: 'service~2'}]);
+          done();
+        }
+        else {
+          event = event + 1;
+        }
+      }
+    );
+    subject_services.next(services);
+    subject_servicedependency.next(servicedependencies);
+    scope.$apply();
+  });
+
+  describe(`the getModelType`, () => {
+    it('should return the node type', () => {
+      const res = service.getModelType(services[0]);
+      expect(res).toBe('service');
+    });
+
+    it('should return the node type', () => {
+      const res = service.getModelType(serviceInstances[0]);
+      expect(res).toBe('serviceinstance');
+    });
+  });
+
+  describe('the getNodeId method', () => {
+    it('should return the id for a Service', () => {
+      const res = service.getNodeId(services[0]);
+      expect(res).toBe(`service~1`);
+    });
+
+    it('should return the id for a ServiceInstance', () => {
+      const res = service.getNodeId(serviceInstances[0]);
+      expect(res).toBe(`serviceinstance~1`);
+    });
+  });
+
+  describe('the addNode method', () => {
+
+    beforeEach(() => {
+      spyOn(service.serviceGraph, 'setNode');
+      spyOn(service.serviceGraph, 'setEdge');
+    });
+
+    it(`should a service to the graph`, () => {
+      service.addNode(services[0]);
+      expect(service.serviceGraph.setNode).toHaveBeenCalledWith('service~1', services[0]);
+    });
+
+    it('should add a service instance to the graph', () => {
+      service.addNode(serviceInstances[0]);
+      expect(service.serviceGraph.setNode).toHaveBeenCalledWith('serviceinstance~1', serviceInstances[0]);
+    });
+
+    it('should add an "ownership" edge to the graph', () => {
+      service.addNode(serviceInstances[0]);
+      expect(service.serviceGraph.setEdge).toHaveBeenCalledWith('serviceinstance~1', 'service~1', {service: 1, service_instance: 1, type: 'ownership'});
+    });
+  });
+
+  describe('the addEdge method', () => {
+
+    beforeEach(() => {
+      spyOn(service.serviceGraph, 'setEdge');
+    });
+
+    it('should add a ServiceDependency to the graph', () => {
+      service.addEdge(servicedependencies[0]);
+      expect(service.serviceGraph.setEdge).toHaveBeenCalledWith('service~1', 'service~2', servicedependencies[0]);
+    });
+
+    it('should add a ServiceInstanceLink to the graph', () => {
+      service.addEdge(serviceInstanceLinks[0]);
+      expect(service.serviceGraph.setEdge).toHaveBeenCalledWith('serviceinstance~1', 'serviceinstance~2', serviceInstanceLinks[0]);
+    });
+  });
+
+  describe('the nodesFromGraph and linksFromGraph methods', () => {
+    let graph: Graph;
+
+    beforeEach(() => {
+      graph = new Graph();
+      services.forEach(s => {
+        graph.setNode(`service~${s.id}`, s);
+      });
+
+      servicedependencies.forEach(sd => {
+        graph.setEdge('service~1', 'service~2', sd);
+      });
+    });
+
+    it('should add id and type to the nodes', () => {
+      const nodes = service.nodesFromGraph(graph);
+      expect(nodes[0].id).toBe('service~1');
+      expect(nodes[0].type).toBe('service');
+      expect(nodes[0].data).toBeDefined();
+    });
+
+    it('should add id and type to the links', () => {
+      const links = service.linksFromGraph(graph);
+      expect(links[0].id).toBe('service~1-service~2');
+      expect(links[0].type).toBe('servicedependency');
+      expect(links[0].data).toBeDefined();
+    });
+
+    it('should handle ownership links', () => {
+      graph.setNode(`serviceinstance~1`, serviceInstances[0]);
+      graph.setEdge('service~1', 'serviceinstance~1', {type: 'ownership', service: 1, service_instance: 1});
+      const links = service.linksFromGraph(graph);
+      expect(links[1].source).toBe(0);
+      expect(links[1].target).toBe(2);
+    });
+
+    it('should handle serviceinstancelink links', () => {
+      graph.setNode(`serviceinstance~1`, serviceInstances[0]);
+      graph.setNode(`serviceinstance~2`, serviceInstances[1]);
+      graph.setEdge('serviceinstance~1', 'serviceinstance~2', serviceInstanceLinks[0]);
+      const links = service.linksFromGraph(graph);
+      const targetLink = _.find(links, {id: `serviceinstance~1-serviceinstance~2`});
+      expect(targetLink).toBeDefined();
+      expect(targetLink.source).toBe(3);
+      expect(targetLink.target).toBe(2);
+    });
+  });
+
+  describe(`the toggleServiceInstances method`, () => {
+    describe('when they are disabled', () => {
+
+      beforeEach(() => {
+        MockModelStore.query.calls.reset();
+      });
+
+      it('should fetch them', () => {
+        service.toggleServiceInstances();
+        expect(service.serviceInstanceShown).toBeTruthy();
+        expect(MockModelStore.query).toHaveBeenCalledWith(`ServiceInstance`, '/core/serviceinstances');
+        expect(MockModelStore.query).toHaveBeenCalledWith(`ServiceInstanceLink`, '/core/serviceinstancelinks');
+        expect(service.ServiceInstanceSubscription).toBeDefined();
+        expect(service.ServiceInstanceLinkSubscription).toBeDefined();
+      });
+    });
+
+    describe('when they are enabled', () => {
+      beforeEach(() => {
+        service.ServiceInstanceSubscription = {
+          unsubscribe: jasmine.createSpy('ServiceInstanceSubscription')
+        };
+        service.ServiceInstanceLinkSubscription = {
+          unsubscribe: jasmine.createSpy('ServiceInstanceLinkSubscription')
+        };
+        service.serviceInstanceShown = true;
+      });
+
+      it('should cancel subscriptions', () => {
+        service.toggleServiceInstances();
+        expect(service.serviceInstanceShown).toBeFalsy();
+        expect(service.ServiceInstanceSubscription.unsubscribe).toHaveBeenCalled();
+        expect(service.ServiceInstanceLinkSubscription.unsubscribe).toHaveBeenCalled();
+      });
+
+      describe('and loaded in the graph', () => {
+        beforeEach(() => {
+          service.serviceGraph = new Graph();
+
+          services.forEach(s => {
+            service.addNode(s);
+          });
+
+          serviceInstances.forEach(si => {
+            service.addNode(si);
+          });
+
+          serviceInstanceLinks.forEach(sil => {
+            service.addEdge(sil);
+          });
+        });
+        it('should remove ServiceInstance and related nodes/edges from the graph', () => {
+          let filteredGraph = service.toggleServiceInstances();
+          expect(service.serviceInstanceShown).toBeFalsy();
+          expect(filteredGraph.nodes().length).toBe(2);
+          expect(filteredGraph.edges().length).toBe(0);
+          expect(service.serviceGraph.nodes().length).toBe(2);
+          expect(service.serviceGraph.edges().length).toBe(0);
+        });
+      });
+    });
+  });
+});
diff --git a/src/app/service-graph/services/graph.store.ts b/src/app/service-graph/services/graph.store.ts
new file mode 100644
index 0000000..4d46c88
--- /dev/null
+++ b/src/app/service-graph/services/graph.store.ts
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+import * as _ from 'lodash';
+import {Graph} from 'graphlib';
+import {IXosModelStoreService} from '../../datasources/stores/model.store';
+import {IXosDebouncer} from '../../core/services/helpers/debounce.helper';
+import {Subscription} from 'rxjs/Subscription';
+import {BehaviorSubject} from 'rxjs/BehaviorSubject';
+import {Observable} from 'rxjs/Observable';
+import {IXosBaseModel, IXosSgLink, IXosSgNode} from '../interfaces';
+
+
+export interface IXosGraphStore {
+  get(): Observable<Graph>;
+  nodesFromGraph(graph: Graph): IXosSgNode[];
+  linksFromGraph(graph: Graph): IXosSgLink[];
+  toggleServiceInstances(): Graph;
+}
+
+export class XosGraphStore implements IXosGraphStore {
+  static $inject = [
+    '$log',
+    'XosModelStore',
+    'XosDebouncer'
+  ];
+
+  // state
+  private serviceInstanceShown: boolean = false;
+
+  // graphs
+  private serviceGraph: any;
+  private ServiceGraphSubject: BehaviorSubject<any>;
+
+  // datastore
+  private ServiceSubscription: Subscription;
+  private ServiceDependencySubscription: Subscription;
+  private ServiceInstanceSubscription: Subscription;
+  private ServiceInstanceLinkSubscription: Subscription;
+
+  // debounced
+  private efficientNext = this.XosDebouncer.debounce(this.callNext, 500, this, false);
+
+  constructor (
+    private $log: ng.ILogService,
+    private XosModelStore: IXosModelStoreService,
+    private XosDebouncer: IXosDebouncer
+  ) {
+    this.$log.info('[XosGraphStore] Setup');
+
+    this.serviceGraph = new Graph();
+    this.ServiceGraphSubject = new BehaviorSubject(this.serviceGraph);
+
+    this.loadData();
+  }
+
+  $onDestroy() {
+    this.ServiceSubscription.unsubscribe();
+    this.ServiceDependencySubscription.unsubscribe();
+  }
+
+  public nodesFromGraph(graph: Graph): IXosSgNode[] {
+    return _.map(graph.nodes(), (n: string) => {
+      const nodeData = graph.node(n);
+      return {
+        id: n,
+        type: this.getModelType(nodeData),
+        data: nodeData
+      };
+    });
+  }
+
+  public linksFromGraph(graph: Graph): IXosSgLink[] {
+    const nodes = this.nodesFromGraph(graph);
+
+    // NOTE we'll need some intelligence here to differentiate between:
+    // - ServiceDependency
+    // - ServiceInstanceLinks
+    // - Owners
+
+    return _.map(graph.edges(), l => {
+      const link = graph.edge(l);
+      const linkType = this.getModelType(link);
+
+      // FIXME consider ownership links
+      let sourceId, targetId;
+
+      switch (linkType) {
+        case 'servicedependency':
+          sourceId = this.getServiceId(link.subscriber_service_id);
+          targetId = this.getServiceId(link.provider_service_id);
+          break;
+        case 'serviceinstancelink':
+          sourceId = this.getServiceInstanceId(link.subscriber_service_instance_id);
+          targetId = this.getServiceInstanceId(link.provider_service_instance_id);
+          break;
+        case 'ownership':
+          sourceId = this.getServiceId(link.service);
+          targetId = this.getServiceInstanceId(link.service_instance);
+      }
+
+      // NOTE help while debugging
+      if (!sourceId || !targetId) {
+        this.$log.warn(`Link ${l.v}-${l.w} has missing source or target:`, l, link);
+      }
+
+      return {
+        id: `${l.v}-${l.w}`,
+        type: this.getModelType(link),
+        source: _.findIndex(nodes, {id: sourceId}),
+        target: _.findIndex(nodes, {id: targetId}),
+        data: link
+      };
+    });
+  }
+
+  public toggleServiceInstances(): Graph {
+    if (this.serviceInstanceShown) {
+      // NOTE remove subscriptions
+      this.ServiceInstanceSubscription.unsubscribe();
+      this.ServiceInstanceLinkSubscription.unsubscribe();
+
+      // remove nodes from the graph
+      this.removeElementsFromGraph('serviceinstance'); // NOTE links are automatically removed by the graph library
+    }
+    else {
+      // NOTE subscribe to ServiceInstance and ServiceInstanceLink observables
+      this.loadServiceInstances();
+      this.loadServiceInstanceLinks();
+    }
+    this.serviceInstanceShown = !this.serviceInstanceShown;
+    return this.serviceGraph;
+  }
+
+  public get(): Observable<Graph> {
+    return this.ServiceGraphSubject.asObservable();
+  }
+
+  private loadData() {
+    this.loadServices();
+    this.loadServiceDependencies();
+  }
+
+  // graph operations
+  private addNode(node: IXosBaseModel) {
+    const nodeId = this.getNodeId(node);
+    this.serviceGraph.setNode(nodeId, node);
+
+    const nodeType = this.getModelType(node);
+    if (nodeType === 'serviceinstance') {
+      // NOTE adding owner link
+      this.addOwnershipEdge({
+        service: node.owner_id,
+        service_instance: node.id,
+        type: 'ownership'
+      });
+    }
+  }
+
+  private addEdge(link: IXosBaseModel) {
+    const linkType = this.getModelType(link);
+    if (linkType === 'servicedependency') {
+      const sourceId = this.getServiceId(link.subscriber_service_id);
+      const targetId = this.getServiceId(link.provider_service_id);
+      this.serviceGraph.setEdge(sourceId, targetId, link);
+    }
+    if (linkType === 'serviceinstancelink') {
+      // NOTE serviceinstancelink can point also to services, networks, ...
+      const sourceId = this.getServiceInstanceId(link.provider_service_instance_id);
+      if (angular.isDefined(link.subscriber_service_instance_id)) {
+        const targetId = this.getServiceInstanceId(link.subscriber_service_instance_id);
+        this.serviceGraph.setEdge(sourceId, targetId, link);
+      }
+    }
+  }
+
+  private addOwnershipEdge(link: any) {
+    const sourceId = this.getServiceInstanceId(link.service_instance);
+    const targetId = this.getServiceId(link.service);
+    this.serviceGraph.setEdge(sourceId, targetId, link);
+  }
+
+  private removeElementsFromGraph(type: string) {
+    _.forEach(this.serviceGraph.nodes(), (n: string) => {
+      const node = this.serviceGraph.node(n);
+      const nodeType = this.getModelType(node);
+      if (nodeType === type) {
+        this.serviceGraph.removeNode(n);
+      }
+    });
+    // NOTE update the observable
+    this.efficientNext(this.ServiceGraphSubject, this.serviceGraph);
+  }
+
+  // helpers
+  private getModelType(node: IXosBaseModel): string {
+    if (node.type) {
+      // NOTE we'll add "ownership" links
+      return node.type;
+    }
+    return node.class_names.split(',')[0].toLowerCase();
+  }
+
+  private getServiceId(id: number): string {
+    return `service~${id}`;
+  }
+
+  private getServiceInstanceId(id: number): string {
+    return `serviceinstance~${id}`;
+  }
+
+  private getNodeId(node: IXosBaseModel): string {
+
+    const nodeType = this.getModelType(node);
+    switch (nodeType) {
+      case 'service':
+        return this.getServiceId(node.id);
+      case 'serviceinstance':
+        return this.getServiceInstanceId(node.id);
+    }
+  }
+
+  // data loaders
+  private loadServices() {
+    this.ServiceSubscription = this.XosModelStore.query('Service', '/core/services')
+      .subscribe(
+        (res) => {
+          if (res.length > 0) {
+            _.forEach(res, n => {
+              this.addNode(n);
+            });
+            this.efficientNext(this.ServiceGraphSubject, this.serviceGraph);
+          }
+        },
+        (err) => {
+          this.$log.error(`[XosServiceGraphStore] Service Observable: `, err);
+        }
+      );
+  }
+
+  private loadServiceDependencies() {
+    this.ServiceDependencySubscription = this.XosModelStore.query('ServiceDependency', '/core/servicedependencys')
+      .subscribe(
+        (res) => {
+          if (res.length > 0) {
+            _.forEach(res, l => {
+              this.addEdge(l);
+            });
+            this.efficientNext(this.ServiceGraphSubject, this.serviceGraph);
+          }
+        },
+        (err) => {
+          this.$log.error(`[XosServiceGraphStore] Service Observable: `, err);
+        }
+      );
+  }
+
+  private loadServiceInstances() {
+    this.ServiceInstanceSubscription = this.XosModelStore.query('ServiceInstance', '/core/serviceinstances')
+      .subscribe(
+        (res) => {
+          if (res.length > 0) {
+            _.forEach(res, n => {
+              this.addNode(n);
+            });
+            this.efficientNext(this.ServiceGraphSubject, this.serviceGraph);
+          }
+        },
+        (err) => {
+          this.$log.error(`[XosServiceGraphStore] ServiceInstance Observable: `, err);
+        }
+      );
+  }
+
+  private loadServiceInstanceLinks() {
+    this.ServiceInstanceLinkSubscription = this.XosModelStore.query('ServiceInstanceLink', '/core/serviceinstancelinks')
+      .subscribe(
+        (res) => {
+          if (res.length > 0) {
+            _.forEach(res, l => {
+              this.addEdge(l);
+            });
+            this.efficientNext(this.ServiceGraphSubject, this.serviceGraph);
+          }
+        },
+        (err) => {
+          this.$log.error(`[XosServiceGraphStore] ServiceInstanceLinks Observable: `, err);
+        }
+      );
+  }
+
+  private callNext(subject: BehaviorSubject<any>, data: any) {
+    subject.next(data);
+  }
+}
diff --git a/src/app/service-graph/services/node-positioner.service.spec.ts b/src/app/service-graph/services/node-positioner.service.spec.ts
new file mode 100644
index 0000000..14fb9c4
--- /dev/null
+++ b/src/app/service-graph/services/node-positioner.service.spec.ts
@@ -0,0 +1,128 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+import * as angular from 'angular';
+import 'angular-mocks';
+import 'angular-ui-router';
+import {IXosNodePositioner, XosNodePositioner} from './node-positioner.service';
+
+let service: IXosNodePositioner;
+
+let scope: ng.IRootScopeService;
+
+let constraints: string = '';
+
+let mockResource = {
+  query: () => {
+    return;
+  }
+};
+
+const mockModelRest = {
+  getResource: jasmine.createSpy('ModelRest.getResource')
+    .and.returnValue(mockResource)
+};
+
+describe('The XosNodePositioner service', () => {
+
+  beforeEach(() => {
+    angular.module('XosNodePositioner', [])
+      .service('XosNodePositioner', XosNodePositioner)
+      .value('ModelRest', mockModelRest);
+
+    angular.mock.module('XosNodePositioner');
+  });
+
+  beforeEach(angular.mock.inject((
+    XosNodePositioner: IXosNodePositioner,
+    $rootScope: ng.IRootScopeService,
+    _$q_: ng.IQService) => {
+
+    service = XosNodePositioner;
+    scope = $rootScope;
+
+    spyOn(mockResource, 'query').and.callFake(() => {
+      const d = _$q_.defer();
+      d.resolve([{constraints}]);
+      return {$promise: d.promise};
+    });
+  }));
+
+  it('should position the nodes on the svg', (done) => {
+    const svg = {width: 300, height: 100};
+    const nodes = [
+      {data: {name: 'a'}},
+      {data: {name: 'b'}}
+    ];
+    constraints = '["a", "b"]';
+    service.positionNodes(svg, nodes)
+      .then((positioned) => {
+        expect(positioned[0].x).toBe(100);
+        expect(positioned[0].y).toBe(50);
+        expect(positioned[1].x).toBe(200);
+        expect(positioned[1].y).toBe(50);
+        done();
+      });
+
+    scope.$apply();
+  });
+
+  it('should position the nodes on the svg in vertical bundles', (done) => {
+    const svg = {width: 300, height: 90};
+    const nodes = [
+      {data: {name: 'a'}},
+      {data: {name: 'b'}},
+      {data: {name: 'c'}}
+    ];
+    constraints = '["a", ["b", "c"]]';
+    service.positionNodes(svg, nodes)
+      .then((positioned) => {
+        expect(positioned[0].x).toBe(100);
+        expect(positioned[0].y).toBe(45);
+        expect(positioned[1].x).toBe(200);
+        expect(positioned[1].y).toBe(30);
+        expect(positioned[2].x).toBe(200);
+        expect(positioned[2].y).toBe(60);
+        done();
+      });
+
+    scope.$apply();
+  });
+
+  it('should accept null as constraint to leave an empty space', (done) => {
+    const svg = {width: 300, height: 90};
+    const nodes = [
+      {data: {name: 'a'}},
+      {data: {name: 'b'}},
+      {data: {name: 'c'}}
+    ];
+    constraints = '[[null, "a"], ["b", "c"]]';
+    service.positionNodes(svg, nodes)
+      .then((positioned) => {
+        expect(positioned[0].x).toBe(100);
+        expect(positioned[0].y).toBe(60);
+        expect(positioned[1].x).toBe(200);
+        expect(positioned[1].y).toBe(30);
+        expect(positioned[2].x).toBe(200);
+        expect(positioned[2].y).toBe(60);
+        done();
+      });
+
+    scope.$apply();
+  });
+});
diff --git a/src/app/service-graph/services/node-positioner.service.ts b/src/app/service-graph/services/node-positioner.service.ts
new file mode 100644
index 0000000..499afd0
--- /dev/null
+++ b/src/app/service-graph/services/node-positioner.service.ts
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+import * as _ from 'lodash';
+import {IXosResourceService} from '../../datasources/rest/model.rest';
+import {IXosSgNode} from '../interfaces';
+
+export interface IXosNodePositioner {
+  positionNodes(svg: {width: number, height: number}, nodes: any[]): ng.IPromise<IXosSgNode[]>;
+}
+
+export class XosNodePositioner implements IXosNodePositioner {
+  static $inject = [
+    '$log',
+    '$q',
+    'ModelRest'
+  ];
+
+  constructor (
+    private $log: ng.ILogService,
+    private $q: ng.IQService,
+    private ModelRest: IXosResourceService,
+  ) {
+    this.$log.info('[XosNodePositioner] Setup');
+  }
+
+  public positionNodes(svg: {width: number, height: number}, nodes: any[]): ng.IPromise<IXosSgNode[]> {
+
+    // TODO refactor naming in this loop to make it clearer
+    const d =  this.$q.defer();
+
+    this.getConstraints()
+      .then(constraints => {
+        const hStep = this.getHorizontalStep(svg.width, constraints);
+        const positionConstraints = _.reduce(constraints, (all: any, c: string | string[], i: number) => {
+          let pos: {x: number, y: number, fixed: boolean} = {
+            x: svg.width / 2,
+            y: svg.height / 2,
+            fixed: true
+          };
+          // NOTE it's a single element, leave it in the middle
+          if (angular.isString(c)) {
+            pos.x = (i + 1) * hStep;
+            all[c] = pos;
+          }
+          else {
+            const verticalConstraints = c;
+            const vStep = this.getVerticalStep(svg.height, verticalConstraints);
+            _.forEach(verticalConstraints, (c: string, v: number) => {
+              if (angular.isString(c)) {
+                let p = angular.copy(pos);
+                p.x = (i + 1) * hStep;
+                p.y = (v + 1) * vStep;
+                all[c] = p;
+              }
+            });
+          }
+          return all;
+        }, {});
+
+        d.resolve(_.map(nodes, n => {
+          return angular.merge(n, positionConstraints[n.data.name]);
+        }));
+      })
+      .catch(e => {
+        this.$log.error(`[XosNodePositioner] Error retrieving constraints`, e);
+      });
+
+    return d.promise;
+  }
+
+  private getConstraints(): ng.IPromise<any[]> {
+    const d = this.$q.defer();
+    this.ModelRest.getResource('/core/servicegraphconstraints').query().$promise
+      .then(res => {
+        d.resolve(JSON.parse(res[0].constraints));
+      })
+      .catch(e => {
+        d.reject(e);
+      });
+    return d.promise;
+  }
+
+  private getHorizontalStep(svgWidth: number, constraints: any[]) {
+    return svgWidth / (constraints.length + 1);
+  }
+
+  private getVerticalStep(svgHeight: number, verticalConstraints: string[]) {
+    // NOTE verticalConstraints represent the vertical part (the nested array)
+    return svgHeight / (verticalConstraints.length + 1);
+  }
+}
diff --git a/src/app/service-graph/services/renderer/node.renderer.ts b/src/app/service-graph/services/renderer/node.renderer.ts
new file mode 100644
index 0000000..17bcf70
--- /dev/null
+++ b/src/app/service-graph/services/renderer/node.renderer.ts
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as d3 from 'd3';
+import * as _ from 'lodash';
+import {IXosSgNode} from '../../interfaces';
+import {XosServiceGraphConfig as config} from '../../graph.config';
+import {IXosServiceGraphIcons} from '../d3-helpers/graph-icons.service';
+import {IXosGraphHelpers} from '../d3-helpers/graph-elements.helpers';
+
+export interface IXosNodeRenderer {
+  renderNodes(forceLayout: d3.forceLayout, nodeContainer: d3.Selection, nodes: IXosSgNode[]): void;
+}
+
+export class XosNodeRenderer {
+
+  static $inject = [
+    'XosServiceGraphIcons',
+    'XosGraphHelpers'
+  ];
+
+  private drag;
+
+  constructor (
+    private XosServiceGraphIcons: IXosServiceGraphIcons,
+    private XosGraphHelpers: IXosGraphHelpers
+  ) {}
+
+  public renderNodes(forceLayout: any, nodeContainer: any, nodes: IXosSgNode[]): void {
+
+    this.drag = forceLayout.drag()
+      .on('dragstart', (n: IXosSgNode) => {
+        n.fixed =  true;
+      });
+
+    const node = nodeContainer
+      .selectAll('g.node')
+      .data(nodes, n => n.id);
+
+    node
+      .call(this.drag);
+
+    const entering = node.enter()
+      .append('g')
+      .attr({
+        id: n => n.id,
+        class: n => `node ${n.type} ${this.XosGraphHelpers.parseElemClasses(n.d3Class)}`,
+      });
+
+    this.renderServiceNodes(entering.filter('.service'));
+    this.renderServiceInstanceNodes(entering.filter('.serviceinstance'));
+
+    node.exit().remove();
+  }
+
+  private renderServiceNodes(nodes: d3.selection) {
+
+    nodes
+      .append('rect')
+      .attr({
+        rx: config.node.radius,
+        ry: config.node.radius
+      });
+
+    nodes
+      .append('path')
+      .attr({
+        d: this.XosServiceGraphIcons.get('service').path,
+        transform: this.XosServiceGraphIcons.get('service').transform,
+        class: 'icon'
+      });
+
+    this.positionServiceNodeGroup(nodes);
+    this.handleLabels(nodes);
+  }
+
+  private renderServiceInstanceNodes(nodes: d3.selection) {
+    nodes.append('rect')
+      .attr({
+        width: 40,
+        height: 40,
+        x: -20,
+        y: -20,
+        transform: `rotate(45)`
+      });
+
+    nodes
+      .append('path')
+      .attr({
+        d: this.XosServiceGraphIcons.get('serviceinstance').path,
+        class: 'icon'
+      });
+
+    this.positionServiceInstanceNodeGroup(nodes);
+    this.handleLabels(nodes); // eventually improve, padding top is wrong
+  }
+
+  private positionServiceNodeGroup(nodes: d3.selection) {
+    const self = this;
+    nodes.each(function (d: IXosSgNode) {
+      const node = d3.select(this);
+      const rect = node.select('rect');
+      const icon = node.select('path');
+      const bbox = self.XosGraphHelpers.getSiblingIconBBox(rect.node());
+
+      rect
+        .attr({
+          width: bbox.width + config.node.padding,
+          height: bbox.height + config.node.padding,
+          x: - (config.node.padding / 2),
+          y: - (config.node.padding / 2),
+          transform: `translate(${-bbox.width / 2}, ${-bbox.height / 2})`
+        });
+
+      icon
+        .attr({
+          transform: `translate(${-bbox.width / 2}, ${-bbox.height / 2})`
+        });
+    });
+  }
+
+  private positionServiceInstanceNodeGroup(nodes: d3.selection) {
+    const self = this;
+    nodes.each(function (d: IXosSgNode) {
+      const node = d3.select(this);
+      const rect = node.select('rect');
+      const icon = node.select('path');
+      const bbox = self.XosGraphHelpers.getSiblingIconBBox(rect.node());
+      const size = _.max([bbox.width, bbox.height]); // NOTE we need it to be a square
+      rect
+        .attr({
+          width: size + config.node.padding,
+          height: size + config.node.padding,
+          x: - (config.node.padding / 2),
+          y: - (config.node.padding / 2),
+          transform: `rotate(45), translate(${-bbox.width / 2}, ${-bbox.height / 2})`
+        });
+
+      icon
+        .attr({
+          transform: `translate(${-bbox.width / 2}, ${-bbox.height / 2})`
+        });
+    });
+  }
+
+  private handleLabels(nodes: d3.selection) {
+    const self = this;
+    // if (this.userConfig.labels) {
+
+      // group to contain label text and wrapper
+      const label = nodes.append('g')
+        .attr({
+          class: 'label'
+        });
+
+      // setting up the wrapper
+      label
+        .append('rect')
+        .attr({
+          class: 'label-wrapper',
+          rx: config.node.radius,
+          ry: config.node.radius
+        });
+
+      // adding text
+      label
+        .append('text')
+        .text(n => this.getNodeLabel(n))
+        .attr({
+          'opacity': 0,
+          'text-anchor': 'left',
+          'alignment-baseline': 'bottom',
+          'font-size': config.node.text,
+          y: config.node.text * 0.78
+        })
+        .transition()
+        .duration(config.duration)
+        .attr({
+          opacity: 1
+        });
+
+      // resize and position label
+      label.each(function() {
+        const text = d3.select(this).select('text').node();
+        const rect = d3.select(this).select('rect');
+        const iconRect = d3.select(this.parentNode).select('rect').node();
+        const icon = self.XosGraphHelpers.getBBox(iconRect);
+        const bbox = self.XosGraphHelpers.getBBox(text);
+
+        // scale the rectangle around the label to fit the text
+        rect
+          .attr({
+            width: bbox.width + config.node.padding,
+            height: config.node.text - 2 + config.node.padding,
+            x: -(config.node.padding / 2),
+            y: -(config.node.padding / 2),
+          });
+
+        // translate the lable group to the correct position
+        d3.select(this)
+          .attr({
+            transform: function() {
+              const label = self.XosGraphHelpers.getBBox(this);
+              const x = - (label.width - config.node.padding) / 2;
+              const y = (icon.height / 2) + config.node.padding;
+              return `translate(${x}, ${y})`;
+            }
+          });
+      });
+    // }
+    // else {
+    //   node.selectAll('text')
+    //     .transition()
+    //     .duration(this.duration)
+    //     .attr({
+    //       opacity: 0
+    //     })
+    //     .remove();
+    // }
+  }
+
+  private getNodeLabel(n: any): string {
+    return n.data.name ? n.data.name.toUpperCase() : n.data.id;
+    // return n.data.name ? n.data.name.toUpperCase() + ` - ${n.data.id}` : n.data.id;
+  }
+}
diff --git a/src/app/service-graph/services/service-graph.store.spec.ts b/src/app/service-graph/services/service-graph.store.spec.ts
deleted file mode 100644
index 91c7dd0..0000000
--- a/src/app/service-graph/services/service-graph.store.spec.ts
+++ /dev/null
@@ -1,158 +0,0 @@
-
-/*
- * Copyright 2017-present Open Networking Foundation
-
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
-
- * http://www.apache.org/licenses/LICENSE-2.0
-
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-import * as angular from 'angular';
-import 'angular-mocks';
-import 'angular-ui-router';
-import {IXosServiceGraphStore, XosServiceGraphStore} from './service-graph.store';
-import {Subject} from 'rxjs';
-import {XosDebouncer} from '../../core/services/helpers/debounce.helper';
-import {IXosServiceGraph} from '../interfaces';
-import {XosServiceGraphExtender, IXosServiceGraphExtender} from './graph.extender';
-
-let service: IXosServiceGraphStore, extender: IXosServiceGraphExtender;
-
-const subjects = {
-  service: new Subject<any>(),
-  tenant: new Subject<any>(),
-  subscriber: new Subject<any>(),
-  tenantroot: new Subject<any>(),
-  network: new Subject<any>(),
-  servicedependency: new Subject<any>()
-};
-
-// COARSE data
-const coarseServices = [
-  {
-    id: 1,
-    name: 'Service A',
-    class_names: 'Service, XOSBase'
-  },
-  {
-    id: 2,
-    name: 'Service B',
-    class_names: 'Service, XOSBase'
-  }
-];
-
-const coarseTenants = [
-  {
-    id: 1,
-    provider_service_id: 2,
-    subscriber_service_id: 1,
-    kind: 'coarse',
-    class_names: 'Tenant, XOSBase'
-  }
-];
-
-const mockModelStore = {
-  query: (modelName: string) => {
-    return subjects[modelName.toLowerCase()].asObservable();
-  }
-};
-
-describe('The XosServiceGraphStore service', () => {
-
-  beforeEach(() => {
-    angular.module('xosServiceGraphStore', [])
-      .service('XosServiceGraphStore', XosServiceGraphStore)
-      .value('XosModelStore', mockModelStore)
-      .service('XosServiceGraphExtender', XosServiceGraphExtender)
-      .service('XosDebouncer', XosDebouncer);
-
-    angular.mock.module('xosServiceGraphStore');
-  });
-
-  beforeEach(angular.mock.inject((
-    XosServiceGraphStore: IXosServiceGraphStore,
-    XosServiceGraphExtender: IXosServiceGraphExtender
-  ) => {
-    service = XosServiceGraphStore;
-    extender = XosServiceGraphExtender;
-  }));
-
-  describe('when subscribing for the COARSE service graph', () => {
-    beforeEach((done) => {
-      subjects.service.next(coarseServices);
-      subjects.servicedependency.next(coarseTenants);
-      setTimeout(done, 500);
-    });
-
-    it('should return an observer for the Coarse Service Graph', (done) => {
-      service.getCoarse()
-        .subscribe(
-          (res: IXosServiceGraph) => {
-            expect(res.nodes.length).toBe(2);
-            expect(res.nodes[0].d3Class).toBeUndefined();
-            expect(res.links.length).toBe(1);
-            expect(res.links[0].d3Class).toBeUndefined();
-            done();
-          },
-          (err) => {
-            done(err);
-          }
-        );
-    });
-
-    xdescribe('when a reducer is registered', () => {
-      // NOTE the reducer appliance has been moved in the component
-      beforeEach((done) => {
-        extender.register('coarse', 'test', (graph: IXosServiceGraph) => {
-          graph.nodes = graph.nodes.map(n => {
-            n.d3Class = `testNode`;
-            return n;
-          });
-
-          graph.links = graph.links.map(n => {
-            n.d3Class = `testLink`;
-            return n;
-          });
-
-          return graph;
-        });
-
-        // triggering another next cycle to apply the reducer
-        subjects.service.next(coarseServices);
-        subjects.tenant.next(coarseTenants);
-        setTimeout(done, 500);
-      });
-
-      it('should transform the result', (done) => {
-        service.getCoarse()
-          .subscribe(
-            (res: IXosServiceGraph) => {
-              expect(res.nodes.length).toBe(2);
-              expect(res.nodes[0].d3Class).toEqual('testNode');
-              expect(res.links.length).toBe(1);
-              expect(res.links[0].d3Class).toEqual('testLink');
-              done();
-            },
-            (err) => {
-              done(err);
-            }
-          );
-      });
-    });
-  });
-
-  describe('when subscribing for the Fine-grained service graph', () => {
-    xit('should have a test', () => {
-      expect(true).toBeTruthy();
-    });
-  });
-});
diff --git a/src/app/service-graph/services/service-graph.store.ts b/src/app/service-graph/services/service-graph.store.ts
deleted file mode 100644
index 92b9e6d..0000000
--- a/src/app/service-graph/services/service-graph.store.ts
+++ /dev/null
@@ -1,205 +0,0 @@
-
-/*
- * Copyright 2017-present Open Networking Foundation
-
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
-
- * http://www.apache.org/licenses/LICENSE-2.0
-
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-import * as _ from 'lodash';
-import {Observable, BehaviorSubject, Subscription} from 'rxjs';
-import {IXosModelStoreService} from '../../datasources/stores/model.store';
-import {
-  IXosServiceGraph, IXosServiceModel, IXosTenantModel, IXosCoarseGraphData,
-  IXosServiceGraphNode, IXosServiceGraphLink, IXosFineGrainedGraphData
-} from '../interfaces';
-import {IXosDebouncer} from '../../core/services/helpers/debounce.helper';
-export interface IXosServiceGraphStore {
-  // TODO remove, moved in a new service
-  get(): Observable<IXosServiceGraph>;
-  // TODO rename in get()
-  getCoarse(): Observable<IXosServiceGraph>;
-}
-
-export class XosServiceGraphStore implements IXosServiceGraphStore {
-  static $inject = [
-    '$log',
-    'XosModelStore',
-    'XosDebouncer'
-  ];
-
-  // graph data store
-  private graphData: BehaviorSubject<IXosFineGrainedGraphData> = new BehaviorSubject({
-    services: [],
-    tenants: [],
-    networks: [],
-    subscribers: [],
-    servicedependencies: []
-  });
-
-  private emptyGraph: IXosServiceGraph = {
-    nodes: [],
-    links: []
-  };
-
-  // representation of the graph as D3 requires
-  private d3CoarseGraph = new BehaviorSubject(this.emptyGraph);
-  private d3FineGrainedGraph = new BehaviorSubject(this.emptyGraph);
-
-  // storing locally reference to the data model
-  private services;
-  private tenants;
-  private subscribers;
-  private networks;
-  private servicedependencys;
-
-  // debounced functions
-  private handleData;
-
-  // datastore
-  private ServiceSubscription: Subscription;
-  private NetworkSubscription: Subscription;
-  private ServiceDependencySubscription: Subscription;
-
-  constructor (
-    private $log: ng.ILogService,
-    private XosModelStore: IXosModelStoreService,
-    private XosDebouncer: IXosDebouncer
-  ) {
-
-    this.$log.info(`[XosServiceGraphStore] Setup`);
-
-    // we want to have a quiet period of 500ms from the last event before doing anything
-    this.handleData = this.XosDebouncer.debounce(this._handleData, 500, this, false);
-
-    // observe models and populate graphData
-    this.ServiceSubscription = this.XosModelStore.query('Service', '/core/services')
-      .subscribe(
-        (res) => {
-          this.combineData(res, 'services');
-        },
-        (err) => {
-          this.$log.error(`[XosServiceGraphStore] Service Observable: `, err);
-        }
-      );
-
-    this.ServiceDependencySubscription = this.XosModelStore.query('ServiceDependency', '/core/servicedependencys')
-      .subscribe(
-        (res) => {
-          this.combineData(res, 'servicedependencies');
-        },
-        (err) => {
-          this.$log.error(`[XosServiceGraphStore] Service Observable: `, err);
-        }
-      );
-
-    this.NetworkSubscription = this.XosModelStore.query('Network', '/core/networks')
-      .subscribe(
-        (res) => {
-          this.combineData(res, 'networks');
-        },
-        (err) => {
-          this.$log.error(`[XosServiceGraphStore] graphData Observable: `, err);
-        }
-      );
-
-    // observe graphData and build Coarse and FineGrained graphs
-    this.graphData
-      .subscribe(
-        (res: IXosFineGrainedGraphData) => {
-          this.$log.debug(`[XosServiceGraphStore] New graph data received`, res);
-          this.graphDataToCoarseGraph(res);
-          // this.graphDataToFineGrainedGraph(res);
-        },
-        (err) => {
-          this.$log.error(`[XosServiceGraphStore] graphData Observable: `, err);
-        }
-      );
-  }
-
-  public get() {
-    return this.d3FineGrainedGraph.asObservable();
-  }
-
-  public getCoarse() {
-    return this.d3CoarseGraph.asObservable();
-  }
-
-  private combineData(data: any, type: 'services'|'tenants'|'subscribers'|'networks'|'servicedependencies') {
-    switch (type) {
-      case 'services':
-        this.services = data;
-        break;
-      case 'tenants':
-        this.tenants = data;
-        break;
-      case 'subscribers':
-        this.subscribers = data;
-        break;
-      case 'networks':
-        this.networks = data;
-        break;
-      case 'servicedependencies':
-        this.servicedependencys = data;
-        break;
-    }
-    this.handleData(this.services, this.tenants);
-  }
-
-  private _handleData(services: IXosServiceModel[], tenants: IXosTenantModel[]) {
-    this.graphData.next({
-      services: this.services,
-      tenants: this.tenants,
-      subscribers: this.subscribers,
-      networks: this.networks,
-      servicedependencies: this.servicedependencys
-    });
-  }
-
-  private getNodeIndexById(id: number | string, nodes: IXosServiceModel[]) {
-    return _.findIndex(nodes, {id: id});
-  }
-
-  private graphDataToCoarseGraph(data: IXosCoarseGraphData) {
-
-    try {
-      const links: IXosServiceGraphLink[] = _.chain(data.servicedependencies)
-        .map((t: IXosTenantModel) => {
-          return {
-            id: t.id,
-            source: this.getNodeIndexById(t.provider_service_id, data.services),
-            target: this.getNodeIndexById(t.subscriber_service_id, data.services),
-            model: t
-          };
-        })
-        .value();
-
-      const nodes: IXosServiceGraphNode[] = _.map(data.services, (s: IXosServiceModel) => {
-        return {
-          id: s.id,
-          label: s.name,
-          model: s
-        };
-      });
-
-      let graph: IXosServiceGraph = {
-        nodes,
-        links
-      };
-
-      this.d3CoarseGraph.next(graph);
-    } catch (e) {
-      this.d3CoarseGraph.error(e);
-    }
-  }
-}
diff --git a/src/app/service-graph/services/service-instance.graph.store.ts b/src/app/service-graph/services/service-instance.graph.store.ts
deleted file mode 100644
index b8220b9..0000000
--- a/src/app/service-graph/services/service-instance.graph.store.ts
+++ /dev/null
@@ -1,281 +0,0 @@
-
-/*
- * Copyright 2017-present Open Networking Foundation
-
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
-
- * http://www.apache.org/licenses/LICENSE-2.0
-
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-import * as _ from 'lodash';
-import {Observable, BehaviorSubject, Subscription} from 'rxjs';
-import {
-  IXosServiceGraph, IXosServiceInstanceGraphData, IXosServiceGraphNode
-} from '../interfaces';
-import {IXosDebouncer} from '../../core/services/helpers/debounce.helper';
-import {IXosModelStoreService} from '../../datasources/stores/model.store';
-import {IXosServiceGraphStore} from './service-graph.store';
-
-export interface IXosServiceInstanceGraphStore {
-  get(): Observable<IXosServiceGraph>;
-}
-
-export class XosServiceInstanceGraphStore implements IXosServiceInstanceGraphStore {
-  static $inject = [
-    '$log',
-    'XosServiceGraphStore',
-    'XosModelStore',
-    'XosDebouncer'
-  ];
-
-  private CoarseGraphSubscription: Subscription;
-  private ServiceInstanceSubscription: Subscription;
-  private ServiceInstanceLinkSubscription: Subscription;
-  private NetworkSubscription: Subscription;
-
-  // debounced functions
-  private handleData;
-
-
-  // FIXME this is declared also in ServiceGraphStore
-  private emptyGraph: IXosServiceGraph = {
-    nodes: [],
-    links: []
-  };
-
-  // graph data store
-  private graphData: BehaviorSubject<IXosServiceInstanceGraphData> = new BehaviorSubject({
-    serviceGraph: this.emptyGraph,
-    serviceInstances: [],
-    serviceInstanceLinks: [],
-    networks: []
-  });
-
-  private d3ServiceInstanceGraph = new BehaviorSubject(this.emptyGraph);
-
-  private serviceGraph: IXosServiceGraph = this.emptyGraph;
-  private serviceInstances: any[] = [];
-  private serviceInstanceLinks: any[] = [];
-  private networks: any[] = [];
-
-  constructor (
-    private $log: ng.ILogService,
-    private XosServiceGraphStore: IXosServiceGraphStore,
-    private XosModelStore: IXosModelStoreService,
-    private XosDebouncer: IXosDebouncer
-  ) {
-    this.$log.info(`[XosServiceInstanceGraphStore] Setup`);
-
-    // we want to have a quiet period of 500ms from the last event before doing anything
-    this.handleData = this.XosDebouncer.debounce(this._handleData, 500, this, false);
-
-    this.CoarseGraphSubscription = this.XosServiceGraphStore.getCoarse()
-      .subscribe(
-        (graph: IXosServiceGraph) => {
-          this.combineData(graph, 'serviceGraph');
-        }
-      );
-
-    this.ServiceInstanceSubscription = this.XosModelStore.query('ServiceInstance', '/core/serviceinstances')
-      .subscribe(
-        (res) => {
-          this.combineData(res, 'serviceInstance');
-        },
-        (err) => {
-          this.$log.error(`[XosServiceInstanceGraphStore] Service Observable: `, err);
-        }
-      );
-
-    this.ServiceInstanceLinkSubscription = this.XosModelStore.query('ServiceInstanceLink', '/core/serviceinstancelinks')
-      .subscribe(
-        (res) => {
-          this.combineData(res, 'serviceInstanceLink');
-        },
-        (err) => {
-          this.$log.error(`[XosServiceInstanceGraphStore] Service Observable: `, err);
-        }
-      );
-
-    this.NetworkSubscription = this.XosModelStore.query('Network', '/core/networks')
-      .subscribe(
-        (res) => {
-          this.combineData(res, 'networks');
-        },
-        (err) => {
-          this.$log.error(`[XosServiceGraphStore] graphData Observable: `, err);
-        }
-      );
-
-    // observe graphData and build ServiceInstance graph
-    this.graphData
-      .subscribe(
-        (res: IXosServiceInstanceGraphData) => {
-          this.$log.debug(`[XosServiceInstanceGraphStore] New graph data received`, res);
-
-          this.graphDataToD3(res);
-        },
-        (err) => {
-          this.$log.error(`[XosServiceInstanceGraphStore] graphData Observable: `, err);
-        }
-      );
-  }
-
-  public get(): Observable<IXosServiceGraph> {
-    return this.d3ServiceInstanceGraph;
-  }
-
-  // called by all the observables, combine the data in a globla graph observable
-  private combineData(data: any, type: 'serviceGraph' | 'serviceInstance' | 'serviceInstanceLink' | 'serviceInterface' | 'networks') {
-    switch (type) {
-      case 'serviceGraph':
-        this.serviceGraph = angular.copy(data);
-        break;
-      case 'serviceInstance':
-        this.serviceInstances = data;
-        break;
-      case 'serviceInstanceLink':
-        this.serviceInstanceLinks = data;
-        break;
-      case 'networks':
-        this.networks = data;
-        break;
-    }
-    this.handleData();
-  }
-
-  private _handleData() {
-    this.graphData.next({
-      serviceGraph: this.serviceGraph,
-      serviceInstances: this.serviceInstances,
-      serviceInstanceLinks: this.serviceInstanceLinks,
-      networks: this.networks
-    });
-  }
-
-  private getNodeType(n: any) {
-    return n.class_names.split(',')[0].toLowerCase();
-  }
-
-  private getNodeLabel(n: any) {
-    if (this.getNodeType(n) === 'serviceinstance') {
-      return n.name ? n.name : n.id;
-    }
-    return n.humanReadableName ? n.humanReadableName : n.name;
-  }
-
-  private d3Id(type: string, id: number) {
-    return `${type.toLowerCase()}~${id}`;
-  }
-
-  private toD3Node(n: any): IXosServiceGraphNode {
-    return {
-      id: this.d3Id(this.getNodeType(n), n.id),
-      label: this.getNodeLabel(n),
-      model: n,
-      type: this.getNodeType(n)
-    };
-  }
-
-  private getServiceInstanceIndexById(l: any, nodes: any[], where: 'source' | 'target'): string {
-    if (where === 'source') {
-      return _.find(nodes, {id: `serviceinstance~${l.provider_service_instance_id}`});
-    }
-    else {
-      if (l.subscriber_service_id) {
-        return _.find(nodes, {id: `service~${l.subscriber_service_id}`});
-      }
-      else if (l.subscriber_network_id) {
-        return _.find(nodes, {id: `network~${l.subscriber_network_id}`});
-      }
-      else if (l.subscriber_service_instance_id) {
-        return _.find(nodes, {id: `serviceinstance~${l.subscriber_service_instance_id}`});
-      }
-    }
-  }
-
-  private getOwnerById(id: number, nodes: any[]): any {
-    return _.find(nodes, {id: `service~${id}`});
-  }
-
-  private graphDataToD3(data: IXosServiceInstanceGraphData) {
-    try {
-      // get all the nodes
-      let nodes = _.chain(data.serviceGraph.nodes)
-        .map(n => {
-          // HACK we are receiving node as d3 models
-          return n.model;
-        })
-        .map(n => {
-          return this.toD3Node(n);
-        })
-        .value();
-
-      data.serviceInstances = _.chain(data.serviceInstances)
-        .map(n => {
-          return this.toD3Node(n);
-        })
-        .value();
-      nodes = nodes.concat(data.serviceInstances);
-
-      data.networks = _.chain(data.networks)
-        .filter(n => {
-          const subscriber = _.findIndex(data.serviceInstanceLinks, {subscriber_network_id: n.id});
-          return subscriber > -1;
-        })
-        .map(n => {
-          return this.toD3Node(n);
-        })
-        .value();
-      nodes = nodes.concat(data.networks);
-
-      let links = data.serviceGraph.links;
-
-      // create the links starting from the coarse ones
-      links = _.reduce(data.serviceInstanceLinks, (links, l) => {
-        let link =  {
-          id: `service_instance_link~${l.id}`,
-          source: this.getServiceInstanceIndexById(l, nodes, 'source'),
-          target: this.getServiceInstanceIndexById(l, nodes, 'target'),
-          model: l,
-          d3Class: 'service-instance'
-        };
-        links.push(link);
-        return links;
-      }, data.serviceGraph.links);
-
-      const linksToService = _.reduce(data.serviceInstances, (links, n) => {
-        if (angular.isDefined(n.model.owner_id)) {
-          let link =  {
-            id: `owner~${n.id}`,
-            source: n,
-            target: this.getOwnerById(n.model.owner_id, nodes),
-            model: n,
-            d3Class: 'owner'
-          };
-          links.push(link);
-        }
-        return links;
-      }, []);
-
-      links = links.concat(linksToService);
-
-      let graph: IXosServiceGraph = {
-        nodes,
-        links
-      };
-
-      this.d3ServiceInstanceGraph.next(graph);
-    } catch (e) {
-      this.d3ServiceInstanceGraph.error(e);
-    }
-  }
-}
diff --git a/src/app/views/dashboard/dashboard.html b/src/app/views/dashboard/dashboard.html
index 93cf2aa..3aa8505 100644
--- a/src/app/views/dashboard/dashboard.html
+++ b/src/app/views/dashboard/dashboard.html
@@ -19,7 +19,7 @@
  <!--<h1>Dashboard</h1>-->
  <div class="row">
      <div class="col-xs-12">
-         <xos-coarse-tenancy-graph></xos-coarse-tenancy-graph>
+         <xos-service-graph></xos-service-graph>
      </div>
  </div>
 <div class="row">