diff --git a/org.contextmapper.dsl.tests/integ-test-files/refactorings/merge-bounded-contexts-test-12-input.cml b/org.contextmapper.dsl.tests/integ-test-files/refactorings/merge-bounded-contexts-test-12-input.cml new file mode 100644 index 00000000..09670211 --- /dev/null +++ b/org.contextmapper.dsl.tests/integ-test-files/refactorings/merge-bounded-contexts-test-12-input.cml @@ -0,0 +1,9 @@ +BoundedContext TicketSales { + responsibilities = "Selling fare products", "Refunds" + nonResponsibilities = "Gate validation", "Support cases" +} + +BoundedContext CustomerCare { + responsibilities = "Support cases" + nonResponsibilities = "Payment processing", "Gate validation" +} diff --git a/org.contextmapper.dsl.tests/src/org/contextmapper/dsl/BoundedContextDSLParsingTest.xtend b/org.contextmapper.dsl.tests/src/org/contextmapper/dsl/BoundedContextDSLParsingTest.xtend index 7ec9b6ca..0c00b5e0 100644 --- a/org.contextmapper.dsl.tests/src/org/contextmapper/dsl/BoundedContextDSLParsingTest.xtend +++ b/org.contextmapper.dsl.tests/src/org/contextmapper/dsl/BoundedContextDSLParsingTest.xtend @@ -258,6 +258,43 @@ class BoundedContextDSLParsingTest { String.format(ONLY_TEAMS_CAN_REALIZE_OTHER_BOUNDED_CONTEXT, "NotATeam")); } + @Test + def void canDefineNonResponsibilities() { + // given + val String dslSnippet = ''' + BoundedContext testContext { + responsibilities = "Selling fare products", "Refunds" + nonResponsibilities = "Gate validation", "Support cases" + } + '''; + // when + val ContextMappingModel result = parseHelper.parse(dslSnippet); + // then + assertThatNoParsingErrorsOccurred(result); + assertThatNoValidationErrorsOccurred(result); + val nonResponsibilities = result.boundedContexts.get(0).nonResponsibilities; + assertEquals(2, nonResponsibilities.size); + assertEquals("Gate validation", nonResponsibilities.get(0)); + assertEquals("Support cases", nonResponsibilities.get(1)); + } + + @Test + def void canDefineNonResponsibilitiesWithoutEqualSign() { + // given + val String dslSnippet = ''' + BoundedContext testContext { + nonResponsibilities "Gate validation" + } + '''; + // when + val ContextMappingModel result = parseHelper.parse(dslSnippet); + // then + assertThatNoParsingErrorsOccurred(result); + assertThatNoValidationErrorsOccurred(result); + assertEquals(1, result.boundedContexts.get(0).nonResponsibilities.size); + assertEquals("Gate validation", result.boundedContexts.get(0).nonResponsibilities.get(0)); + } + @Test def void canDefineAttributesWithoutEqualSign() { // given diff --git a/org.contextmapper.dsl.tests/src/org/contextmapper/dsl/refactoring/MergeBoundedContextsTest.java b/org.contextmapper.dsl.tests/src/org/contextmapper/dsl/refactoring/MergeBoundedContextsTest.java index 4716b608..5ffefd5e 100644 --- a/org.contextmapper.dsl.tests/src/org/contextmapper/dsl/refactoring/MergeBoundedContextsTest.java +++ b/org.contextmapper.dsl.tests/src/org/contextmapper/dsl/refactoring/MergeBoundedContextsTest.java @@ -196,6 +196,24 @@ void canMergeImplementationTechnology() throws IOException { assertEquals("Java, Scala", bc.getImplementationTechnology()); } + @Test + void canMergeNonResponsibilities() throws IOException { + // given + String inputModelName = "merge-bounded-contexts-test-12-input.cml"; + CMLResource input = getResourceCopyOfTestCML(inputModelName); + MergeBoundedContextsRefactoring refactoring = new MergeBoundedContextsRefactoring("TicketSales", "CustomerCare"); + + // when + refactoring.refactor(input); + + // then + ContextMappingModel model = input.getContextMappingModel(); + assertEquals(1, model.getBoundedContexts().size()); + BoundedContext bc = model.getBoundedContexts().get(0); + assertEquals(3, bc.getResponsibilities().size()); + assertEquals(List.of("Gate validation", "Payment processing"), bc.getNonResponsibilities()); + } + @Test void canMergeEmptyImplementationTechnologies() throws IOException { // given diff --git a/org.contextmapper.dsl/src/org/contextmapper/dsl/ContextMappingDSL.xtext b/org.contextmapper.dsl/src/org/contextmapper/dsl/ContextMappingDSL.xtext index 28786cb6..0ad4f58a 100644 --- a/org.contextmapper.dsl/src/org/contextmapper/dsl/ContextMappingDSL.xtext +++ b/org.contextmapper.dsl/src/org/contextmapper/dsl/ContextMappingDSL.xtext @@ -58,6 +58,7 @@ BoundedContext: (('domainVisionStatement' ('=')? domainVisionStatement=STRING)? & ('type' ('=')? type=BoundedContextType)? & (('responsibilities' ('=')? responsibilities+=STRING) ("," responsibilities+=STRING)*)? & + (('nonResponsibilities' ('=')? nonResponsibilities+=STRING) ("," nonResponsibilities+=STRING)*)? & ('implementationTechnology' ('=')? implementationTechnology=STRING)? & ('knowledgeLevel' ('=')? knowledgeLevel=KnowledgeLevel)?) ('businessModel' ('=')? businessModel=STRING)? & diff --git a/org.contextmapper.dsl/src/org/contextmapper/dsl/formatting2/ContextMappingDSLFormatter.xtend b/org.contextmapper.dsl/src/org/contextmapper/dsl/formatting2/ContextMappingDSLFormatter.xtend index b7265896..20ca810d 100644 --- a/org.contextmapper.dsl/src/org/contextmapper/dsl/formatting2/ContextMappingDSLFormatter.xtend +++ b/org.contextmapper.dsl/src/org/contextmapper/dsl/formatting2/ContextMappingDSLFormatter.xtend @@ -120,6 +120,7 @@ class ContextMappingDSLFormatter extends TacticDDDLanguageFormatter { boundedContext.regionFor.keyword("domainVisionStatement").prepend[newLine] boundedContext.regionFor.keyword("type").prepend[newLine] boundedContext.regionFor.keyword("responsibilities").prepend[newLine] + boundedContext.regionFor.keyword("nonResponsibilities").prepend[newLine] boundedContext.regionFor.keyword("implementationTechnology").prepend[newLine] boundedContext.regionFor.keyword("knowledgeLevel").prepend[newLine] diff --git a/org.contextmapper.dsl/src/org/contextmapper/dsl/refactoring/MergeBoundedContextsRefactoring.java b/org.contextmapper.dsl/src/org/contextmapper/dsl/refactoring/MergeBoundedContextsRefactoring.java index d3fe1617..ab98347a 100644 --- a/org.contextmapper.dsl/src/org/contextmapper/dsl/refactoring/MergeBoundedContextsRefactoring.java +++ b/org.contextmapper.dsl/src/org/contextmapper/dsl/refactoring/MergeBoundedContextsRefactoring.java @@ -77,6 +77,7 @@ protected void doRefactor() { if (bc1.getType().equals(BoundedContextType.TEAM)) bc1.getRealizedBoundedContexts().addAll(bc2.getRealizedBoundedContexts()); bc1.getResponsibilities().addAll(bc2.getResponsibilities()); + mergeNonResponsibilities(bc1, bc2); bc1.setImplementationTechnology(mergeImplementationTechnologies(bc1.getImplementationTechnology(), bc2.getImplementationTechnology())); // remove BC2 @@ -91,6 +92,15 @@ protected void doRefactor() { bc2Model.setTopComment("// Due to the application of 'Merge Bounded Contexts' this file no longer contains any Bounded Contexts."); } + private void mergeNonResponsibilities(BoundedContext bc1, BoundedContext bc2) { + for (String nonResponsibility : bc2.getNonResponsibilities()) { + if (!bc1.getNonResponsibilities().contains(nonResponsibility)) + bc1.getNonResponsibilities().add(nonResponsibility); + } + // the merged context is responsible for everything one of the two contexts was responsible for + bc1.getNonResponsibilities().removeAll(bc1.getResponsibilities()); + } + private String mergeImplementationTechnologies(String implementationTechnology1, String implementationTechnology2) { if (implementationTechnology1 == null && implementationTechnology2 == null) return null;