v6.0.0 Release Notes

Sep 23, 2020 • Stefan Kapferer

Today we released Context Mapper 6.0.0 🥳

With this release we mainly improved the systematic service decomposition approach with Service Cutter and adjusted the CML grammar to achieve that. We also added some semantic validators that help to identify ambiguous references and/or references that target objects in non-reachable Bounded Contexts. In addition, we improved the Context Mapper API for standalone applications to ease the usage of Context Mapper as a library.

Summary of Changes

  • CML Adjustments
    • Use case and user story grammar: reads and writes keyword have been removed.
    • New Aggregate grammar features support users in classifying domain parts according to the Service Cutter user representations. Therefore, the SCL files must no longer be modified manually (we generate it automatically).
  • Service Cut Generator Improvements
    • Improved traceability of generated service cut’s.
    • New refactoring (model transformation) that allows users to extract a suggested service in the original CML model.
  • New Semantic Validators
    • Support users in detecting ambiguous references if domain objects have duplicate names.
    • Warn users when references target domain objects in other Bounded Contexts that are not reachable (no Context Map relationship exists).
  • Standalone API Improvements

As always, if you have any issues or other feedback, please let us know.

CML Adjustments

This is a major release, since we adjusted the CML grammar at some points to improve the Service Cutter integration (some language changes break the backwards compatibility).

Use Cases and User Stories

With the first integration of Service Cutter into Context Mapper, we originally added the reads and writes keywords which allowed users to specify the read and written nanoentities in use cases:

UseCase BookCargo {
  reads "Location.unLocode"
  writes "Cargo.trackingId", "RouteSpecification.origin", "RouteSpecification.arrivalDeadline", "RouteSpecification.destination"
}
Note: The example above no longer compiles in CML!

Since we improved the grammar for user stories and use cases with our latest releases, the reads and writes directives no longer made sence. One can express the same thing with our new language features as follows:

UseCase BookCargo {
  interactions
    read "Location" with its "unLocode",
    update "Cargo" with its "trackingId",
    update "RouteSpecification" with its "origin", "arrivalDeadline", "destination"
}

For this reason, we removed the reads and writes keywords/directives (first listing above no longer compiles). Our Service Cutter tools use the information provided by the new CML syntax now.

With our documentation page you can find out how you can express the same thing as user story instead of a use case.

Aggregate Classifications for Service Cutter

The Service Cutter tool takes so-called user representations as input. Those are very important to generate reasonable service cut’s. Our solution to propose service cut’s in Context Mapper provides a DSL (the Service Cutter Configuration DSL: SCL) that allows users to specify these user representations.

Up to now, users had to specify their user representations in such SCL files manually. With this release we support CML features that allow you to model all required information in CML directly. The SCL file can then be generated automatically and users do not have to modify it manually.

The Service Cutter tutorial explains how all the user representations of Service Cutter can be modeled in CML.

The CML grammar for Aggregates was adjusted so that they can be classified according to Service Cutter’s criteria. All changes are documented in the language reference. Additional features are:

The following example summarizes the new grammar features to classify Aggregates according to the Service Cutter criteria:

BoundedContext DemoContext {
  Aggregate AggregateDemo1 {
    securityZone "Zone1"
    securityAccessGroup "Customers"
    
    availabilityCriticality HIGH
    consistencyCriticality NORMAL
    contentVolatility OFTEN
    structuralVolatility NORMAL
    securityCriticality HIGH
    storageSimilarity NORMAL

    Entity DemoEntityOne
  }

  Aggregate AggregateDemo2 {
    securityZone "Zone2"
    securityAccessGroup "Employees"
    
    availabilityCriticality NORMAL
    consistencyCriticality NORMAL
    contentVolatility NORMAL
    structuralVolatility NORMAL
    securityCriticality NORMAL
    storageSimilarity NORMAL
    
    Entity DemoEntityTwo
  }
}

Service Cut Generator Improvements

We improved the traceability of our systematic service decomposition approach and implemented a refactoring that allows users to extract a suggested service of a generated service cut automatically.

