Systematic Service Decomposition with Context Mapper and Service Cutter

Context Mapper provides a generator that decomposes your domain model into Bounded Contexts in a systematic manner. The service decomposition tool is based on Service Cutter. Based on a catalog of 16 coupling criteria and a graph clustering algorithm, the generator suggests how an application could be decomposed into Bounded Contexts, services, or components.

Note that it is not our goal to automate the job of domain modelers and software architects! The generated decompositions are just suggestions and can give you hints how your domain objects are coupled. Don’t expect that the perfect decomposition is generated for you without questioning it.

This tutorial illustrates how you can use Service Cutter inside Context Mapper, or export your domain model for Service Cutter out of a CML file.

The Domain Model

We use the DDD sample application (Cargo Tracking) for this tutorial. As a first step, we modeled the domain of the application in CML. You can find the model here.

We modeled the domain inside one single Bounded Context. The context contains four Aggregates with its Entities and Value Objects:

BoundedContext CargoTracking {
  Aggregate Cargo {
    owner CargoPlaner

    Entity Cargo {
      aggregateRoot
      - TrackingId trackingId
      - Location origin
      - RouteSpecification routeSpecification
      - Itinerary itinerary
      - Delivery delivery
    }
    /* shortened Aggregate here */
  }
  Aggregate Location {
    owner Administrators

    Entity Location {
      aggregateRoot
      - UnLocode unLocode
      String name
    }
    /* shortened Aggregate here */
  }
  Aggregate Handling {
    owner CargoTracker

    DomainEvent HandlingEvent {
      - HandlingEventType handlingEventType
      - Voyage voyage
      - Location location
      Date completionTime
      Date registrationTime
      - Cargo cargo
    }
    /* shortened Aggregate here */
  }
  Aggregate Voyage {
    owner VoyageManager

    Entity Voyage {
      aggregateRoot
      - VoyageNumber voyageNumber
      - Schedule schedule
    }
    /* shortened Aggregate here */
  }
}

The CML code above just gives you an impression how the model looks like but is shortened a lot. Find the complete model here. The following PlantUML diagram (generated with Context Mapper) illustrates the domain model graphically:

DDD Sample Application Domain Model (PlantUML generated with Context Mapper)

Use Case Modeling

With a Bounded Context definition as the one above you are already able to generate new service decompositions or Service Cutter files. However, we highly recommend the model your use cases as well, since they have a big impact on the ideal service decomposition.

On the User Requirements page of our language reference you can find out how use cases or user stories are modeled in CML. For this tutorial based on the DDD sample application we modeled the use cases provided by the Service Cutter sample files:

UseCase ViewTracking {
  reads "Cargo.trackingId", "HandlingEvent.type", "HandlingEvent.location", "HandlingEvent.completionTime", "Delivery.transportStatus", "Delivery.estimatedArrivalTime", "Delivery.misdirected", "Voyage.voyageNumber", "RouteSpecification.destination"
}

UseCase ViewCargos {
  reads "Cargo.trackingId", "RouteSpecification.destination", "RouteSpecification.arrivalDeadline", "Delivery.routingStatus", "Itinerary.itineraryNumber"
}

UseCase BookCargo {
  reads "Location.unLocode"
  writes "Cargo.trackingId", "RouteSpecification.origin", "RouteSpecification.arrivalDeadline", "RouteSpecification.destination"
}

UseCase ChangeCargoDestination {
  reads "Cargo.trackingId", "RouteSpecification.destination"
  writes "RouteSpecification.destination"
}

/* we shortened this listing to save space (find all use cases in the original CML file) */

Note that we only modeled the read and written nanoentities here. You can model the user requirements in much more detail in CML, but these two attributes are necessary for the Service Cutter generators and tools.

The CML use cases or user stories will be mapped to the Service Cutter’s Use Case definition.

Define Owners (Teams)

A Bounded Contexts is not necessarily a system or component. A team can constitute a Bounded Context as well. If you decompose a system you should respect existing teams (code and domain model owners) as well, since they have an influence to the coupling. In CML you can assign owners on the level of Aggregates.

This might not make sense for the DDD sample application, as the domain model is not that big. However, we assigned the Aggregates to four different teams to illustrate how this is done:

BoundedContext CargoTracking {
  Aggregate Cargo {
    owner CargoPlaner

    /* removed content here to save space */
  }
  Aggregate Location {
    owner Administrators

    /* removed content here to save space */
  }
  Aggregate Handling {
    owner CargoTracker

    /* removed content here to save space */ 
  }
  Aggregate Voyage {
    owner VoyageManager

    /* removed content here to save space */
  }
}

/* team definitions: */
BoundedContext CargoPlaner { type TEAM }
BoundedContext CargoTracker { type TEAM }
BoundedContext VoyageManager { type TEAM }
BoundedContext Administrators { type TEAM }

The CML team assignments will be mapped to the Service Cutter’s Shared Owner Group definition.

Additional User Representations

Besides Use Cases and Shared Owner Groups Service Cutter uses additional user representations that may influence the coupling between your model elements. This is the list of all supported user representations:

  • Entity Relationship Model (ERM)
  • Use Cases
  • Shared Owner Groups
  • Aggregates
  • Entities
  • Predefined Services
  • Separated Security Zones
  • Security Access Groups
  • Compatibilities

