Implemented saving, changing, retrieving, and deleting balance segment sets.
diff --git a/api/src/main/java/io/mifos/portfolio/api/v1/domain/BalanceSegmentSet.java b/api/src/main/java/io/mifos/portfolio/api/v1/domain/BalanceSegmentSet.java
index 7d4c3da..a2daf1c 100644
--- a/api/src/main/java/io/mifos/portfolio/api/v1/domain/BalanceSegmentSet.java
+++ b/api/src/main/java/io/mifos/portfolio/api/v1/domain/BalanceSegmentSet.java
@@ -22,7 +22,6 @@
 import java.math.BigDecimal;
 import java.util.List;
 import java.util.Objects;
-import java.util.SortedSet;
 
 /**
  * @author Myrle Krantz
@@ -32,7 +31,7 @@
   @ValidIdentifier
   private String identifier;
 
-  private SortedSet<BigDecimal> segments;
+  private List<BigDecimal> segments;
 
   @ValidIdentifiers
   private List<String> segmentIdentifiers;
@@ -40,12 +39,6 @@
   public BalanceSegmentSet() {
   }
 
-  public BalanceSegmentSet(String identifier, SortedSet<BigDecimal> segments, List<String> segmentIdentifiers) {
-    this.identifier = identifier;
-    this.segments = segments;
-    this.segmentIdentifiers = segmentIdentifiers;
-  }
-
   public String getIdentifier() {
     return identifier;
   }
@@ -54,11 +47,11 @@
     this.identifier = identifier;
   }
 
-  public SortedSet<BigDecimal> getSegments() {
+  public List<BigDecimal> getSegments() {
     return segments;
   }
 
-  public void setSegments(SortedSet<BigDecimal> segments) {
+  public void setSegments(List<BigDecimal> segments) {
     this.segments = segments;
   }
 
diff --git a/api/src/main/java/io/mifos/portfolio/api/v1/validation/CheckValidSegmentList.java b/api/src/main/java/io/mifos/portfolio/api/v1/validation/CheckValidSegmentList.java
index aeb02ec..96fc98d 100644
--- a/api/src/main/java/io/mifos/portfolio/api/v1/validation/CheckValidSegmentList.java
+++ b/api/src/main/java/io/mifos/portfolio/api/v1/validation/CheckValidSegmentList.java
@@ -37,11 +37,20 @@
     if (value.getSegmentIdentifiers() == null)
       return false;
 
-    if (value.getSegments().size() + 1 != value.getSegmentIdentifiers().size())
+    if (value.getSegments().size() == 0)
       return false;
 
-    for (final BigDecimal segment : value.getSegments()) {
-      if (segment.compareTo(BigDecimal.ZERO) <= 0)
+    if (value.getSegments().size() != value.getSegmentIdentifiers().size())
+      return false;
+
+    if (value.getSegments().get(0).compareTo(BigDecimal.ZERO) != 0)
+      return false;
+
+    for (int i = 0; i < value.getSegments().size() -1; i++) {
+      final BigDecimal segment1 = value.getSegments().get(i);
+      final BigDecimal segment2 = value.getSegments().get(i+1);
+
+      if (segment1.compareTo(segment2) > 0)
         return false;
     }
 
diff --git a/api/src/test/java/io/mifos/portfolio/api/v1/domain/BalanceSegmentSetTest.java b/api/src/test/java/io/mifos/portfolio/api/v1/domain/BalanceSegmentSetTest.java
index c125654..d787a96 100644
--- a/api/src/test/java/io/mifos/portfolio/api/v1/domain/BalanceSegmentSetTest.java
+++ b/api/src/test/java/io/mifos/portfolio/api/v1/domain/BalanceSegmentSetTest.java
@@ -34,7 +34,7 @@
   protected BalanceSegmentSet createValidTestSubject() {
     final BalanceSegmentSet ret = new BalanceSegmentSet();
     ret.setIdentifier("valid");
-    ret.setSegments(new TreeSet<>(Arrays.asList(BigDecimal.valueOf(100), BigDecimal.valueOf(10_000))));
+    ret.setSegments(Arrays.asList(BigDecimal.ZERO, BigDecimal.valueOf(100), BigDecimal.valueOf(10_000)));
     ret.setSegmentIdentifiers(Arrays.asList("small", "medium", "large"));
     return ret;
   }
@@ -54,8 +54,14 @@
     ret.add(new ValidationTestCase<BalanceSegmentSet>("too short identifier list")
         .adjustment(x -> x.setSegmentIdentifiers(Arrays.asList("small", "large")))
         .valid(false));
-    ret.add(new ValidationTestCase<BalanceSegmentSet>("negative segmentation")
-        .adjustment(x -> x.setSegments(new TreeSet<>(Arrays.asList(BigDecimal.ONE.negate(), BigDecimal.valueOf(100)))))
+    ret.add(new ValidationTestCase<BalanceSegmentSet>("too short segment list")
+        .adjustment(x -> x.setSegments(Arrays.asList(BigDecimal.ZERO, BigDecimal.valueOf(100))))
+        .valid(false));
+    ret.add(new ValidationTestCase<BalanceSegmentSet>("non-zero first entry")
+        .adjustment(x -> x.setSegments(Arrays.asList(BigDecimal.ONE, BigDecimal.valueOf(100), BigDecimal.valueOf(10_000))))
+        .valid(false));
+    ret.add(new ValidationTestCase<BalanceSegmentSet>("mis-ordered segmentation")
+        .adjustment(x -> x.setSegments(Arrays.asList(BigDecimal.ZERO, BigDecimal.valueOf(10_000), BigDecimal.valueOf(100))))
         .valid(false));
     ret.add(new ValidationTestCase<BalanceSegmentSet>("invalid identifier")
         .adjustment(x -> x.setIdentifier("//"))
diff --git a/component-test/src/main/java/io/mifos/portfolio/TestBalanceSegmentSets.java b/component-test/src/main/java/io/mifos/portfolio/TestBalanceSegmentSets.java
index 001be34..e4852e6 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestBalanceSegmentSets.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestBalanceSegmentSets.java
@@ -25,7 +25,6 @@
 
 import java.math.BigDecimal;
 import java.util.Arrays;
-import java.util.TreeSet;
 
 /**
  * @author Myrle Krantz
@@ -37,8 +36,11 @@
 
     final BalanceSegmentSet balanceSegmentSet = new BalanceSegmentSet();
     balanceSegmentSet.setIdentifier(testEnvironment.generateUniqueIdentifer("bss"));
-    balanceSegmentSet.setSegments(new TreeSet<>(Arrays.asList(BigDecimal.TEN, BigDecimal.valueOf(10_000))));
-    balanceSegmentSet.setSegmentIdentifiers(Arrays.asList("a", "b", "c"));
+    balanceSegmentSet.setSegments(Arrays.asList(
+        BigDecimal.ZERO.setScale(4, BigDecimal.ROUND_HALF_EVEN),
+        BigDecimal.TEN.setScale(4, BigDecimal.ROUND_HALF_EVEN),
+        BigDecimal.valueOf(10_000_0000, 4)));
+    balanceSegmentSet.setSegmentIdentifiers(Arrays.asList("abc", "def", "ghi"));
 
     portfolioManager.createBalanceSegmentSet(product.getIdentifier(), balanceSegmentSet);
     Assert.assertTrue(this.eventRecorder.wait(EventConstants.POST_BALANCE_SEGMENT_SET, new BalanceSegmentSetEvent(product.getIdentifier(), balanceSegmentSet.getIdentifier())));
@@ -46,8 +48,11 @@
     final BalanceSegmentSet createdBalanceSegmentSet = portfolioManager.getBalanceSegmentSet(product.getIdentifier(), balanceSegmentSet.getIdentifier());
     Assert.assertEquals(balanceSegmentSet, createdBalanceSegmentSet);
 
-    balanceSegmentSet.setSegments(new TreeSet<>(Arrays.asList(BigDecimal.valueOf(100), BigDecimal.valueOf(10_000))));
-    balanceSegmentSet.setSegmentIdentifiers(Arrays.asList("a", "b", "c"));
+    balanceSegmentSet.setSegments(Arrays.asList(
+        BigDecimal.ZERO.setScale(4, BigDecimal.ROUND_HALF_EVEN),
+        BigDecimal.valueOf(100_0000, 4),
+        BigDecimal.valueOf(10_000_0000, 4)));
+    balanceSegmentSet.setSegmentIdentifiers(Arrays.asList("abc", "def", "ghi"));
 
     portfolioManager.changeBalanceSegmentSet(product.getIdentifier(), balanceSegmentSet.getIdentifier(), balanceSegmentSet);
     Assert.assertTrue(this.eventRecorder.wait(EventConstants.PUT_BALANCE_SEGMENT_SET, new BalanceSegmentSetEvent(product.getIdentifier(), balanceSegmentSet.getIdentifier())));
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/command/ChangeBalanceSegmentSetCommand.java b/service/src/main/java/io/mifos/portfolio/service/internal/command/ChangeBalanceSegmentSetCommand.java
new file mode 100644
index 0000000..19edb4c
--- /dev/null
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/command/ChangeBalanceSegmentSetCommand.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * 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.
+ */
+package io.mifos.portfolio.service.internal.command;
+
+import io.mifos.portfolio.api.v1.domain.BalanceSegmentSet;
+
+import java.util.Objects;
+
+/**
+ * @author Myrle Krantz
+ */
+public class ChangeBalanceSegmentSetCommand {
+  private final String productIdentifier;
+  private final BalanceSegmentSet instance;
+
+  public ChangeBalanceSegmentSetCommand(String productIdentifier, BalanceSegmentSet instance) {
+    this.productIdentifier = productIdentifier;
+    this.instance = instance;
+  }
+
+  public String getProductIdentifier() {
+    return productIdentifier;
+  }
+
+  public BalanceSegmentSet getInstance() {
+    return instance;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ChangeBalanceSegmentSetCommand that = (ChangeBalanceSegmentSetCommand) o;
+    return Objects.equals(productIdentifier, that.productIdentifier) &&
+        Objects.equals(instance, that.instance);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(productIdentifier, instance);
+  }
+
+  @Override
+  public String toString() {
+    return "ChangeBalanceSegmentSetCommand{" +
+        "productIdentifier='" + productIdentifier + '\'' +
+        ", instance=" + instance +
+        '}';
+  }
+}
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/command/CreateBalanceSegmentSetCommand.java b/service/src/main/java/io/mifos/portfolio/service/internal/command/CreateBalanceSegmentSetCommand.java
new file mode 100644
index 0000000..a5a4925
--- /dev/null
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/command/CreateBalanceSegmentSetCommand.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * 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.
+ */
+package io.mifos.portfolio.service.internal.command;
+
+import io.mifos.portfolio.api.v1.domain.BalanceSegmentSet;
+
+/**
+ * @author Myrle Krantz
+ */
+public class CreateBalanceSegmentSetCommand {
+  private final String productIdentifier;
+  private final BalanceSegmentSet instance;
+
+  public CreateBalanceSegmentSetCommand(String productIdentifier, BalanceSegmentSet instance) {
+    this.productIdentifier = productIdentifier;
+    this.instance = instance;
+  }
+
+  public String getProductIdentifier() {
+    return productIdentifier;
+  }
+
+  public BalanceSegmentSet getInstance() {
+    return instance;
+  }
+
+  @Override
+  public String toString() {
+    return "CreateBalanceSegmentSetCommand{" +
+        "productIdentifier='" + productIdentifier + '\'' +
+        ", instance=" + instance +
+        '}';
+  }
+}
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/command/DeleteBalanceSegmentSetCommand.java b/service/src/main/java/io/mifos/portfolio/service/internal/command/DeleteBalanceSegmentSetCommand.java
new file mode 100644
index 0000000..cb4ac07
--- /dev/null
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/command/DeleteBalanceSegmentSetCommand.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * 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.
+ */
+package io.mifos.portfolio.service.internal.command;
+
+/**
+ * @author Myrle Krantz
+ */
+public class DeleteBalanceSegmentSetCommand {
+  private final String productIdentifier;
+  private final String balanceSegmentSetIdentifier;
+
+  public DeleteBalanceSegmentSetCommand(String productIdentifier, String balanceSegmentSetIdentifier) {
+    this.productIdentifier = productIdentifier;
+    this.balanceSegmentSetIdentifier = balanceSegmentSetIdentifier;
+  }
+
+  public String getProductIdentifier() {
+    return productIdentifier;
+  }
+
+  public String getBalanceSegmentSetIdentifier() {
+    return balanceSegmentSetIdentifier;
+  }
+
+  @Override
+  public String toString() {
+    return "DeleteBalanceSegmentSetCommand{" +
+        "productIdentifier='" + productIdentifier + '\'' +
+        ", balanceSegmentSetIdentifier='" + balanceSegmentSetIdentifier + '\'' +
+        '}';
+  }
+}
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/BalanceSegmentSetCommandHandler.java b/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/BalanceSegmentSetCommandHandler.java
new file mode 100644
index 0000000..dd8a7a3
--- /dev/null
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/BalanceSegmentSetCommandHandler.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * 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.
+ */
+package io.mifos.portfolio.service.internal.command.handler;
+
+import io.mifos.core.command.annotation.Aggregate;
+import io.mifos.core.command.annotation.CommandHandler;
+import io.mifos.core.command.annotation.CommandLogLevel;
+import io.mifos.core.command.annotation.EventEmitter;
+import io.mifos.core.lang.ServiceException;
+import io.mifos.portfolio.api.v1.events.BalanceSegmentSetEvent;
+import io.mifos.portfolio.api.v1.events.EventConstants;
+import io.mifos.portfolio.service.internal.command.ChangeBalanceSegmentSetCommand;
+import io.mifos.portfolio.service.internal.command.CreateBalanceSegmentSetCommand;
+import io.mifos.portfolio.service.internal.command.DeleteBalanceSegmentSetCommand;
+import io.mifos.portfolio.service.internal.mapper.BalanceSegmentSetMapper;
+import io.mifos.portfolio.service.internal.repository.BalanceSegmentSetEntity;
+import io.mifos.portfolio.service.internal.repository.BalanceSegmentSetRepository;
+import io.mifos.portfolio.service.internal.repository.ProductEntity;
+import io.mifos.portfolio.service.internal.repository.ProductRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author Myrle Krantz
+ */
+@Aggregate
+public class BalanceSegmentSetCommandHandler {
+  private final BalanceSegmentSetRepository balanceSegmentSetRepository;
+  private final ProductRepository productRepository;
+
+  @Autowired
+  public BalanceSegmentSetCommandHandler(
+      final BalanceSegmentSetRepository balanceSegmentSetRepository,
+      final ProductRepository productRepository) {
+    this.balanceSegmentSetRepository = balanceSegmentSetRepository;
+    this.productRepository = productRepository;
+  }
+
+  @Transactional
+  @CommandHandler(logStart = CommandLogLevel.INFO, logFinish = CommandLogLevel.INFO)
+  @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.POST_BALANCE_SEGMENT_SET)
+  public BalanceSegmentSetEvent process(final CreateBalanceSegmentSetCommand createBalanceSegmentSetCommand) {
+    final ProductEntity product = productRepository.findByIdentifier(createBalanceSegmentSetCommand.getProductIdentifier())
+        .orElseThrow(() -> ServiceException.notFound("Product with identifier ''{0}'' doesn''t exist.", createBalanceSegmentSetCommand.getProductIdentifier()));
+
+    final List<BalanceSegmentSetEntity> balanceSegmentSetEntities = BalanceSegmentSetMapper.map(
+        createBalanceSegmentSetCommand.getInstance(), product);
+
+    balanceSegmentSetRepository.save(balanceSegmentSetEntities);
+
+    return new BalanceSegmentSetEvent(
+        createBalanceSegmentSetCommand.getProductIdentifier(),
+        createBalanceSegmentSetCommand.getInstance().getIdentifier());
+  }
+
+  @Transactional
+  @CommandHandler(logStart = CommandLogLevel.INFO, logFinish = CommandLogLevel.INFO)
+  @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.PUT_BALANCE_SEGMENT_SET)
+  public BalanceSegmentSetEvent process(final ChangeBalanceSegmentSetCommand changeBalanceSegmentSetCommand) {
+    final ProductEntity product = productRepository.findByIdentifier(changeBalanceSegmentSetCommand.getProductIdentifier())
+        .orElseThrow(() -> ServiceException.notFound("Product with identifier ''{0}'' doesn''t exist.", changeBalanceSegmentSetCommand.getProductIdentifier()));
+
+    final List<BalanceSegmentSetEntity> balanceSegmentSets = balanceSegmentSetRepository.findByProductIdentifierAndSegmentSetIdentifier(
+        changeBalanceSegmentSetCommand.getProductIdentifier(),
+        changeBalanceSegmentSetCommand.getInstance().getIdentifier())
+        .collect(Collectors.toList());
+    if (balanceSegmentSets.isEmpty())
+      throw ServiceException.notFound("Segment set with identifier ''{0}.{1}'' doesn''t exist.",
+          changeBalanceSegmentSetCommand.getProductIdentifier(),
+          changeBalanceSegmentSetCommand.getInstance().getIdentifier());
+
+    balanceSegmentSetRepository.deleteInBatch(balanceSegmentSets);
+
+    final List<BalanceSegmentSetEntity> balanceSegmentSetEntities = BalanceSegmentSetMapper.map(
+        changeBalanceSegmentSetCommand.getInstance(), product);
+
+    balanceSegmentSetRepository.save(balanceSegmentSetEntities);
+
+    return new BalanceSegmentSetEvent(
+        changeBalanceSegmentSetCommand.getProductIdentifier(),
+        changeBalanceSegmentSetCommand.getInstance().getIdentifier());
+  }
+
+  @Transactional
+  @CommandHandler(logStart = CommandLogLevel.INFO, logFinish = CommandLogLevel.INFO)
+  @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.DELETE_BALANCE_SEGMENT_SET)
+  public BalanceSegmentSetEvent process(final DeleteBalanceSegmentSetCommand deleteBalanceSegmentSetCommand) {
+    final List<BalanceSegmentSetEntity> balanceSegmentSets = balanceSegmentSetRepository.findByProductIdentifierAndSegmentSetIdentifier(
+        deleteBalanceSegmentSetCommand.getProductIdentifier(),
+        deleteBalanceSegmentSetCommand.getBalanceSegmentSetIdentifier())
+        .collect(Collectors.toList());
+    if (balanceSegmentSets.isEmpty())
+      throw ServiceException.notFound("Segment set with identifier ''{0}.{1}'' doesn''t exist.",
+          deleteBalanceSegmentSetCommand.getProductIdentifier(),
+          deleteBalanceSegmentSetCommand.getBalanceSegmentSetIdentifier());
+
+    balanceSegmentSetRepository.deleteInBatch(balanceSegmentSets);
+
+    return new BalanceSegmentSetEvent(
+        deleteBalanceSegmentSetCommand.getProductIdentifier(),
+        deleteBalanceSegmentSetCommand.getBalanceSegmentSetIdentifier());
+  }
+}
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/mapper/BalanceSegmentSetMapper.java b/service/src/main/java/io/mifos/portfolio/service/internal/mapper/BalanceSegmentSetMapper.java
new file mode 100644
index 0000000..c42dfd0
--- /dev/null
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/mapper/BalanceSegmentSetMapper.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * 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.
+ */
+package io.mifos.portfolio.service.internal.mapper;
+
+import io.mifos.portfolio.api.v1.domain.BalanceSegmentSet;
+import io.mifos.portfolio.service.internal.repository.BalanceSegmentSetEntity;
+import io.mifos.portfolio.service.internal.repository.ProductEntity;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * @author Myrle Krantz
+ */
+public class BalanceSegmentSetMapper {
+  public static List<BalanceSegmentSetEntity> map(final BalanceSegmentSet instance, final ProductEntity product) {
+    return
+        Stream.iterate(0, i -> i+1).limit(instance.getSegmentIdentifiers().size())
+            .map(i -> {
+              final BalanceSegmentSetEntity ret = new BalanceSegmentSetEntity();
+              ret.setProduct(product);
+              ret.setSegmentSetIdentifier(instance.getIdentifier());
+              ret.setSegmentIdentifier(instance.getSegmentIdentifiers().get(i));
+              ret.setLowerBound(instance.getSegments().get(i));
+              return ret;
+            })
+            .collect(Collectors.toList());
+  }
+
+  public static Optional<BalanceSegmentSet> map(final Stream<BalanceSegmentSetEntity> instances) {
+    final BalanceSegmentSet ret = new BalanceSegmentSet();
+    ret.setSegments(new ArrayList<>());
+    ret.setSegmentIdentifiers(new ArrayList<>());
+    instances.sorted(Comparator.comparing(BalanceSegmentSetEntity::getLowerBound))
+        .forEach(seg -> {
+          ret.setIdentifier(seg.getSegmentSetIdentifier());
+          ret.getSegments().add(seg.getLowerBound());
+          ret.getSegmentIdentifiers().add(seg.getSegmentIdentifier());
+        });
+    if (ret.getSegments().isEmpty())
+      return Optional.empty();
+    else
+      return Optional.of(ret);
+  }
+}
\ No newline at end of file
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/repository/BalanceSegmentSetEntity.java b/service/src/main/java/io/mifos/portfolio/service/internal/repository/BalanceSegmentSetEntity.java
new file mode 100644
index 0000000..ee8c5c2
--- /dev/null
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/repository/BalanceSegmentSetEntity.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * 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.
+ */
+package io.mifos.portfolio.service.internal.repository;
+
+import javax.persistence.*;
+import java.math.BigDecimal;
+import java.util.Objects;
+
+/**
+ * @author Myrle Krantz
+ */
+@Entity
+@Table(name = "bastet_p_balance_segs")
+public class BalanceSegmentSetEntity {
+  @Id
+  @GeneratedValue(strategy = GenerationType.IDENTITY)
+  @Column(name = "id")
+  private Long id;
+
+  @ManyToOne(fetch = FetchType.LAZY)
+  @JoinColumn(name = "product_id", nullable = false)
+  private ProductEntity product;
+
+  @Column(name = "seg_set_identifier", nullable = false)
+  private String segmentSetIdentifier;
+
+  @Column(name = "segment_identifier", nullable = false)
+  private String segmentIdentifier;
+
+  @Column(name = "lower_bound")
+  private BigDecimal lowerBound;
+
+  public BalanceSegmentSetEntity() {
+  }
+
+  public Long getId() {
+    return id;
+  }
+
+  public void setId(Long id) {
+    this.id = id;
+  }
+
+  public ProductEntity getProduct() {
+    return product;
+  }
+
+  public void setProduct(ProductEntity product) {
+    this.product = product;
+  }
+
+  public String getSegmentSetIdentifier() {
+    return segmentSetIdentifier;
+  }
+
+  public void setSegmentSetIdentifier(String segmentSetIdentifier) {
+    this.segmentSetIdentifier = segmentSetIdentifier;
+  }
+
+  public String getSegmentIdentifier() {
+    return segmentIdentifier;
+  }
+
+  public void setSegmentIdentifier(String segmentIdentifier) {
+    this.segmentIdentifier = segmentIdentifier;
+  }
+
+  public BigDecimal getLowerBound() {
+    return lowerBound;
+  }
+
+  public void setLowerBound(BigDecimal lowerBound) {
+    this.lowerBound = lowerBound;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    BalanceSegmentSetEntity that = (BalanceSegmentSetEntity) o;
+    return Objects.equals(product, that.product) &&
+        Objects.equals(segmentSetIdentifier, that.segmentSetIdentifier) &&
+        Objects.equals(segmentIdentifier, that.segmentIdentifier);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(product, segmentSetIdentifier, segmentIdentifier);
+  }
+}
\ No newline at end of file
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/repository/BalanceSegmentSetRepository.java b/service/src/main/java/io/mifos/portfolio/service/internal/repository/BalanceSegmentSetRepository.java
new file mode 100644
index 0000000..9aac27d
--- /dev/null
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/repository/BalanceSegmentSetRepository.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * 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.
+ */
+package io.mifos.portfolio.service.internal.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.stream.Stream;
+
+/**
+ * @author Myrle Krantz
+ */
+@Repository
+public interface BalanceSegmentSetRepository extends JpaRepository<BalanceSegmentSetEntity, Long> {
+  Stream<BalanceSegmentSetEntity> findByProductIdentifierAndSegmentSetIdentifier(String productIdentifier, String segmentSetIdentifier);
+}
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/service/BalanceSegmentSetService.java b/service/src/main/java/io/mifos/portfolio/service/internal/service/BalanceSegmentSetService.java
new file mode 100644
index 0000000..451a57e
--- /dev/null
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/service/BalanceSegmentSetService.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * 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.
+ */
+package io.mifos.portfolio.service.internal.service;
+
+import io.mifos.portfolio.api.v1.domain.BalanceSegmentSet;
+import io.mifos.portfolio.service.internal.mapper.BalanceSegmentSetMapper;
+import io.mifos.portfolio.service.internal.repository.BalanceSegmentSetRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Optional;
+
+/**
+ * @author Myrle Krantz
+ */
+@Service
+public class BalanceSegmentSetService {
+  private final BalanceSegmentSetRepository balanceSegmentSetRepository;
+
+  @Autowired
+  public BalanceSegmentSetService(
+      final BalanceSegmentSetRepository balanceSegmentSetRepository) {
+    this.balanceSegmentSetRepository = balanceSegmentSetRepository;
+  }
+
+  public boolean existsByIdentifier(final String productIdentifier, final String balanceSegmentSetIdentifier)
+  {
+    //TODO: replace with existsBy once we've upgraded to spring data 1.11 or later.
+    return balanceSegmentSetRepository
+        .findByProductIdentifierAndSegmentSetIdentifier(productIdentifier, balanceSegmentSetIdentifier)
+        .findAny().isPresent();
+  }
+
+  public Optional<BalanceSegmentSet> findByIdentifier(
+      final String productIdentifier,
+      final String balanceSegmentSetIdentifier) {
+    return BalanceSegmentSetMapper.map(balanceSegmentSetRepository
+        .findByProductIdentifierAndSegmentSetIdentifier(productIdentifier, balanceSegmentSetIdentifier));
+  }
+}
diff --git a/service/src/main/java/io/mifos/portfolio/service/rest/BalanceSegmentSetRestController.java b/service/src/main/java/io/mifos/portfolio/service/rest/BalanceSegmentSetRestController.java
new file mode 100644
index 0000000..a5ca9cc
--- /dev/null
+++ b/service/src/main/java/io/mifos/portfolio/service/rest/BalanceSegmentSetRestController.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * 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.
+ */
+package io.mifos.portfolio.service.rest;
+
+import io.mifos.anubis.annotation.AcceptedTokenType;
+import io.mifos.anubis.annotation.Permittable;
+import io.mifos.core.command.gateway.CommandGateway;
+import io.mifos.core.lang.ServiceException;
+import io.mifos.portfolio.api.v1.PermittableGroupIds;
+import io.mifos.portfolio.api.v1.domain.BalanceSegmentSet;
+import io.mifos.portfolio.service.internal.command.ChangeBalanceSegmentSetCommand;
+import io.mifos.portfolio.service.internal.command.CreateBalanceSegmentSetCommand;
+import io.mifos.portfolio.service.internal.command.DeleteBalanceSegmentSetCommand;
+import io.mifos.portfolio.service.internal.service.BalanceSegmentSetService;
+import io.mifos.portfolio.service.internal.service.CaseService;
+import io.mifos.portfolio.service.internal.service.ProductService;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+
+/**
+ * @author Myrle Krantz
+ */
+@RestController
+@RequestMapping("/products/{productidentifier}/balancesegmentsets/")
+public class BalanceSegmentSetRestController {
+
+  private final CommandGateway commandGateway;
+  private final ProductService productService;
+  private final BalanceSegmentSetService balanceSegmentSetService;
+  private final CaseService caseService;
+
+  public BalanceSegmentSetRestController(final CommandGateway commandGateway,
+                                         final ProductService productService,
+                                         final BalanceSegmentSetService balanceSegmentSetService,
+                                         final CaseService caseService) {
+    this.commandGateway = commandGateway;
+    this.productService = productService;
+    this.balanceSegmentSetService = balanceSegmentSetService;
+    this.caseService = caseService;
+  }
+
+  @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.PRODUCT_MANAGEMENT)
+  @RequestMapping(
+      method = RequestMethod.POST,
+      consumes = MediaType.APPLICATION_JSON_VALUE,
+      produces = MediaType.APPLICATION_JSON_VALUE
+  )
+  public @ResponseBody
+  ResponseEntity<Void> createBalanceSegmentSet(
+      @PathVariable("productidentifier") final String productIdentifier,
+      @RequestBody @Valid final BalanceSegmentSet instance) {
+    checkThatProductExists(productIdentifier);
+    checkThatSegmentSetDoesntExist(productIdentifier, instance.getIdentifier());
+    checkProductChangeable(productIdentifier);
+
+    this.commandGateway.process(new CreateBalanceSegmentSetCommand(productIdentifier, instance));
+    return new ResponseEntity<>(HttpStatus.ACCEPTED);
+  }
+
+  @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.PRODUCT_MANAGEMENT)
+  @RequestMapping(
+      value = "{balancesegmentsetidentifier}",
+      method = RequestMethod.GET,
+      consumes = MediaType.ALL_VALUE,
+      produces = MediaType.APPLICATION_JSON_VALUE)
+  public @ResponseBody BalanceSegmentSet getBalanceSegmentSet(
+      @PathVariable("productidentifier") final String productIdentifier,
+      @PathVariable("balancesegmentsetidentifier") final String balanceSegmentSetIdentifier) {
+    return balanceSegmentSetService.findByIdentifier(productIdentifier, balanceSegmentSetIdentifier)
+        .orElseThrow(() -> ServiceException.notFound(
+            "Segment set with identifier ''{0}.{1}'' doesn''t exist.", productIdentifier, balanceSegmentSetIdentifier));
+  }
+
+  @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.PRODUCT_MANAGEMENT)
+  @RequestMapping(
+      value = "{balancesegmentsetidentifier}",
+      method = RequestMethod.PUT,
+      consumes = MediaType.ALL_VALUE,
+      produces = MediaType.APPLICATION_JSON_VALUE)
+  public @ResponseBody ResponseEntity<Void>  changeBalanceSegmentSet(
+      @PathVariable("productidentifier") final String productIdentifier,
+      @PathVariable("balancesegmentsetidentifier") final String balanceSegmentSetIdentifier,
+      @RequestBody @Valid BalanceSegmentSet balanceSegmentSet) {
+    checkThatProductAndBalanceSegmentSetExist(productIdentifier, balanceSegmentSetIdentifier);
+    checkProductChangeable(productIdentifier);
+
+    if (!balanceSegmentSetIdentifier.equals(balanceSegmentSet.getIdentifier()))
+      throw ServiceException.badRequest("Instance identifier may not be changed.");
+
+    this.commandGateway.process(new ChangeBalanceSegmentSetCommand(productIdentifier, balanceSegmentSet));
+    return new ResponseEntity<>(HttpStatus.ACCEPTED);
+  }
+
+  @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.PRODUCT_MANAGEMENT)
+  @RequestMapping(
+      value = "{balancesegmentsetidentifier}",
+      method = RequestMethod.DELETE,
+      consumes = MediaType.ALL_VALUE,
+      produces = MediaType.APPLICATION_JSON_VALUE
+  )
+  public @ResponseBody ResponseEntity<Void>  deleteBalanceSegmentSet(
+      @PathVariable("productidentifier") final String productIdentifier,
+      @PathVariable("balancesegmentsetidentifier") final String balanceSegmentSetIdentifier) {
+    checkThatProductAndBalanceSegmentSetExist(productIdentifier, balanceSegmentSetIdentifier);
+    checkProductChangeable(productIdentifier);
+
+    this.commandGateway.process(new DeleteBalanceSegmentSetCommand(productIdentifier, balanceSegmentSetIdentifier));
+    return new ResponseEntity<>(HttpStatus.ACCEPTED);
+  }
+
+  private void checkThatProductExists(final String productIdentifier) {
+    if (!productService.existsByIdentifier(productIdentifier))
+      throw ServiceException.notFound("Product with identifier ''{0}'' doesn''t exist.", productIdentifier);
+  }
+
+  private void checkThatSegmentSetDoesntExist(final String productIdentifier, final String segmentSetIdentifier) {
+    if (balanceSegmentSetService.existsByIdentifier(productIdentifier, segmentSetIdentifier))
+      throw ServiceException.notFound("Segment set with identifier ''{0}.{1}'' already exists.", productIdentifier, segmentSetIdentifier);
+  }
+
+  private void checkThatProductAndBalanceSegmentSetExist(final String productIdentifier, final String segmentSetIdentifier) {
+    if (!balanceSegmentSetService.existsByIdentifier(productIdentifier, segmentSetIdentifier))
+      throw ServiceException.notFound("Segment set with identifier ''{0}.{1}'' doesn''t exist.", productIdentifier, segmentSetIdentifier);
+  }
+
+  private void checkProductChangeable(final String productIdentifier) {
+    if (caseService.existsByProductIdentifier(productIdentifier))
+      throw ServiceException.conflict("Cases exist for product with the identifier ''{0}''. Product cannot be changed.", productIdentifier);
+  }
+}
diff --git a/service/src/main/resources/db/migrations/mariadb/V7__balance_segment_sets.sql b/service/src/main/resources/db/migrations/mariadb/V7__balance_segment_sets.sql
new file mode 100644
index 0000000..2cd41a5
--- /dev/null
+++ b/service/src/main/resources/db/migrations/mariadb/V7__balance_segment_sets.sql
@@ -0,0 +1,26 @@
+--
+-- Copyright 2017 Kuelap, Inc.
+--
+-- 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.
+--
+
+CREATE TABLE bastet_p_balance_segs (
+  id BIGINT NOT NULL AUTO_INCREMENT,
+  seg_set_identifier       VARCHAR(32)    NOT NULL,
+  segment_identifier       VARCHAR(32)    NOT NULL,
+  product_id               BIGINT         NOT NULL,
+  lower_bound              DECIMAL(19,4)  NOT NULL,
+  CONSTRAINT bastet_p_balance_segs_pk PRIMARY KEY (id),
+  CONSTRAINT bastet_p_balance_segs_uq UNIQUE (product_id, seg_set_identifier, segment_identifier),
+  CONSTRAINT bastet_p_balance_segs_fk FOREIGN KEY (product_id) REFERENCES bastet_products (id)
+);
\ No newline at end of file