We adjusted our tutorial accordingly. You basically find all changes there. In summary, we made the following changes:

  • Better documentation for the .servicecutter.yml file (can be found here).
  • SCL (Service Cutter user representations) file is generated fully automatically now!
  • The generated CML files contain a dump of the used configurations and scorings (traceability).
    • An example can be found here.
  • Besides the actual CML file, we generate a Graphviz DOT file (*.gv) representing the internal graph used by Service Cutter (traceability).
    • Allows you to analyze the impact of changed scorings on the actual graph behind the scenes.
    • Visualizing the graph graphically can also help understanding the clustering algorithms results (works only for small examples, as the graphs get huge quickly).
    • This feature is also explained in our tutorial now.
  • A new refactoring allows you to extract a service of a suggested service cut in the original CML model.

New Semantic Validators

We added a few new semantic validators that warn users about issues that raise with duplicate domain object names. This might not be problematic while modeling, but can limit the potential of our model transformations and refactorings.

  • A warning indicates when a domain object name is used twice.
  • A warning indicates if a reference targets a domain object name that is used twice.
    • In this case it is an ambiguous reference, as the user cannot control which domain object shall actually be referenced.

In addition, we warn the user if he references a domain object that is part of another Bounded Context that is not reachable. With not reachable we mean that there is no Context Map relationship that would allow the access to the referenced object. For example, CML produces a warning for the following customer reference:

ContextMap {
  contains CustomerContext, AddressContext
  // no relationship
}

BoundedContext CustomerContext {
  Aggregate Customer {
    Entity Customer {
      aggregateRoot
      String firstName
      String lastName
    }
  }
}

BoundedContext AddressContext {
  Aggregate Address {
    Entity Address {
      aggregateRoot
      String street
      String city
      - Customer customer // produces warning, as the CustomerContext is not reachable
    }
  }
}

The user can solve this issue by creating a relationship between the contexts that make the referenced object reachable:

ContextMap {
  contains CustomerContext, AddressContext

  CustomerContext -> AddressContext
}

BoundedContext CustomerContext {
  Aggregate Customer {
    Entity Customer {
      aggregateRoot
      String firstName
      String lastName
    }
  }
}

BoundedContext AddressContext {
  Aggregate Address {
    Entity Address {
      aggregateRoot
      String street
      String city
      - Customer customer
    }
  }
}

Note: In case you declare the exposed Aggregates in the relationship, you only make the object available if it is part of the Aggregates that are exposed accordingly.

Standalone API Improvements

As documented in the following GitHub issue, the API for the usage of Context Mapper as a library was not optimal so far: https://github.com/ContextMapper/context-mapper-dsl/issues/243

We improved the API so that common use cases (such as reading models, calling generators, etc.) can be implemented as easy as possible. The following code samples give you an impression of the new API.

Read a model:

public static void main(String[] args){
  StandaloneContextMapperAPI contextMapper = ContextMapperStandaloneSetup.getStandaloneAPI();
  CMLResource cml = contextMapper.loadCML("./src/main/cml/hello-world.cml");

  // read the model:
  ContextMappingModel model = cml.getContextMappingModel();

  // process the model as you like ...
}

Call a generator:

public static void main(String[] args){
  // init Context Mapper
  StandaloneContextMapperAPI contextMapper = ContextMapperStandaloneSetup.getStandaloneAPI();
  
  // load CML model
  CMLResource cml = contextMapper.loadCML("./src/main/cml/simple-context-map.cml");
  
  // load and call generator
  ContextMapGenerator generator = new ContextMapGenerator();
  contextMapper.callGenerator(cml, generator);
}

Call a refactoring:

public static void main(String[] args){
  // init Context Mapper
  StandaloneContextMapperAPI contextMapper = ContextMapperStandaloneSetup.getStandaloneAPI();
  
  // load CML model
  CMLResource cml = contextMapper.loadCML("./src/main/cml/insurance-sample.cml");
  
  // load and call refactoring
  contextMapper.applyRefactoring(cml, new SplitBoundedContextByOwner("PolicyManagementContext"));
}

Thats it for this new release of Context Mapper. As always, if you have any issues or other feedback, please let us know.