Most of the user representations we can now derive from our CML model. The ERM is derived from the Entities and their references in CML. As already explained above, the Use Cases and Shared Owner Groups are modeled in CML as well. We further derive the Aggregates and Entities for Service Cutter from the corresponding counterparts in CML. In addition, we derive Predefined Services from already existing Bounded Contexts of your CML model. The remaining user representations have to be added manually, in case they are relevant for your application.

The Service Cutter Configuration DSL (SCL)

The Service Cutter tool takes the user representations as a JSON file. In order to ease the description of these representations we created another DSL. Once you created a CML model, you can easily derive an initial SCL file from it. You find two SCL generators in Context Mapper:

SCL Generators in Context Mapper

The first generator (“Generate Service Cutter User Representations (SCL)”) generates an SCL file that contains the user representations which can be derived from your CML model. You can enhance this file with additional user representations. However, the representations that can be derived from CML code will always be overwritten as soon as you call the generator again or if you generate new Service Cut’s in Context Mapper.

Note: If you update your CML model (for example you create a new Aggregate with new Entities), you can always call the SCL generator again and it will update your *.scl file.

The second generator (“Generate Service Cutter User Representation Example File (SCL)”) only generates an exemplary file that illustrates the SCL syntax. The generated user representations do not make sense and should not be used to generate Service Cut’s! In this file you find syntactic examples for all potential user representations you can model:

SCL Example File

Now, having a CML and a SCL model, you have two options how you can proceed:

  • Generate new service cut’s in Context Mapper
  • Analyze your model in Service Cutter

Generate Service Cut’s

You can create new CML files with new service decompositions by calling “Generate New Service Cut”:

Generate New Service Cut (Context Menu in VS Code)

Note that depending on the graph clustering algorithm (we explain how you can change the algorithm below) you may get a different result each time you call the generator. In this case, you can generete multiple suggestions by calling the generator multiple times; it always create a new *.cml file containing a new service decomposition for your model:

Generate Multiple Service Cuts

Note: The generator always uses the *.scl file with the same name your *.cml file has. For example: if you call the generator for a file called mymodel.cml, your SCL file must be called mymodel.scl and it must be stored in the same directory. The SCL file generator already creates the file under the correct file name.

Note: If you have not already created an SCL file as described above, the service cut generator will automatically create one for you.

Criteria Scoring

Service Cutter allows you to score the individual coupling criteria. Thereby you can define which criteria are more important than others in your specific case. In case you use Service Cutter, you can control the scores on the user interface (see screenshot below). In case you use the service cut generator in Context Mapper, you have to change the scores in the .servicecutter.yml file. The file is automatically generated into the root folder of your project when you call the service cut generator for the first time:

Generate Multiple Service Cuts

You can change the scoring in the priorities part of the YAML file (see screenshot above). The following values are allowed: IGNORE, XS, S, M, L, XL, and XXL.

Note: In case you work with Eclipse you have to ensure that the .* resources filter is disabled in the project/file explorer (so that you can see the .servicecutter.yml file):

Eclipse: Disable .* File Filter

Algorithms

You can further change the clustering algorithm in the .servicecutter.yml file. We currently support the following three algorithms:

Note that that LEUNG and CHINESE_WHISPERS produce randomized and non-deterministic results. That means that you get different results each time you generate a new service cut.

Analyze Model in Service Cutter

Instead of generating new service cuts in Context Mapper, it is also possible to analyze the decompositions in Service Cutter. While Context Mapper generates new CML models, Service Cutter illustrates the graph clusterings graphically.

To use Service Cutter you need the ERM and user representations as JSON files. Both can now easily be generated with Context Mapper. The ERM is generated out of the CML file…

Generate ERM JSON File

… and the user representations out of the SCL file:

Generate User Representations JSON File

Now you can start Service Cutter and import the model to analyze it. You can start Service Cutter easily by cloning the repository and using Docker:

git clone git@github.com:ServiceCutter/ServiceCutter.git
cd ServiceCutter
docker-compose up

Once the application is up-and-running you can open it in your browser under http://localhost:8080 (user: admin, password: admin). Under the System Specification tab you can upload the ERD file first, and then add the user representations:

Import ERD and User Representations (JSON files) in Service Cutter

After you have imported the two files you can switch to the Service Cuts tab and analyze different decompositions depending on the criteria scoring:

Analyze Service Cuts in Service Cutter

Summary

In this tutorial we have shown how you can use Service Cutter to generate decomposition suggestions for your system modeled in CML. It is important to note that we only understand them as suggestions. They can help to analyze the coupling between objects in your domain model and therefore may help you in finding the right service decomposition and Bounded Contexts. Don’t expect that the produced result is the best decomposition without questioning them seriously!

For our DDD sample application we used the generated outputs to discover some parts of the domain model which seem to be loosely coupled from the rest. Concretely, we extracted a Bounded Context for the Location Aggregate and one for the Voyage Aggregate. You can find this CML model here.

To extract new Bounded Contexts according to the ideas you have developed by using Service Cutter you may use our Architectural Refactorings. For example, you can extract Aggregates by using AR-5: Extract Aggregates by Cohesion.

In order to present and discuss decomposition suggestions with your colleagues, you can use our generators to create graphical representations (graphical Context Map or PlantUML diagrams) of the service decompositions.