Context Map Discovery

Our “context map discovery” or “reverse engineering” library allows you to derive a CML context map from existing source code. If you work in a project involving an existing monolith, you may want to generate a Bounded Context that represents and contains your domain model. Afterwards, you can analyze and decompose the architecture with our tools. This helps you to get started with our tool and avoids huge efforts to re-model the existing system. If your system already has a (micro-)service-oriented architecture, you may want to reverse engineer the CML context map illustrating all bounded contexts and their relationships.

The discovery library supports the reverse engineering of bounded contexts and context maps (including relationships between bounded contexts). It is extensible and designed to plug-in new discovery strategies. The current prototype supports bounded context discovery for Spring Boot applications and relationship discovery on the basis of Docker compose.

Contributions to the discovery library are very welcome! If you implement a new discovery strategy for another programming language or framework, please contribute it to our project and create a Pull Request (PR) in our GitHub repository.

Usage

The latest version of the discovery library is available through Maven Central: Maven Central

You can find all information about the library, how to use it, and how to extend it with new discovery strategies in our Github repository:

https://github.com/ContextMapper/context-map-discovery

Note that this is a prototype and limited in the discovery strategies already implemented. Additional strategies will have to be implemented in the future.

Lakeside Mutual Case Study

The following example illustrates how the discovery library works. We applied it to the Lakeside Mutual project, a fictitious insurance company. It is a sample application to demonstrate microservices. With our context map discovery library we derived a CML context map from the Lakeside Mutual source code.

The following diagram, courtesy of the Lakeside Mutual project itself, illustrates the architecture:

Lakeside Mutual Architecture Overview

With the strategies already available we are able to discover the bounded contexts:

  • Customer Management
  • Customer Self-Service
  • Policy Management
  • Customer Core

The risk management context is currently not detected, since a strategy on the basis of Node.js is not available yet.

The following piece of code is all that is needed to generate the context map with our discovery library:

public class LakesideMutualContextMapDiscoverer {

  public static void main(String[] args) throws IOException {
    // configure the discoverer
    ContextMapDiscoverer discoverer = new ContextMapDiscoverer()
        .usingBoundedContextDiscoveryStrategies(
            new SpringBootBoundedContextDiscoveryStrategy("com.lakesidemutual"))
        .usingRelationshipDiscoveryStrategies(
            new DockerComposeRelationshipDiscoveryStrategy(
                new File(System.getProperty("user.home") + "/source/LakesideMutual/")))
        .usingBoundedContextNameMappingStrategies(
            new SeparatorToCamelCaseBoundedContextNameMappingStrategy("-") {
              @Override
              public String mapBoundedContextName(String s) {
                // remove the "Backend" part of the Docker service names to map correctly...
                String name = super.mapBoundedContextName(s);
                return name.endsWith("Backend") ? name.substring(0, name.length() - 7) : name;
              }
            });

    // run the discovery process to get the Context Map
    ContextMap contextmap = discoverer.discoverContextMap();

    // serialize the Context Map to CML
    new ContextMapSerializer().serializeContextMap(contextmap, new File("./src-gen/lakesidemutual.cml"));
  }

}

The library is based on strategies implementing the three interfaces BoundedContextDiscoveryStrategy, RelationshipDiscoveryStrategy, and BoundedContextNameMappingStrategy. The BoundedContextNameMappingStrategy strategy can be used to map different bounded context names between the bounded context and relationship strategies.

In this example we use the SpringBootBoundedContextDiscoveryStrategy to discover the bounded contexts via Spring annotations. It derives Bounded Contexts from applications, Aggregates from REST endpoints, and Entities from the REST endpoint methods. The DockerComposeRelationshipDiscoveryStrategy strategy is used to derive the relationships between the bounded context from the docker-compose.yml file. The extended SeparatorToCamelCaseBoundedContextNameMappingStrategy in the example above is used to map names such as ‘customer-management-backend’ (name according to relationship strategy) to ‘CustomerManagement’ (name according the discovered bounded context).

The code above creates the following context map for the application:

ContextMap {
  contains PolicyManagement
  contains CustomerManagement
  contains CustomerSelfService
  contains CustomerCore

  CustomerCore -> PolicyManagement

  CustomerCore -> CustomerManagement

  PolicyManagement -> CustomerSelfService

  CustomerCore -> CustomerSelfService

}

BoundedContext PolicyManagement {
  implementationTechnology "Spring Boot"
  // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.policymanagement.interfaces.RiskComputationService.
  Aggregate riskfactor
  // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.policymanagement.interfaces.InsuranceQuoteRequestInformationHolder.
  Aggregate PolicyManagement_insurance_quote_requests {
    /* removed to save space here */
  }
  // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.policymanagement.interfaces.PolicyInformationHolder.
  Aggregate policies {
    /* removed to save space here */
  }
  // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.policymanagement.interfaces.CustomerInformationHolder.
  Aggregate PolicyManagement_customers {
    /* removed to save space here */
  }
}

BoundedContext CustomerManagement {
  implementationTechnology "Spring Boot"
  // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.customermanagement.interfaces.CustomerInformationHolder.
  Aggregate CustomerManagement_customers {
    /* removed to save space here */
  }
  // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.customermanagement.interfaces.InteractionLogInformationHolder.
  Aggregate interaction_logs {
    /* removed to save space here */
  }
  // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.customermanagement.interfaces.NotificationInformationHolder.
  Aggregate notifications
}

BoundedContext CustomerSelfService {
  implementationTechnology "Spring Boot"
  // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.customerselfservice.interfaces.AuthenticationController.
  Aggregate auth
  // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.customerselfservice.interfaces.CityStaticDataHolder.
  Aggregate cities {
    /* removed to save space here */
  }
  // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.customerselfservice.interfaces.InsuranceQuoteRequestInformationHolder.
  Aggregate insurance_quote_requests {
    /* removed to save space here */
  }
  // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.customerselfservice.interfaces.CustomerInformationHolder.
  Aggregate customers {
    /* removed to save space here */
  }
  // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.customerselfservice.interfaces.UserInformationHolder.
  Aggregate user {
    /* removed to save space here */
  }
}

BoundedContext CustomerCore {
  implementationTechnology "Spring Boot"
  // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.customercore.interfaces.CityStaticDataHolder.
  Aggregate CustomerCore_cities {
    /* removed to save space here */
  }
  // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.customercore.interfaces.CustomerInformationHolder.
  Aggregate CustomerCore_customers {
    /* removed to save space here */
  }
}

Note that we removed the entities in the CML model above in order to save space here. The full example and the project source code can be found here.