From 7917e2ad2fc4104f9a1aa8a212546823963232d7 Mon Sep 17 00:00:00 2001 From: Stefan Kapferer Date: Thu, 21 Nov 2019 22:54:16 +0100 Subject: [PATCH] Update Bounded Context discovery to discover aggregate root entities with methods (#6) --- Examples/LakesideMutual/gradle.properties | 2 +- .../LakesideMutual/src-gen/lakesidemutual.cml | 409 +++++++++++------- gradle.properties | 2 +- .../cml/ContextMapToCMLConverter.java | 99 +++-- .../discovery/model/Aggregate.java | 31 +- .../discovery/model/Attribute.java | 14 +- .../discovery/model/DomainObject.java | 207 +++++++++ .../discovery/model/DomainObjectType.java | 27 ++ .../contextmapper/discovery/model/Entity.java | 143 ------ .../contextmapper/discovery/model/Method.java | 152 +++++++ .../discovery/model/Parameter.java | 69 +++ .../discovery/model/Reference.java | 16 +- ...eBasedBoundedContextDiscoveryStrategy.java | 308 +++++++++++++ ...ngBootBoundedContextDiscoveryStrategy.java | 150 +------ .../cml/ContextMapToCMLConverterTest.java | 104 ++++- .../discovery/model/AggregateTest.java | 8 +- .../discovery/model/DomainObjectTest.java | 168 +++++++ .../discovery/model/EntityTest.java | 143 ------ .../discovery/model/MethodTest.java | 62 +++ .../discovery/model/ParameterTest.java | 37 ++ ...otBoundedContextDiscoveryStrategyTest.java | 129 ++++-- .../interfaces/CustomerInformationHolder.java | 8 + .../name/TestSpringBootApplication.java | 2 +- .../interfaces/CustomerInformationHolder.java | 14 +- .../name/model/Address.java | 2 +- .../name/model/Customer.java | 2 +- .../name/model/CustomerId.java | 2 +- .../name/model/duplicate1}/CustomerId.java | 2 +- .../name/model/duplicate2}/CustomerId.java | 2 +- 29 files changed, 1622 insertions(+), 692 deletions(-) create mode 100644 src/main/java/org/contextmapper/discovery/model/DomainObject.java create mode 100644 src/main/java/org/contextmapper/discovery/model/DomainObjectType.java delete mode 100644 src/main/java/org/contextmapper/discovery/model/Entity.java create mode 100644 src/main/java/org/contextmapper/discovery/model/Method.java create mode 100644 src/main/java/org/contextmapper/discovery/model/Parameter.java create mode 100644 src/main/java/org/contextmapper/discovery/strategies/boundedcontexts/AbstractRESTResourceBasedBoundedContextDiscoveryStrategy.java create mode 100644 src/test/java/org/contextmapper/discovery/model/DomainObjectTest.java delete mode 100644 src/test/java/org/contextmapper/discovery/model/EntityTest.java create mode 100644 src/test/java/org/contextmapper/discovery/model/MethodTest.java create mode 100644 src/test/java/org/contextmapper/discovery/model/ParameterTest.java rename src/test/java/test/duplicate/{entity => domainobject}/name/TestSpringBootApplication.java (94%) rename src/test/java/test/duplicate/{entity => domainobject}/name/interfaces/CustomerInformationHolder.java (79%) rename src/test/java/test/duplicate/{entity => domainobject}/name/model/Address.java (95%) rename src/test/java/test/duplicate/{entity => domainobject}/name/model/Customer.java (96%) rename src/test/java/test/duplicate/{entity => domainobject}/name/model/CustomerId.java (93%) rename src/test/java/test/duplicate/{entity/name/model/duplicate2 => domainobject/name/model/duplicate1}/CustomerId.java (91%) rename src/test/java/test/duplicate/{entity/name/model/duplicate1 => domainobject/name/model/duplicate2}/CustomerId.java (91%) diff --git a/Examples/LakesideMutual/gradle.properties b/Examples/LakesideMutual/gradle.properties index 6ec4f4e..6df5e0c 100644 --- a/Examples/LakesideMutual/gradle.properties +++ b/Examples/LakesideMutual/gradle.properties @@ -1,3 +1,3 @@ # dependency versions -discoveryLibVersion=1.1.0 +discoveryLibVersion=1.2.0-SNAPSHOT lakesideMutualVersion=0.0.1-SNAPSHOT diff --git a/Examples/LakesideMutual/src-gen/lakesidemutual.cml b/Examples/LakesideMutual/src-gen/lakesidemutual.cml index a772d5c..a1ef9ab 100644 --- a/Examples/LakesideMutual/src-gen/lakesidemutual.cml +++ b/Examples/LakesideMutual/src-gen/lakesidemutual.cml @@ -5,63 +5,111 @@ ContextMap { contains CustomerCore CustomerCore -> PolicyManagement { - exposedAggregates CustomerCore_cities, CustomerCore_customers // The list of exposed Aggregates may contain Aggregates which are not used by the downstream (discovery strategy simply added all Aggregates). + exposedAggregates cities, CustomerCore_customers // The list of exposed Aggregates may contain Aggregates which are not used by the downstream (discovery strategy simply added all Aggregates). } CustomerCore -> CustomerManagement { - exposedAggregates CustomerCore_cities, CustomerCore_customers // The list of exposed Aggregates may contain Aggregates which are not used by the downstream (discovery strategy simply added all Aggregates). + exposedAggregates cities, CustomerCore_customers // The list of exposed Aggregates may contain Aggregates which are not used by the downstream (discovery strategy simply added all Aggregates). } PolicyManagement -> CustomerSelfService { - exposedAggregates riskfactor, PolicyManagement_insurance_quote_requests, policies, PolicyManagement_customers // The list of exposed Aggregates may contain Aggregates which are not used by the downstream (discovery strategy simply added all Aggregates). + exposedAggregates insurance_quote_requests, customers, riskfactor, policies // The list of exposed Aggregates may contain Aggregates which are not used by the downstream (discovery strategy simply added all Aggregates). } CustomerCore -> CustomerSelfService { - exposedAggregates CustomerCore_cities, CustomerCore_customers // The list of exposed Aggregates may contain Aggregates which are not used by the downstream (discovery strategy simply added all Aggregates). + exposedAggregates cities, CustomerCore_customers // The list of exposed Aggregates may contain Aggregates which are not used by the downstream (discovery strategy simply added all Aggregates). } } 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 { - // This entity has been derived from the class com.lakesidemutual.policymanagement.interfaces.dtos.insurancequoterequest.InsuranceQuoteRequestDto. - Entity PolicyManagement_insurance_quote_requests_InsuranceQuoteRequestDto { - String policyId + // This Aggregate has been created on the basis of the RESTful HTTP controller com.lakesidemutual.policymanagement.interfaces.InsuranceQuoteRequestInformationHolder. + Aggregate insurance_quote_requests { + // This value object has been derived from the class com.lakesidemutual.policymanagement.interfaces.dtos.insurancequoterequest.InsuranceQuoteRequestDto. + ValueObject InsuranceQuoteRequestDto { List statusHistory - CustomerInfoDto customerInfo + String policyId InsuranceOptionsDto insuranceOptions - Date date - InsuranceQuoteDto insuranceQuote + CustomerInfoDto customerInfo Long id + InsuranceQuoteDto insuranceQuote + Date date + } + Entity insurance_quote_requests_RootEntity { + aggregateRoot + def List<@InsuranceQuoteRequestDto> getInsuranceQuoteRequests; + def @InsuranceQuoteRequestDto getInsuranceQuoteRequest; } } - // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.policymanagement.interfaces.PolicyInformationHolder. - Aggregate policies { - // This entity has been derived from the class com.lakesidemutual.policymanagement.domain.policy.PolicyId. - Entity PolicyId { - long serialVersionUID + // This Aggregate has been created on the basis of the RESTful HTTP controller com.lakesidemutual.policymanagement.interfaces.CustomerInformationHolder. + Aggregate customers { + // This value object has been derived from the class com.lakesidemutual.policymanagement.interfaces.dtos.customer.CustomerIdDto. + ValueObject CustomerIdDto { String id } - // This entity has been derived from the class com.lakesidemutual.policymanagement.interfaces.dtos.policy.PolicyDto. - Entity PolicyDto { + Entity customers_RootEntity { + aggregateRoot + def @CustomerDto getCustomer (@CustomerIdDto customerIdDto); + def List<@customers_PolicyDto> getPolicies (@CustomerIdDto customerIdDto); + def @PaginatedCustomerResponseDto getCustomers; + } + // This value object has been derived from the class com.lakesidemutual.policymanagement.interfaces.dtos.customer.CustomerDto. + ValueObject CustomerDto { + String customerId + CustomerProfileDto customerProfile List links - PolicyPeriodDto policyPeriod - String policyId - MoneyAmountDto policyLimit - List expandable + - List customers + } + // This value object has been derived from the class com.lakesidemutual.policymanagement.interfaces.dtos.policy.PolicyDto. + ValueObject customers_PolicyDto { Object customer Date creationDate - InsuringAgreementDto insuringAgreement String policyType + InsuringAgreementDto insuringAgreement + MoneyAmountDto insurancePremium + List links + MoneyAmountDto policyLimit + PolicyPeriodDto policyPeriod + String policyId MoneyAmountDto deductible + List expandable + } + // This value object has been derived from the class com.lakesidemutual.policymanagement.interfaces.dtos.customer.PaginatedCustomerResponseDto. + ValueObject PaginatedCustomerResponseDto { + int offset + int ^size + String filter + int limit + List links + - List customers + } + } + // This Aggregate has been created on the basis of the RESTful HTTP controller com.lakesidemutual.policymanagement.interfaces.RiskComputationService. + Aggregate riskfactor { + Entity riskfactor_RootEntity { + aggregateRoot + } + } + // This Aggregate has been created on the basis of the RESTful HTTP controller com.lakesidemutual.policymanagement.interfaces.PolicyInformationHolder. + Aggregate policies { + // This value object has been derived from the class com.lakesidemutual.policymanagement.interfaces.dtos.policy.PolicyDto. + ValueObject PolicyDto { MoneyAmountDto insurancePremium + List expandable + String policyId + String policyType + MoneyAmountDto deductible + List links + MoneyAmountDto policyLimit + PolicyPeriodDto policyPeriod + InsuringAgreementDto insuringAgreement + Date creationDate + Object customer + - List policies } - // This entity has been derived from the class com.lakesidemutual.policymanagement.interfaces.dtos.policy.CreatePolicyRequestDto. - Entity CreatePolicyRequestDto { + // This value object has been derived from the class com.lakesidemutual.policymanagement.interfaces.dtos.policy.CreatePolicyRequestDto. + ValueObject CreatePolicyRequestDto { MoneyAmountDto insurancePremium InsuringAgreementDto insuringAgreement MoneyAmountDto policyLimit @@ -70,205 +118,264 @@ BoundedContext PolicyManagement { String policyType MoneyAmountDto deductible } - // This entity has been derived from the class com.lakesidemutual.policymanagement.interfaces.dtos.policy.PaginatedPolicyResponseDto. - Entity PaginatedPolicyResponseDto { + Entity policies_RootEntity { + aggregateRoot + def @PolicyDto getPolicy (@PolicyId policyId); + def @PolicyDto updatePolicy (@CreatePolicyRequestDto createPolicyDto, @PolicyId policyId); + def @PaginatedPolicyResponseDto getPolicies; + } + // This value object has been derived from the class com.lakesidemutual.policymanagement.interfaces.dtos.policy.PaginatedPolicyResponseDto. + ValueObject PaginatedPolicyResponseDto { int limit List links int offset int ^size - List policies } - } - // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.policymanagement.interfaces.CustomerInformationHolder. - Aggregate PolicyManagement_customers { - // This entity has been derived from the class com.lakesidemutual.policymanagement.interfaces.dtos.customer.CustomerIdDto. - Entity CustomerIdDto { + // This value object has been derived from the class com.lakesidemutual.policymanagement.domain.policy.PolicyId. + ValueObject PolicyId { + long serialVersionUID String id } - // This entity has been derived from the class com.lakesidemutual.policymanagement.interfaces.dtos.customer.PaginatedCustomerResponseDto. - Entity PaginatedCustomerResponseDto { + } +} + +BoundedContext CustomerManagement { + implementationTechnology "Spring Boot" + // This Aggregate has been created on the basis of the RESTful HTTP controller com.lakesidemutual.customermanagement.interfaces.CustomerInformationHolder. + Aggregate CustomerManagement_customers { + // This value object has been derived from the class com.lakesidemutual.customermanagement.interfaces.dtos.PaginatedCustomerResponseDto. + ValueObject CustomerManagement_customers_PaginatedCustomerResponseDto { String filter int ^size int offset List links int limit - - List customers + - List customers } - // This entity has been derived from the class com.lakesidemutual.policymanagement.interfaces.dtos.customer.CustomerDto. - Entity PolicyManagement_customers_CustomerDto { - CustomerProfileDto customerProfile - String customerId + // This value object has been derived from the class com.lakesidemutual.customermanagement.interfaces.dtos.CustomerDto. + ValueObject CustomerManagement_customers_CustomerDto { List links - } - } -} - -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 { - // This entity has been derived from the class com.lakesidemutual.customermanagement.interfaces.dtos.CustomerDto. - Entity CustomerManagement_customers_CustomerDto { String customerId - List links - - CustomerProfileDto customerProfile + - CustomerProfileDto customerProfile - List customers } - // This entity has been derived from the class com.lakesidemutual.customermanagement.interfaces.dtos.CustomerProfileDto. - Entity CustomerProfileDto { - Date birthday + // This value object has been derived from the class com.lakesidemutual.customermanagement.interfaces.dtos.CustomerProfileDto. + ValueObject CustomerProfileDto { List moveHistory String firstname - AddressDto currentAddress String phoneNumber + Date birthday String lastname String ^email + AddressDto currentAddress + - CustomerProfileDto customerProfile } - // This entity has been derived from the class com.lakesidemutual.customermanagement.interfaces.dtos.PaginatedCustomerResponseDto. - Entity CustomerManagement_customers_PaginatedCustomerResponseDto { - int offset - int ^size - String filter - int limit - List links - - List customers + Entity CustomerManagement_customers_RootEntity { + aggregateRoot + def @CustomerManagement_customers_PaginatedCustomerResponseDto getCustomers; + def @CustomerManagement_customers_CustomerDto getCustomer (@CustomerManagement_customers_CustomerId customerId); + def @CustomerManagement_customers_CustomerDto updateCustomer (@CustomerManagement_customers_CustomerId customerId, @CustomerProfileDto customerProfile); + } + // This value object has been derived from the class com.lakesidemutual.customermanagement.domain.customer.CustomerId. + ValueObject CustomerManagement_customers_CustomerId { + long serialVersionUID + String id } - // This entity has been derived from the class com.lakesidemutual.customermanagement.domain.customer.CustomerId. - Entity CustomerManagement_customers_CustomerId } - // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.customermanagement.interfaces.InteractionLogInformationHolder. - Aggregate interaction_logs { - // This entity has been derived from the class com.lakesidemutual.customermanagement.domain.interactionlog.InteractionLogAggregateRoot. - Entity InteractionLogAggregateRoot { - Collection interactions - String lastAcknowledgedInteractionId + // This Aggregate has been created on the basis of the RESTful HTTP controller com.lakesidemutual.customermanagement.interfaces.NotificationInformationHolder. + Aggregate notifications { + Entity notifications_RootEntity { + aggregateRoot + def List<@NotificationDto> getNotifications; + } + // This value object has been derived from the class com.lakesidemutual.customermanagement.interfaces.dtos.NotificationDto. + ValueObject NotificationDto { + int count String customerId String username } - // This entity has been derived from the class com.lakesidemutual.customermanagement.domain.customer.CustomerId. - Entity interaction_logs_CustomerId { - String id + } + // This Aggregate has been created on the basis of the RESTful HTTP controller com.lakesidemutual.customermanagement.interfaces.InteractionLogInformationHolder. + Aggregate interaction_logs { + // This value object has been derived from the class com.lakesidemutual.customermanagement.domain.customer.CustomerId. + ValueObject interaction_logs_CustomerId { long serialVersionUID + String id + } + // This value object has been derived from the class com.lakesidemutual.customermanagement.domain.interactionlog.InteractionLogAggregateRoot. + ValueObject InteractionLogAggregateRoot { + String customerId + String lastAcknowledgedInteractionId + String username + Collection interactions + } + Entity interaction_logs_RootEntity { + aggregateRoot + def @InteractionLogAggregateRoot getInteractionLog (@interaction_logs_CustomerId customerId); } } - // 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 { - // This entity has been derived from the class com.lakesidemutual.customerselfservice.interfaces.dtos.city.CitiesResponseDto. - Entity CitiesResponseDto { + // This Aggregate has been created on the basis of the RESTful HTTP controller com.lakesidemutual.customerselfservice.interfaces.AuthenticationController. + Aggregate auth { + Entity auth_RootEntity { + aggregateRoot + } + } + // This Aggregate has been created on the basis of the RESTful HTTP controller com.lakesidemutual.customerselfservice.interfaces.InsuranceQuoteRequestInformationHolder. + Aggregate CustomerSelfService_insurance_quote_requests { + // This value object has been derived from the class com.lakesidemutual.customerselfservice.interfaces.dtos.insurancequoterequest.InsuranceQuoteRequestDto. + ValueObject CustomerSelfService_insurance_quote_requests_InsuranceQuoteRequestDto { + List statusHistory + Long id + Date date + InsuranceQuoteDto insuranceQuote + InsuranceOptionsDto insuranceOptions + CustomerInfoDto customerInfo + String policyId + } + Entity CustomerSelfService_insurance_quote_requests_RootEntity { + aggregateRoot + def @CustomerSelfService_insurance_quote_requests_InsuranceQuoteRequestDto getInsuranceQuoteRequest; + def List<@CustomerSelfService_insurance_quote_requests_InsuranceQuoteRequestDto> getInsuranceQuoteRequests; + } + } + // This Aggregate has been created on the basis of the RESTful HTTP controller com.lakesidemutual.customerselfservice.interfaces.CityStaticDataHolder. + Aggregate CustomerSelfService_cities { + // This value object has been derived from the class com.lakesidemutual.customerselfservice.interfaces.dtos.city.CitiesResponseDto. + ValueObject CustomerSelfService_cities_CitiesResponseDto { List cities } + Entity CustomerSelfService_cities_RootEntity { + aggregateRoot + def @CustomerSelfService_cities_CitiesResponseDto getCitiesForPostalCode; + } } - // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.customerselfservice.interfaces.CustomerInformationHolder. - Aggregate customers { - // This entity has been derived from the class com.lakesidemutual.customerselfservice.interfaces.dtos.customer.AddressDto. - Entity AddressDto { - String city - String postalCode - String streetAddress + // This Aggregate has been created on the basis of the RESTful HTTP controller com.lakesidemutual.customerselfservice.interfaces.UserInformationHolder. + Aggregate user { + Entity user_RootEntity { + aggregateRoot + def @UserResponseDto getCurrentUser; } - // This entity has been derived from the class com.lakesidemutual.customerselfservice.interfaces.dtos.customer.CustomerDto. - Entity CustomerDto { - CustomerProfileDto customerProfile - List links + // This value object has been derived from the class com.lakesidemutual.customerselfservice.interfaces.dtos.identityaccess.UserResponseDto. + ValueObject UserResponseDto { + String ^email String customerId } - // This entity has been derived from the class com.lakesidemutual.customerselfservice.domain.customer.CustomerId. - Entity CustomerId { - long serialVersionUID - String id - } } - // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.customerselfservice.interfaces.InsuranceQuoteRequestInformationHolder. - Aggregate insurance_quote_requests { - // This entity has been derived from the class com.lakesidemutual.customerselfservice.interfaces.dtos.insurancequoterequest.InsuranceQuoteRequestDto. - Entity InsuranceQuoteRequestDto { - InsuranceQuoteDto insuranceQuote - Long id + // This Aggregate has been created on the basis of the RESTful HTTP controller com.lakesidemutual.customerselfservice.interfaces.CustomerInformationHolder. + Aggregate CustomerSelfService_customers { + // This value object has been derived from the class com.lakesidemutual.customerselfservice.interfaces.dtos.insurancequoterequest.InsuranceQuoteRequestDto. + ValueObject CustomerSelfService_customers_InsuranceQuoteRequestDto { String policyId List statusHistory - Date date - InsuranceOptionsDto insuranceOptions CustomerInfoDto customerInfo + InsuranceOptionsDto insuranceOptions + Long id + InsuranceQuoteDto insuranceQuote + Date date } - } - // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.customerselfservice.interfaces.UserInformationHolder. - Aggregate user { - // This entity has been derived from the class com.lakesidemutual.customerselfservice.interfaces.dtos.identityaccess.UserResponseDto. - Entity UserResponseDto { + // This value object has been derived from the class com.lakesidemutual.customerselfservice.interfaces.dtos.customer.CustomerDto. + ValueObject CustomerSelfService_customers_CustomerDto { String customerId - String ^email + List links + CustomerProfileDto customerProfile + } + // This value object has been derived from the class com.lakesidemutual.customerselfservice.domain.customer.CustomerId. + ValueObject CustomerSelfService_customers_CustomerId { + String id + long serialVersionUID + } + Entity CustomerSelfService_customers_RootEntity { + aggregateRoot + def @CustomerSelfService_customers_AddressDto changeAddress (@CustomerSelfService_customers_CustomerId customerId, @CustomerSelfService_customers_AddressDto requestDto); + def @CustomerSelfService_customers_CustomerDto getCustomer (@CustomerSelfService_customers_CustomerId customerId); + def List<@CustomerSelfService_customers_InsuranceQuoteRequestDto> getInsuranceQuoteRequests (@CustomerSelfService_customers_CustomerId customerId); + } + // This value object has been derived from the class com.lakesidemutual.customerselfservice.interfaces.dtos.customer.AddressDto. + ValueObject CustomerSelfService_customers_AddressDto { + String streetAddress + String city + String postalCode } } } 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 { - // This entity has been derived from the class com.lakesidemutual.customercore.interfaces.dtos.city.CitiesResponseDto. - Entity CustomerCore_cities_CitiesResponseDto { + // This Aggregate has been created on the basis of the RESTful HTTP controller com.lakesidemutual.customercore.interfaces.CityStaticDataHolder. + Aggregate cities { + Entity cities_RootEntity { + aggregateRoot + def @CitiesResponseDto getCitiesForPostalCode; + } + // This value object has been derived from the class com.lakesidemutual.customercore.interfaces.dtos.city.CitiesResponseDto. + ValueObject CitiesResponseDto { List cities } } - // This Aggregate has been created on the basis of the Spring REST controller com.lakesidemutual.customercore.interfaces.CustomerInformationHolder. + // This Aggregate has been created on the basis of the RESTful HTTP controller com.lakesidemutual.customercore.interfaces.CustomerInformationHolder. Aggregate CustomerCore_customers { - // This entity has been derived from the class com.lakesidemutual.customercore.interfaces.dtos.customer.CustomerResponseDto. - Entity CustomerResponseDto { - Collection
moveHistory - String firstname - String lastname + // This value object has been derived from the class com.lakesidemutual.customercore.interfaces.dtos.customer.AddressDto. + ValueObject AddressDto { + String streetAddress String city - List links - String phoneNumber - Date birthday String postalCode - String customerId - String ^email - String streetAddress } - // This entity has been derived from the class com.lakesidemutual.customercore.interfaces.dtos.customer.CustomerProfileUpdateRequestDto. - Entity CustomerProfileUpdateRequestDto { + // This value object has been derived from the class com.lakesidemutual.customercore.domain.customer.CustomerId. + ValueObject CustomerId { + String id + long serialVersionUID + } + Entity CustomerCore_customers_RootEntity { + aggregateRoot + def @CustomerResponseDto updateCustomer (@CustomerId customerId, @CustomerProfileUpdateRequestDto requestDto); + def @CustomersResponseDto getCustomer; + def @AddressDto changeAddress (@AddressDto requestDto, @CustomerId customerId); + def @CustomerCore_customers_PaginatedCustomerResponseDto getCustomers; + } + // This value object has been derived from the class com.lakesidemutual.customercore.interfaces.dtos.customer.CustomerProfileUpdateRequestDto. + ValueObject CustomerProfileUpdateRequestDto { + String lastname + Date birthday + String phoneNumber + String streetAddress + String city String ^email String postalCode - String streetAddress String firstname + } + // This value object has been derived from the class com.lakesidemutual.customercore.interfaces.dtos.customer.CustomerResponseDto. + ValueObject CustomerResponseDto { String phoneNumber String lastname - Date birthday + String streetAddress + Collection
moveHistory + String firstname + String ^email String city + String postalCode + Date birthday + String customerId + List links + - List customers - List customers } - // This entity has been derived from the class com.lakesidemutual.customercore.interfaces.dtos.customer.CustomersResponseDto. - Entity CustomersResponseDto { + // This value object has been derived from the class com.lakesidemutual.customercore.interfaces.dtos.customer.CustomersResponseDto. + ValueObject CustomersResponseDto { List links - List customers } - // This entity has been derived from the class com.lakesidemutual.customercore.interfaces.dtos.customer.AddressDto. - Entity CustomerCore_customers_AddressDto { - String city - String postalCode - String streetAddress - } - // This entity has been derived from the class com.lakesidemutual.customercore.interfaces.dtos.customer.PaginatedCustomerResponseDto. - Entity CustomerCore_customers_PaginatedCustomerResponseDto { + // This value object has been derived from the class com.lakesidemutual.customercore.interfaces.dtos.customer.PaginatedCustomerResponseDto. + ValueObject CustomerCore_customers_PaginatedCustomerResponseDto { List links + String filter int limit int offset int ^size - String filter - List customers } - // This entity has been derived from the class com.lakesidemutual.customercore.domain.customer.CustomerId. - Entity CustomerCore_customers_CustomerId { - String id - long serialVersionUID - } } } diff --git a/gradle.properties b/gradle.properties index 8ce5412..e091718 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ ossReleaseStagingRepository=https://oss.sonatype.org/service/local/staging/deplo # dependency versions jUnitVersion=5.5.2 -cmlVersion=5.3.2 +cmlVersion=5.5.0 reflectionsVersion=0.9.11 springBootVersion=2.2.0.RELEASE springWebVersion=5.2.0.RELEASE diff --git a/src/main/java/org/contextmapper/discovery/cml/ContextMapToCMLConverter.java b/src/main/java/org/contextmapper/discovery/cml/ContextMapToCMLConverter.java index aa862dc..8a58500 100644 --- a/src/main/java/org/contextmapper/discovery/cml/ContextMapToCMLConverter.java +++ b/src/main/java/org/contextmapper/discovery/cml/ContextMapToCMLConverter.java @@ -15,13 +15,14 @@ */ package org.contextmapper.discovery.cml; +import org.contextmapper.discovery.model.Method; import org.contextmapper.discovery.model.Relationship; import org.contextmapper.dsl.contextMappingDSL.*; import org.contextmapper.tactic.dsl.tacticdsl.*; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; +import java.util.*; + +import static org.contextmapper.discovery.model.DomainObjectType.ENTITY; /** * Converts a {@link org.contextmapper.discovery.model.ContextMap} to the CML {@link org.contextmapper.dsl.contextMappingDSL.ContextMap} @@ -31,7 +32,7 @@ public class ContextMapToCMLConverter { private Map boundedContextMap = new HashMap<>(); - private Map entityLookupMap = new HashMap<>(); + private Map domainObjectLookupMap = new HashMap<>(); public ContextMappingModel convert(org.contextmapper.discovery.model.ContextMap inputMap) { ContextMappingModel model = ContextMappingDSLFactory.eINSTANCE.createContextMappingModel(); @@ -45,11 +46,6 @@ public ContextMappingModel convert(org.contextmapper.discovery.model.ContextMap } for (Relationship relationship : inputMap.getRelationships()) { - UpstreamDownstreamRelationship upstreamDownstreamRelationship = convert(relationship); - if (!contextMap.getBoundedContexts().contains(upstreamDownstreamRelationship.getUpstream())) - contextMap.getBoundedContexts().add(upstreamDownstreamRelationship.getUpstream()); - if (!contextMap.getBoundedContexts().contains(upstreamDownstreamRelationship.getDownstream())) - contextMap.getBoundedContexts().add(upstreamDownstreamRelationship.getDownstream()); contextMap.getRelationships().add(convert(relationship)); } @@ -72,21 +68,73 @@ private BoundedContext convert(org.contextmapper.discovery.model.BoundedContext private Aggregate convert(org.contextmapper.discovery.model.Aggregate inputAggregate) { Aggregate aggregate = ContextMappingDSLFactory.eINSTANCE.createAggregate(); aggregate.setName(inputAggregate.getName()); - aggregate.setComment("// " + inputAggregate.getDiscoveryComment()); - for (org.contextmapper.discovery.model.Entity entity : inputAggregate.getEntities()) { - aggregate.getDomainObjects().add(convert(entity)); + if (inputAggregate.getDiscoveryComment() != null && !"".equals(inputAggregate.getDiscoveryComment())) + aggregate.setComment("// " + inputAggregate.getDiscoveryComment()); + for (org.contextmapper.discovery.model.DomainObject domainObject : inputAggregate.getDomainObjects()) { + aggregate.getDomainObjects().add(convert(domainObject)); + } + for (org.contextmapper.discovery.model.DomainObject domainObject : inputAggregate.getDomainObjects()) { + convertDomainObjectMethods(domainObject); } + Optional rootEntity = aggregate.getDomainObjects().stream().filter(o -> o instanceof Entity).map(o -> (Entity) o) + .filter(e -> e.getName().endsWith("_RootEntity")).findFirst(); + if (rootEntity.isPresent()) + rootEntity.get().setAggregateRoot(true); return aggregate; } - private Entity convert(org.contextmapper.discovery.model.Entity inputEntity) { + private DomainObject convert(org.contextmapper.discovery.model.DomainObject inputDomainObject) { + if (ENTITY.equals(inputDomainObject.getType())) + return convertDomainObjectToEntity(inputDomainObject); + return convertDomainObjectToValueObject(inputDomainObject); + } + + private Entity convertDomainObjectToEntity(org.contextmapper.discovery.model.DomainObject inputDomainObject) { Entity entity = TacticdslFactory.eINSTANCE.createEntity(); - entity.setName(inputEntity.getName()); - entity.setComment("// " + inputEntity.getDiscoveryComment()); - entityLookupMap.put(inputEntity, entity); + entity.setName(inputDomainObject.getName()); + domainObjectLookupMap.put(inputDomainObject, entity); return entity; } + private ValueObject convertDomainObjectToValueObject(org.contextmapper.discovery.model.DomainObject inputDomainObject) { + ValueObject valueObject = TacticdslFactory.eINSTANCE.createValueObject(); + valueObject.setName(inputDomainObject.getName()); + if (inputDomainObject.getDiscoveryComment() != null && !"".equals(inputDomainObject.getDiscoveryComment())) + valueObject.setComment("// " + inputDomainObject.getDiscoveryComment()); + domainObjectLookupMap.put(inputDomainObject, valueObject); + return valueObject; + } + + private void convertDomainObjectMethods(org.contextmapper.discovery.model.DomainObject inputDomainObject) { + DomainObject domainObject = this.domainObjectLookupMap.get(inputDomainObject); + for (Method inputMethod : inputDomainObject.getMethods()) { + DomainObjectOperation operation = TacticdslFactory.eINSTANCE.createDomainObjectOperation(); + operation.setName(inputMethod.getName()); + operation.setReturnType(createComplexType(inputMethod.getReturnType(), inputMethod.getReturnCollectionType())); + operation.getParameters().addAll(createParameters(inputMethod.getParameters())); + domainObject.getOperations().add(operation); + } + } + + private Set createParameters(Set inputParameters) { + Set parameters = new HashSet<>(); + for (org.contextmapper.discovery.model.Parameter inputParameter : inputParameters) { + Parameter parameter = TacticdslFactory.eINSTANCE.createParameter(); + parameter.setName(inputParameter.getName()); + parameter.setParameterType(createComplexType(inputParameter.getType(), inputParameter.getCollectionType())); + parameters.add(parameter); + } + return parameters; + } + + private ComplexType createComplexType(org.contextmapper.discovery.model.DomainObject inputDomainObject, String collectionType) { + ComplexType complexType = TacticdslFactory.eINSTANCE.createComplexType(); + complexType.setDomainObjectType(this.domainObjectLookupMap.get(inputDomainObject)); + if (collectionType != null) + complexType.setCollectionType(CollectionType.get(collectionType)); + return complexType; + } + private UpstreamDownstreamRelationship convert(Relationship relationship) { UpstreamDownstreamRelationship upstreamDownstreamRelationship = ContextMappingDSLFactory.eINSTANCE.createUpstreamDownstreamRelationship(); upstreamDownstreamRelationship.setUpstream(this.boundedContextMap.get(relationship.getUpstream().getName())); @@ -96,30 +144,31 @@ private UpstreamDownstreamRelationship convert(Relationship relationship) { if (cmlAggregate.isPresent()) upstreamDownstreamRelationship.getUpstreamExposedAggregates().add(cmlAggregate.get()); } - upstreamDownstreamRelationship.setExposedAggregatesComment("// " + relationship.getExposedAggregatesComment()); + if (relationship.getExposedAggregatesComment() != null && !"".equals(relationship.getExposedAggregatesComment())) + upstreamDownstreamRelationship.setExposedAggregatesComment("// " + relationship.getExposedAggregatesComment()); return upstreamDownstreamRelationship; } private void updateEntityAttributesAndReferences() { - for (Map.Entry entry : this.entityLookupMap.entrySet()) { - updateEntity(entry.getKey(), entry.getValue()); + for (Map.Entry entry : this.domainObjectLookupMap.entrySet()) { + updateDomainObject(entry.getKey(), entry.getValue()); } } - private void updateEntity(org.contextmapper.discovery.model.Entity inputEntity, Entity entity) { - for (org.contextmapper.discovery.model.Attribute inputAttribute : inputEntity.getAttributes()) { + private void updateDomainObject(org.contextmapper.discovery.model.DomainObject inputDomainObject, DomainObject domainObject) { + for (org.contextmapper.discovery.model.Attribute inputAttribute : inputDomainObject.getAttributes()) { Attribute attribute = TacticdslFactory.eINSTANCE.createAttribute(); attribute.setName(inputAttribute.getName()); attribute.setType(inputAttribute.getType()); attribute.setCollectionType(CollectionType.get(inputAttribute.getCollectionType())); - entity.getAttributes().add(attribute); + domainObject.getAttributes().add(attribute); } - for (org.contextmapper.discovery.model.Reference inputReference : inputEntity.getReferences()) { + for (org.contextmapper.discovery.model.Reference inputReference : inputDomainObject.getReferences()) { Reference reference = TacticdslFactory.eINSTANCE.createReference(); reference.setName(inputReference.getName()); - reference.setDomainObjectType(this.entityLookupMap.get(inputReference.getType())); + reference.setDomainObjectType(this.domainObjectLookupMap.get(inputReference.getType())); reference.setCollectionType(CollectionType.get(inputReference.getCollectionType())); - entity.getReferences().add(reference); + domainObject.getReferences().add(reference); } } } diff --git a/src/main/java/org/contextmapper/discovery/model/Aggregate.java b/src/main/java/org/contextmapper/discovery/model/Aggregate.java index 378dc3b..e2f4adf 100644 --- a/src/main/java/org/contextmapper/discovery/model/Aggregate.java +++ b/src/main/java/org/contextmapper/discovery/model/Aggregate.java @@ -29,12 +29,12 @@ public class Aggregate { private String name; - private Set entities; + private Set domainObjects; private String discoveryComment; public Aggregate(String name) { setName(name); - this.entities = new HashSet<>(); + this.domainObjects = new HashSet<>(); } /** @@ -58,30 +58,33 @@ public String getName() { } /** - * Adds an entity to the Aggregate. + * Adds a domain object to the Aggregate. * - * @param entity the entity to be added to the Aggregate + * @param domainObject the domain object to be added to the Aggregate */ - public void addEntity(Entity entity) { - this.entities.add(entity); + public void addDomainObject(DomainObject domainObject) { + this.domainObjects.add(domainObject); + domainObject.setParent(this); } /** - * Adds all entities in the given set to the Aggregate. + * Adds all domain objects in the given set to the Aggregate. * - * @param entities the set of entities to be added to the Aggregate + * @param domainObjects the set of domain objects to be added to the Aggregate */ - public void addEntities(Set entities) { - this.entities.addAll(entities); + public void addDomainObjects(Set domainObjects) { + for (DomainObject domainObject : domainObjects) { + this.addDomainObject(domainObject); + } } /** - * Gets the set of entities within the Aggregate. + * Gets the set of domain objects within the Aggregate. * - * @return the set of entities which are part of the Aggregate + * @return the set of domain objects which are part of the Aggregate */ - public Set getEntities() { - return new HashSet<>(entities); + public Set getDomainObjects() { + return new HashSet<>(domainObjects); } /** diff --git a/src/main/java/org/contextmapper/discovery/model/Attribute.java b/src/main/java/org/contextmapper/discovery/model/Attribute.java index aeb1847..776d966 100644 --- a/src/main/java/org/contextmapper/discovery/model/Attribute.java +++ b/src/main/java/org/contextmapper/discovery/model/Attribute.java @@ -26,7 +26,7 @@ */ public class Attribute { - private Entity parent; + private DomainObject parent; private String name; private String type; private String collectionType; @@ -55,20 +55,20 @@ public String getType() { } /** - * Gets the parent entity containing this attribute. + * Gets the parent domain object containing this attribute. * - * @return the parent entity containing this attribute + * @return the parent domain object containing this attribute */ - public Entity getParent() { + public DomainObject getParent() { return parent; } /** - * Sets the parent entity containing this attribute. + * Sets the parent domain object containing this attribute. * - * @param parent the parent entity containing this attribute + * @param parent the parent domain object containing this attribute */ - public void setParent(Entity parent) { + public void setParent(DomainObject parent) { this.parent = parent; } diff --git a/src/main/java/org/contextmapper/discovery/model/DomainObject.java b/src/main/java/org/contextmapper/discovery/model/DomainObject.java new file mode 100644 index 0000000..dbf1e1a --- /dev/null +++ b/src/main/java/org/contextmapper/discovery/model/DomainObject.java @@ -0,0 +1,207 @@ +/* + * Copyright 2019 The Context Mapper Project Team + * + * 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 org.contextmapper.discovery.model; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; + +import java.util.HashSet; +import java.util.Set; + +/** + * Represents a discovered domain object (part of an Aggregate). + * + * @author Stefan Kapferer + */ +public class DomainObject { + + private String name; + private String originalType; + private Set attributes; + private Set references; + private Set methods; + private String discoveryComment; + private DomainObjectType type; + private Aggregate parent; + + public DomainObject(DomainObjectType type, String name) { + if (name == null || "".equals(name)) + throw new IllegalArgumentException("The name of a domain object must not be null or empty."); + if (type == null) + throw new IllegalArgumentException("The type of a domain object must not be null."); + + this.type = type; + this.name = name; + this.attributes = new HashSet<>(); + this.references = new HashSet<>(); + this.methods = new HashSet<>(); + } + + public DomainObject(DomainObjectType type, String name, String originalType) { + this(type, name); + this.originalType = originalType; + } + + /** + * Gets the name of the domain object. + * + * @return the name of the domain object + */ + public String getName() { + return name; + } + + /** + * Gets the original type of the domain object. + * + * @return the original type of the domain object + */ + public String getOriginalType() { + return originalType; + } + + /** + * Gets the type of the domain object. + * + * @return the type of the domain object + */ + public DomainObjectType getType() { + return type; + } + + /** + * Sets the type of the domain object. + * + * @param type the type of the domain object + */ + public void setType(DomainObjectType type) { + this.type = type; + } + + /** + * Adds a new attribute to the domain object. + * + * @param attribute the attribute to be added to the domain object + */ + public void addAttribute(Attribute attribute) { + attribute.setParent(this); + this.attributes.add(attribute); + } + + /** + * Gets the set of attributes of the domain object. + * + * @return the set of attributes of the domain object + */ + public Set getAttributes() { + return new HashSet<>(attributes); + } + + /** + * Adds a new reference to the domain object. + * + * @param reference the reference to be added to the domain object + */ + public void addReference(Reference reference) { + reference.setParent(this); + this.references.add(reference); + } + + /** + * Gets the set of references of the domain object. + * + * @return the set of reference of the domain object + */ + public Set getReferences() { + return new HashSet<>(references); + } + + /** + * Adds a new method to the domain object. + * + * @param method the method to be added to the domain object + */ + public void addMethod(Method method) { + method.setParent(this); + this.methods.add(method); + } + + /** + * Gets the set of methods of the domain object. + * + * @return the set of methods of the domain object + */ + public Set getMethods() { + return new HashSet<>(methods); + } + + /** + * Sets a comment describing how the domain object has been discovered. + * + * @param discoveryComment the comment describing how the domain object has been discovered + */ + public void setDiscoveryComment(String discoveryComment) { + this.discoveryComment = discoveryComment; + } + + /** + * Gets a comment describing how the domain object has been discovered. + * + * @return the comment describing how the domain object has been discovered + */ + public String getDiscoveryComment() { + return discoveryComment; + } + + /** + * Gets the parent Aggregate of which the domain object is part of. + * + * @return the parent Aggregate + */ + public Aggregate getParent() { + return parent; + } + + /** + * Sets the parent Aggregate of which the domain object is part of. + * + * @param parent the parent Aggregate + */ + public void setParent(Aggregate parent) { + this.parent = parent; + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof DomainObject)) + return false; + + DomainObject domainObject = (DomainObject) object; + + return new EqualsBuilder() + .append(type, domainObject.type) + .append(name, domainObject.name) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder() + .append(type) + .append(name) + .hashCode(); + } +} diff --git a/src/main/java/org/contextmapper/discovery/model/DomainObjectType.java b/src/main/java/org/contextmapper/discovery/model/DomainObjectType.java new file mode 100644 index 0000000..f78946b --- /dev/null +++ b/src/main/java/org/contextmapper/discovery/model/DomainObjectType.java @@ -0,0 +1,27 @@ +/* + * Copyright 2019 The Context Mapper Project Team + * + * 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 org.contextmapper.discovery.model; + +/** + * Represents the type of a discovered domain object. + * + * @author Stefan Kapferer + */ +public enum DomainObjectType { + + ENTITY, VALUE_OBJECT + +} diff --git a/src/main/java/org/contextmapper/discovery/model/Entity.java b/src/main/java/org/contextmapper/discovery/model/Entity.java deleted file mode 100644 index b2bf1c4..0000000 --- a/src/main/java/org/contextmapper/discovery/model/Entity.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2019 The Context Mapper Project Team - * - * 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 org.contextmapper.discovery.model; - -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; - -import java.util.HashSet; -import java.util.Set; - -/** - * Represents an Entity (part of Aggregate). - * - * @author Stefan Kapferer - */ -public class Entity { - - private String name; - private String type; - private Set attributes; - private Set references; - private String discoveryComment; - - public Entity(String type, String name) { - if (name == null || "".equals(name)) - throw new IllegalArgumentException("The name of an entity must not be null or empty."); - if (type == null || "".equals(type)) - throw new IllegalArgumentException("The type of an entity must not be null or empty."); - - this.type = type; - this.name = name; - this.attributes = new HashSet<>(); - this.references = new HashSet<>(); - } - - /** - * Gets the name of the entity. - * - * @return the name of the entity - */ - public String getName() { - return name; - } - - /** - * Gets the type of the entity. - * - * @return the type of the entity - */ - public String getType() { - return type; - } - - /** - * Adds a new attribute to the entity. - * - * @param attribute the attribute to be added to the entity - */ - public void addAttribute(Attribute attribute) { - attribute.setParent(this); - this.attributes.add(attribute); - } - - /** - * Gets the set of attributes of the entity. - * - * @return the set of attributes of the entity - */ - public Set getAttributes() { - return new HashSet<>(attributes); - } - - /** - * Adds a new reference to the entity. - * - * @param reference the reference to be added to the entity - */ - public void addReference(Reference reference) { - reference.setParent(this); - this.references.add(reference); - } - - /** - * Gets the set of references of the entity. - * - * @return the set of reference of the entity - */ - public Set getReferences() { - return new HashSet<>(references); - } - - /** - * Sets a comment describing how the entity has been discovered. - * - * @param discoveryComment the comment describing how the entity has been discovered - */ - public void setDiscoveryComment(String discoveryComment) { - this.discoveryComment = discoveryComment; - } - - /** - * Gets a comment describing how the entity has been discovered. - * - * @return the comment describing how the entity has been discovered - */ - public String getDiscoveryComment() { - return discoveryComment; - } - - @Override - public boolean equals(Object object) { - if (!(object instanceof Entity)) - return false; - - Entity entity = (Entity) object; - - return new EqualsBuilder() - .append(type, entity.type) - .append(name, entity.name) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder() - .append(type) - .append(name) - .hashCode(); - } -} diff --git a/src/main/java/org/contextmapper/discovery/model/Method.java b/src/main/java/org/contextmapper/discovery/model/Method.java new file mode 100644 index 0000000..dfd516a --- /dev/null +++ b/src/main/java/org/contextmapper/discovery/model/Method.java @@ -0,0 +1,152 @@ +/* + * Copyright 2019 The Context Mapper Project Team + * + * 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 org.contextmapper.discovery.model; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; + +import java.util.HashSet; +import java.util.Set; + +/** + * Represents a method in a domain object. + * + * @author Stefan Kapferer + */ +public class Method { + + private DomainObject parent; + private String name; + private DomainObject returnType; + private String returnCollectionType; + private Set parameters; + + public Method(String name) { + this.name = name; + this.parameters = new HashSet<>(); + } + + /** + * Gets the name of the method. + * + * @return the name of the method + */ + public String getName() { + return name; + } + + /** + * Sets the return type of the method. + * + * @param returnType the return type of the method + */ + public void setReturnType(DomainObject returnType) { + this.returnType = returnType; + } + + /** + * Gets the return type of the method. + * + * @return the return type of the method + */ + public DomainObject getReturnType() { + return returnType; + } + + /** + * Sets the return collection type of the method. + * + * @param returnCollectionType the return collection type of the method + */ + public void setReturnCollectionType(String returnCollectionType) { + this.returnCollectionType = returnCollectionType; + } + + /** + * Gets the return collection type of the method. + * + * @return the return collection type of the method + */ + public String getReturnCollectionType() { + return returnCollectionType; + } + + /** + * Adds a new parameter to the method. + * + * @param parameter the parameter to be added to the method + */ + public void addParameter(Parameter parameter) { + this.parameters.add(parameter); + } + + /** + * Adds a set of new parameters to the method. + * + * @param parameters the set of parameters to be added to the method + */ + public void addParameters(Set parameters) { + this.parameters.addAll(parameters); + } + + /** + * Gets the parameters of the method. + * + * @return the set of parameters of the method + */ + public Set getParameters() { + return new HashSet<>(parameters); + } + + /** + * Gets the parent domain object containing this attribute. + * + * @return the parent domain object containing this attribute + */ + public DomainObject getParent() { + return parent; + } + + /** + * Sets the parent domain object containing this attribute. + * + * @param parent the parent domain object containing this attribute + */ + public void setParent(DomainObject parent) { + this.parent = parent; + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof Method)) + return false; + + Method method = (Method) object; + + return new EqualsBuilder() + .append(parent, method.parent) + .append(name, method.name) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder() + .append(parent) + .append(name) + .hashCode(); + } +} diff --git a/src/main/java/org/contextmapper/discovery/model/Parameter.java b/src/main/java/org/contextmapper/discovery/model/Parameter.java new file mode 100644 index 0000000..f4d2d81 --- /dev/null +++ b/src/main/java/org/contextmapper/discovery/model/Parameter.java @@ -0,0 +1,69 @@ +/* + * Copyright 2019 The Context Mapper Project Team + * + * 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 org.contextmapper.discovery.model; + +/** + * Represents a parameter of a domain object method. + * + * @author Stefan Kapferer + */ +public class Parameter { + + private String name; + private DomainObject type; + private String collectionType; + + public Parameter(String name, DomainObject type) { + this.name = name; + this.type = type; + } + + /** + * Gets the name of the parameter. + * + * @return the name of the parameter + */ + public String getName() { + return name; + } + + /** + * Gets the type of the parameter. + * + * @return the type of the parameter + */ + public DomainObject getType() { + return type; + } + + /** + * Sets the collection type of the parameter + * + * @param collectionType the collection type of the parameter + */ + public void setCollectionType(String collectionType) { + this.collectionType = collectionType; + } + + /** + * Gets the collection type of the parameter. + * + * @return the collection type of the parameter + */ + public String getCollectionType() { + return collectionType; + } +} diff --git a/src/main/java/org/contextmapper/discovery/model/Reference.java b/src/main/java/org/contextmapper/discovery/model/Reference.java index ef5b5cc..c02ea9f 100644 --- a/src/main/java/org/contextmapper/discovery/model/Reference.java +++ b/src/main/java/org/contextmapper/discovery/model/Reference.java @@ -25,12 +25,12 @@ */ public class Reference { - private Entity parent; + private DomainObject parent; private String name; - private Entity type; + private DomainObject type; private String collectionType; - public Reference(Entity type, String name) { + public Reference(DomainObject type, String name) { this.type = type; this.name = name; } @@ -45,11 +45,11 @@ public String getName() { } /** - * Gets the type ({@link Entity}) of the reference attribute. + * Gets the type ({@link DomainObject}) of the reference attribute. * - * @return the type ({@link Entity}) of the reference attribute + * @return the type ({@link DomainObject}) of the reference attribute */ - public Entity getType() { + public DomainObject getType() { return type; } @@ -58,7 +58,7 @@ public Entity getType() { * * @return the parent entity containing this reference attribute */ - public Entity getParent() { + public DomainObject getParent() { return parent; } @@ -67,7 +67,7 @@ public Entity getParent() { * * @param parent the parent entity containing this reference attribute */ - public void setParent(Entity parent) { + public void setParent(DomainObject parent) { this.parent = parent; } diff --git a/src/main/java/org/contextmapper/discovery/strategies/boundedcontexts/AbstractRESTResourceBasedBoundedContextDiscoveryStrategy.java b/src/main/java/org/contextmapper/discovery/strategies/boundedcontexts/AbstractRESTResourceBasedBoundedContextDiscoveryStrategy.java new file mode 100644 index 0000000..ca53d01 --- /dev/null +++ b/src/main/java/org/contextmapper/discovery/strategies/boundedcontexts/AbstractRESTResourceBasedBoundedContextDiscoveryStrategy.java @@ -0,0 +1,308 @@ +/* + * Copyright 2019 The Context Mapper Project Team + * + * 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 org.contextmapper.discovery.strategies.boundedcontexts; + +import org.contextmapper.discovery.model.*; +import org.contextmapper.discovery.strategies.helper.ReflectionHelpers; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.*; +import java.util.stream.Collectors; + +public abstract class AbstractRESTResourceBasedBoundedContextDiscoveryStrategy extends AbstractBoundedContextDiscoveryStrategy { + + private static final String AGG_ROOT_ENTITY_POSTFIX = "_RootEntity"; + + protected Set aggregateNames; + protected ReflectionHelpers reflectionHelpers; + protected Map, DomainObject>> domainObjectMap; + protected Set discoveredDomainObjectNames; + + public AbstractRESTResourceBasedBoundedContextDiscoveryStrategy() { + this.aggregateNames = new HashSet<>(); + this.reflectionHelpers = new ReflectionHelpers(); + this.domainObjectMap = new HashMap<>(); + this.discoveredDomainObjectNames = new HashSet<>(); + } + + /** + * Discover Bounded Contexts by certain types representing the contexts. + */ + @Override + public Set discoverBoundedContexts() { + Set set = new HashSet<>(); + for (Class type : findBoundedContextTypes()) { + String name = type.getSimpleName(); + if (name.endsWith("Application")) + name = name.substring(0, name.length() - 11); + BoundedContext bc = createBoundedContext(name, findBoundedContextTechnology(type)); + bc.addAggregates(discoverAggregates(bc, type.getPackage().getName())); + set.add(bc); + } + updateDomainObjectAttributesAndReferences(); + return set; + } + + /** + * Find types representing a Bounded Context. + */ + protected abstract Set> findBoundedContextTypes(); + + /** + * Find the implementation technology of a Bounded Context by the type representing it. + */ + protected abstract String findBoundedContextTechnology(Class boundedContextType); + + /** + * Find types representing an Aggregate/resource (within a given package). + */ + protected abstract Set> findResourceTypes(String packageName); + + /** + * Find RESTful HTTP resource path by the given resource type. + */ + protected abstract String findResourcePath(Class resourceType); + + /** + * Find RESTful HTTP operations by it methods in a given resource type. + */ + protected abstract Set findResourceMethods(Class resourceType); + + /** + * Discover Aggregates for RESTful HTTP resources (annotated types) + */ + protected Set discoverAggregates(BoundedContext bc, String packageName) { + Set resultSet = new HashSet<>(); + for (Class type : findResourceTypes(packageName)) { + String resourePath = findResourcePath(type); + if (resourePath == null || "".equals(resourePath)) + continue; + Aggregate aggregate = createAggregate(bc, resourePath); + this.domainObjectMap.put(aggregate, new HashMap<>()); + aggregate.addDomainObject(createRootEntity(aggregate.getName())); + aggregate.addDomainObjects(discoverValueObjectsByMethods(aggregate, type, packageName)); + aggregate.setDiscoveryComment("This Aggregate has been created on the basis of the RESTful HTTP controller " + type.getName() + "."); + resultSet.add(aggregate); + } + return resultSet; + } + + /** + * Create an Aggregate for a RESTful HTTP endpoint/resource. + */ + protected Aggregate createAggregate(BoundedContext parentContext, String resourcePath) { + return new Aggregate(getAggregateName(parentContext.getName(), resourcePath)); + } + + protected DomainObject createRootEntity(String aggregateName) { + return new DomainObject(DomainObjectType.ENTITY, aggregateName + AGG_ROOT_ENTITY_POSTFIX); + } + + private String getAggregateName(String boundedContextName, String resourcePath) { + String name = resourcePath; + if (name.startsWith("/")) + name = name.substring(1); + name = name.replaceAll("/", "_"); + name = name.replaceAll("-", "_"); + + if (this.aggregateNames.contains(name)) + name = boundedContextName + "_" + name; + + int counter = 1; + while (this.aggregateNames.contains(name)) { + name = name + "_" + counter; + counter++; + } + + this.aggregateNames.add(name); + return name; + } + + protected Set discoverValueObjectsByMethods(Aggregate aggregate, Class controllerType, String packageName) { + Set valueObjects = new HashSet<>(); + for (Method method : findResourceMethods(controllerType)) { + org.contextmapper.discovery.model.Method aggRootMethod = new org.contextmapper.discovery.model.Method(method.getName()); + DiscoveredType returnType = getMethodReturnType(method, packageName); + if (returnType != null) { + DomainObject returnTypeObject = createValueObjectFromType(aggregate, returnType.domainType); + valueObjects.add(returnTypeObject); + aggRootMethod.setReturnType(returnTypeObject); + aggRootMethod.setReturnCollectionType(returnType.collectionType); + } + Set parameterTypes = getMethodParameterTypes(method, packageName); + Set parameterTypeObjects = createValueObjectParameters(aggregate, parameterTypes.toArray(new DiscoveredParameterType[parameterTypes.size()])); + valueObjects.addAll(parameterTypeObjects.stream().map(p -> p.getType()).collect(Collectors.toSet())); + aggRootMethod.addParameters(parameterTypeObjects); + Optional aggRootEntity = aggregate.getDomainObjects().stream().filter(o -> o.getName().endsWith(AGG_ROOT_ENTITY_POSTFIX)).findFirst(); + if (aggRootEntity.isPresent()) + aggRootEntity.get().addMethod(aggRootMethod); + } + return valueObjects; + } + + private DiscoveredType getMethodReturnType(Method method, String packageName) { + DiscoveredType returnType = null; + if (method.getGenericReturnType() instanceof ParameterizedType) { + returnType = getType((ParameterizedType) method.getGenericReturnType()); + } else { + returnType = new DiscoveredType(null, method.getReturnType()); + } + if (returnType != null && returnType.domainType.getPackage().getName().startsWith(packageName)) + return returnType; + return null; + } + + protected Set getMethodParameterTypes(Method method, String packageName) { + Set parameterTypes = new HashSet<>(); + for (java.lang.reflect.Parameter parameter : method.getParameters()) { + if (parameter.getParameterizedType() instanceof ParameterizedType) { + parameterTypes.add(new DiscoveredParameterType(parameter.getName(), getType((ParameterizedType) parameter.getParameterizedType()))); + } else { + parameterTypes.add(new DiscoveredParameterType(parameter.getName(), new DiscoveredType(null, parameter.getType()))); + } + } + return parameterTypes.stream().filter(p -> p.type.domainType.getPackage().getName().startsWith(packageName)).collect(Collectors.toSet()); + } + + private Set createValueObjectParameters(Aggregate aggregate, DiscoveredParameterType... parameterTypes) { + Set valueObjectParameters = new HashSet<>(); + for (DiscoveredParameterType parameterType : parameterTypes) { + Parameter parameter = new Parameter(parameterType.parameterName, createValueObjectFromType(aggregate, parameterType.type.domainType)); + parameter.setCollectionType(parameterType.type.collectionType); + valueObjectParameters.add(parameter); + } + return valueObjectParameters; + } + + private DomainObject createValueObjectFromType(Aggregate aggregate, Class type) { + if (this.domainObjectMap.get(aggregate).containsKey(type)) + return this.domainObjectMap.get(aggregate).get(type); + + String valueObjectName = type.getSimpleName(); + if (this.discoveredDomainObjectNames.contains(valueObjectName)) + valueObjectName = aggregate.getName() + "_" + valueObjectName; + int counter = 1; + while (this.discoveredDomainObjectNames.contains(valueObjectName)) { + valueObjectName = valueObjectName + "_" + counter; + counter++; + } + this.discoveredDomainObjectNames.add(valueObjectName); + DomainObject domainObject = new DomainObject(DomainObjectType.VALUE_OBJECT, valueObjectName, type.getName()); + domainObject.setDiscoveryComment("This value object has been derived from the class " + type.getName() + "."); + this.domainObjectMap.get(aggregate).put(type, domainObject); + return domainObject; + } + + private void updateDomainObjectAttributesAndReferences() { + for (Map.Entry, DomainObject>> entry : this.domainObjectMap.entrySet()) { + entry.getValue().entrySet().forEach(e -> createAttributesAndReferences4DomainObject(e.getValue(), e.getKey())); + } + } + + private void createAttributesAndReferences4DomainObject(DomainObject domainObject, Class domainObjectType) { + for (Field field : reflectionHelpers.getAllFieldsOfType(domainObjectType)) { + String collectionType = null; + if (reflectionHelpers.isCollectionType(field.getType())) + collectionType = field.getType().getSimpleName(); + Class fieldType = getType(field); + String simpleName = fieldType.getSimpleName(); + if (fieldType.getSimpleName().endsWith("[]")) { + simpleName = simpleName.substring(0, simpleName.length() - 2); + collectionType = "List"; + } + + // reference outside aggregate (only use this if object is not part of aggregate) + DomainObject globallySearchedObject = searchDomainObjectInAllAggregates(fieldType); + + // search in aggregate first: + if (this.domainObjectMap.get(domainObject.getParent()).containsKey(fieldType)) { + domainObject.addReference(createReference(field.getName(), this.domainObjectMap.get(domainObject.getParent()).get(fieldType), collectionType)); + } else if (globallySearchedObject != null) { + domainObject.addReference(createReference(field.getName(), globallySearchedObject, collectionType)); + } else { + Attribute attribute = new Attribute(simpleName, field.getName()); + attribute.setCollectionType(collectionType); + domainObject.addAttribute(attribute); + } + } + } + + private DomainObject searchDomainObjectInAllAggregates(Class type) { + for (Map.Entry, DomainObject>> entry : this.domainObjectMap.entrySet()) { + if (entry.getValue().containsKey(type)) + return entry.getValue().get(type); + } + return null; + } + + private Reference createReference(String name, DomainObject domainObject, String collectionType) { + Reference reference = new Reference(domainObject, name); + reference.setCollectionType(collectionType); + domainObject.addReference(reference); + return reference; + } + + private Class getType(Field field) { + if (reflectionHelpers.isCollectionType(field.getType())) { + return reflectionHelpers.getActualTypesOfParameterizedType((ParameterizedType) field.getGenericType()).iterator().next(); + } else { + return field.getType(); + } + } + + private DiscoveredType getType(ParameterizedType type) { + if (type.getActualTypeArguments().length < 1) + throw new RuntimeException("ParameterizedTypes without parameters not supported!"); + + // we assume there is only one return type for now + Type paramType = type.getActualTypeArguments()[0]; + + if (paramType instanceof ParameterizedType) { + return getType((ParameterizedType) paramType); + } else if (paramType instanceof Class) { + String collectionType = null; + if (type.getRawType() instanceof Class && reflectionHelpers.isCollectionType((Class) type.getRawType())) + collectionType = ((Class) type.getRawType()).getSimpleName(); + return new DiscoveredType(collectionType, (Class) paramType); + } else { + return new DiscoveredType(null, (Class) type.getRawType()); + } + } + + private class DiscoveredType { + private Class domainType; + private String collectionType; + + DiscoveredType(String collectionType, Class domainType) { + this.domainType = domainType; + this.collectionType = collectionType; + } + } + + private class DiscoveredParameterType { + private DiscoveredType type; + private String parameterName; + + DiscoveredParameterType(String parameterName, DiscoveredType discoveredType) { + this.parameterName = parameterName; + this.type = discoveredType; + } + } + +} diff --git a/src/main/java/org/contextmapper/discovery/strategies/boundedcontexts/SpringBootBoundedContextDiscoveryStrategy.java b/src/main/java/org/contextmapper/discovery/strategies/boundedcontexts/SpringBootBoundedContextDiscoveryStrategy.java index 10a684a..82ca81c 100644 --- a/src/main/java/org/contextmapper/discovery/strategies/boundedcontexts/SpringBootBoundedContextDiscoveryStrategy.java +++ b/src/main/java/org/contextmapper/discovery/strategies/boundedcontexts/SpringBootBoundedContextDiscoveryStrategy.java @@ -15,161 +15,49 @@ */ package org.contextmapper.discovery.strategies.boundedcontexts; -import org.contextmapper.discovery.model.*; import org.contextmapper.discovery.strategies.helper.AnnotationScanner; -import org.contextmapper.discovery.strategies.helper.ReflectionHelpers; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; -import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.util.*; -import java.util.stream.Collectors; +import java.util.Set; -public class SpringBootBoundedContextDiscoveryStrategy extends AbstractBoundedContextDiscoveryStrategy implements BoundedContextDiscoveryStrategy { +public class SpringBootBoundedContextDiscoveryStrategy extends AbstractRESTResourceBasedBoundedContextDiscoveryStrategy implements BoundedContextDiscoveryStrategy { private String packageName; - private Set aggregateNames; - private Map, Entity> entityMap; - private ReflectionHelpers reflectionHelpers; - private Set discoveredEntityNames; public SpringBootBoundedContextDiscoveryStrategy(String packageName) { this.packageName = packageName; - this.aggregateNames = new HashSet<>(); - this.entityMap = new HashMap<>(); - this.reflectionHelpers = new ReflectionHelpers(); - this.discoveredEntityNames = new HashSet<>(); } @Override - public Set discoverBoundedContexts() { - Set set = new HashSet<>(); - for (Class type : new AnnotationScanner().scanForAnnotatedType(packageName, SpringBootApplication.class)) { - String name = type.getSimpleName(); - if (name.endsWith("Application")) - name = name.substring(0, name.length() - 11); - BoundedContext bc = createBoundedContext(name, "Spring Boot"); - bc.addAggregates(discoverAggregates(bc, type.getPackage().getName())); - set.add(bc); - } - updateEntityAttributesAndReferences(); - return set; + protected Set> findBoundedContextTypes() { + return new AnnotationScanner().scanForAnnotatedType(packageName, SpringBootApplication.class); } - private Set discoverAggregates(BoundedContext bc, String packageName) { - Set resultSet = new HashSet<>(); - for (Class type : new AnnotationScanner().scanForAnnotatedType(packageName, RequestMapping.class)) { - RequestMapping requestMapping = type.getAnnotation(RequestMapping.class); - if (requestMapping.value().length > 0) { - Aggregate aggregate = createAggregate(bc, requestMapping.value()[0]); - aggregate.addEntities(discoverEntities(aggregate.getName(), type)); - aggregate.setDiscoveryComment("This Aggregate has been created on the basis of the Spring REST controller " + type.getName() + "."); - resultSet.add(aggregate); - } - } - return resultSet; - } - - private Aggregate createAggregate(BoundedContext parentContext, String resourcePath) { - return new Aggregate(getAggregateName(parentContext.getName(), resourcePath)); - } - - private String getAggregateName(String boundedContextName, String resourcePath) { - String name = resourcePath; - if (name.startsWith("/")) - name = name.substring(1); - name = name.replaceAll("/", "_"); - name = name.replaceAll("-", "_"); - - if (this.aggregateNames.contains(name)) - name = boundedContextName + "_" + name; - - int counter = 1; - while (this.aggregateNames.contains(name)) { - name = name + "_" + counter; - counter++; - } - - this.aggregateNames.add(name); - return name; - } - - private Set discoverEntities(String aggregateName, Class controllerType) { - Set entities = new HashSet<>(); - Set> types = getInputAndOutputTypesOfResourceMethods(controllerType); - for (Class type : types) { - entities.add(createEntityFromType(aggregateName, type)); - } - return entities; - } - - private Set> getInputAndOutputTypesOfResourceMethods(Class type) { - Set> inputOutputTypes = new HashSet<>(); - for (Method method : new AnnotationScanner().scanForAnnotatedMethods(type, PutMapping.class, GetMapping.class, RequestMapping.class)) { - if (method.getGenericReturnType() instanceof ParameterizedType) - inputOutputTypes.addAll(reflectionHelpers.getActualTypesOfParameterizedType((ParameterizedType) method.getGenericReturnType())); - - inputOutputTypes.add(method.getGenericReturnType().getClass()); - inputOutputTypes.addAll(Arrays.asList(method.getParameterTypes())); - } - return inputOutputTypes.stream().filter(c -> c.getPackage().getName().startsWith(packageName)).collect(Collectors.toSet()); - } - - private Entity createEntityFromType(String aggregateName, Class type) { - String entityName = type.getSimpleName(); - if (this.discoveredEntityNames.contains(entityName)) - entityName = aggregateName + "_" + entityName; - int counter = 1; - while (this.discoveredEntityNames.contains(entityName)) { - entityName = entityName + "_" + counter; - counter++; - } - this.discoveredEntityNames.add(entityName); - Entity entity = new Entity(type.getName(), entityName); - entity.setDiscoveryComment("This entity has been derived from the class " + type.getName() + "."); - this.entityMap.put(type, entity); - return entity; + @Override + protected String findBoundedContextTechnology(Class boundedContextType) { + return "Spring Boot"; } - private void updateEntityAttributesAndReferences() { - for (Map.Entry, Entity> entry : this.entityMap.entrySet()) { - createAttributesAndReferences4Entity(entry.getValue(), entry.getKey()); - } + @Override + protected Set> findResourceTypes(String packageName) { + return new AnnotationScanner().scanForAnnotatedType(packageName, RequestMapping.class); } - private void createAttributesAndReferences4Entity(Entity entity, Class entityType) { - for (Field field : reflectionHelpers.getAllFieldsOfType(entityType)) { - String collectionType = null; - if (reflectionHelpers.isCollectionType(field.getType())) - collectionType = field.getType().getSimpleName(); - Class fieldType = getType(field); - String simpleName = fieldType.getSimpleName(); - if (fieldType.getSimpleName().endsWith("[]")) { - simpleName = simpleName.substring(0, simpleName.length() - 2); - collectionType = "List"; - } - if (this.entityMap.containsKey(fieldType)) { - Reference reference = new Reference(this.entityMap.get(fieldType), field.getName()); - reference.setCollectionType(collectionType); - entity.addReference(reference); - } else { - Attribute attribute = new Attribute(simpleName, field.getName()); - attribute.setCollectionType(collectionType); - entity.addAttribute(attribute); - } - } + @Override + protected String findResourcePath(Class resourceType) { + RequestMapping requestMapping = resourceType.getAnnotation(RequestMapping.class); + if (requestMapping.value().length > 0) + return requestMapping.value()[0]; + return ""; } - private Class getType(Field field) { - if (reflectionHelpers.isCollectionType(field.getType())) { - return reflectionHelpers.getActualTypesOfParameterizedType((ParameterizedType) field.getGenericType()).iterator().next(); - } else { - return field.getType(); - } + @Override + protected Set findResourceMethods(Class resourceType) { + return new AnnotationScanner().scanForAnnotatedMethods(resourceType, PutMapping.class, GetMapping.class, RequestMapping.class); } } diff --git a/src/test/java/org/contextmapper/discovery/cml/ContextMapToCMLConverterTest.java b/src/test/java/org/contextmapper/discovery/cml/ContextMapToCMLConverterTest.java index 44d4cdd..0894e4a 100644 --- a/src/test/java/org/contextmapper/discovery/cml/ContextMapToCMLConverterTest.java +++ b/src/test/java/org/contextmapper/discovery/cml/ContextMapToCMLConverterTest.java @@ -16,14 +16,20 @@ package org.contextmapper.discovery.cml; import org.contextmapper.discovery.ContextMapDiscoverer; +import org.contextmapper.discovery.model.DomainObject; import org.contextmapper.discovery.strategies.boundedcontexts.SpringBootBoundedContextDiscoveryStrategy; import org.contextmapper.discovery.strategies.names.SeparatorToCamelCaseBoundedContextNameMappingStrategy; import org.contextmapper.discovery.strategies.relationships.DockerComposeRelationshipDiscoveryStrategy; import org.contextmapper.dsl.contextMappingDSL.*; +import org.contextmapper.tactic.dsl.tacticdsl.DomainObjectOperation; import org.contextmapper.tactic.dsl.tacticdsl.Entity; +import org.contextmapper.tactic.dsl.tacticdsl.Parameter; +import org.contextmapper.tactic.dsl.tacticdsl.ValueObject; import org.junit.jupiter.api.Test; import java.io.File; +import java.util.Set; +import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; @@ -76,11 +82,11 @@ public void canConvertAggregates() { assertEquals(1, bc.getAggregates().size()); Aggregate aggregate = bc.getAggregates().get(0); assertEquals("customers", aggregate.getName()); - assertEquals("// This Aggregate has been created on the basis of the Spring REST controller test.application.spring.boot.interfaces.CustomerInformationHolder.", aggregate.getComment()); + assertEquals("// This Aggregate has been created on the basis of the RESTful HTTP controller test.application.spring.boot.interfaces.CustomerInformationHolder.", aggregate.getComment()); } @Test - public void canConvertEntities() { + public void canConvertDomainObjects() { // given ContextMapDiscoverer discoverer = new ContextMapDiscoverer() .usingBoundedContextDiscoveryStrategies( @@ -97,12 +103,78 @@ public void canConvertEntities() { assertEquals(1, bc.getAggregates().size()); Aggregate aggregate = bc.getAggregates().get(0); assertEquals("customers", aggregate.getName()); - assertEquals(3, aggregate.getDomainObjects().size()); - Entity addressEntity = (Entity) aggregate.getDomainObjects().stream().filter(o -> o.getName().equals("Address")).findFirst().get(); - assertNotNull(addressEntity); - assertNotNull(aggregate.getDomainObjects().stream().filter(o -> o.getName().equals("CustomerId")).findFirst().get()); - assertNotNull(aggregate.getDomainObjects().stream().filter(o -> o.getName().equals("Customer")).findFirst().get()); - assertEquals("// This entity has been derived from the class test.application.spring.boot.model.Address.", addressEntity.getComment()); + Set resourceValueObjects = aggregate.getDomainObjects().stream().filter(o -> o instanceof ValueObject).map(o -> (ValueObject) o).collect(Collectors.toSet()); + assertEquals(3, resourceValueObjects.size()); + ValueObject addressValueObject = (ValueObject) resourceValueObjects.stream().filter(o -> o.getName().equals("Address")).findFirst().get(); + assertNotNull(addressValueObject); + assertNotNull(resourceValueObjects.stream().filter(o -> o.getName().equals("CustomerId")).findFirst().get()); + assertNotNull(resourceValueObjects.stream().filter(o -> o.getName().equals("Customer")).findFirst().get()); + assertEquals("// This value object has been derived from the class test.application.spring.boot.model.Address.", addressValueObject.getComment()); + } + + @Test + public void canConvertRootEntity() { + // given + ContextMapDiscoverer discoverer = new ContextMapDiscoverer() + .usingBoundedContextDiscoveryStrategies( + new SpringBootBoundedContextDiscoveryStrategy("test.application.spring.boot") + ); + org.contextmapper.discovery.model.ContextMap contextMap = discoverer.discoverContextMap(); + + // when + ContextMappingModel model = new ContextMapToCMLConverter().convert(contextMap); + + // then + BoundedContext bc = model.getBoundedContexts().iterator().next(); + Aggregate aggregate = bc.getAggregates().get(0); + Set entities = aggregate.getDomainObjects().stream().filter(o -> o instanceof Entity).map(o -> (Entity) o).collect(Collectors.toSet()); + assertEquals(1, entities.size()); + Entity rootEntity = entities.iterator().next(); + assertEquals("customers_RootEntity", rootEntity.getName()); + assertTrue(rootEntity.isAggregateRoot()); + } + + @Test + public void canConvertRootEntityMethods() { + // given + ContextMapDiscoverer discoverer = new ContextMapDiscoverer() + .usingBoundedContextDiscoveryStrategies( + new SpringBootBoundedContextDiscoveryStrategy("test.application.spring.boot") + ); + org.contextmapper.discovery.model.ContextMap contextMap = discoverer.discoverContextMap(); + + // when + ContextMappingModel model = new ContextMapToCMLConverter().convert(contextMap); + + // then + BoundedContext bc = model.getBoundedContexts().iterator().next(); + Aggregate aggregate = bc.getAggregates().get(0); + Set entities = aggregate.getDomainObjects().stream().filter(o -> o instanceof Entity).map(o -> (Entity) o).collect(Collectors.toSet()); + Entity rootEntity = entities.iterator().next(); + assertEquals(3, rootEntity.getOperations().size()); + DomainObjectOperation changeAddress = rootEntity.getOperations().stream().filter(o -> o.getName().equals("changeAddress")).findFirst().get(); + DomainObjectOperation getCustomer = rootEntity.getOperations().stream().filter(o -> o.getName().equals("getCustomer")).findFirst().get(); + DomainObjectOperation getCustomers = rootEntity.getOperations().stream().filter(o -> o.getName().equals("getCustomers")).findFirst().get(); + assertNotNull(changeAddress); + assertNotNull(getCustomer); + assertNotNull(getCustomers); + assertEquals("Address", changeAddress.getReturnType().getDomainObjectType().getName()); + assertEquals("Customer", getCustomer.getReturnType().getDomainObjectType().getName()); + assertEquals("Customer", getCustomers.getReturnType().getDomainObjectType().getName()); + assertEquals("List", getCustomers.getReturnType().getCollectionType().getName()); + Parameter customerIdParam = changeAddress.getParameters().stream().filter(p -> p.getName().equals("arg0")).findFirst().get(); + Parameter requestDtoParam = changeAddress.getParameters().stream().filter(p -> p.getName().equals("arg1")).findFirst().get(); + Parameter customerIdParam2 = getCustomer.getParameters().stream().filter(p -> p.getName().equals("arg0")).findFirst().get(); + Parameter customerIdsParam = getCustomers.getParameters().stream().filter(p -> p.getName().equals("arg0")).findFirst().get(); + assertNotNull(customerIdParam); + assertNotNull(requestDtoParam); + assertNotNull(customerIdParam2); + assertNotNull(customerIdsParam); + assertEquals("CustomerId", customerIdParam.getParameterType().getDomainObjectType().getName()); + assertEquals("Address", requestDtoParam.getParameterType().getDomainObjectType().getName()); + assertEquals("CustomerId", customerIdParam2.getParameterType().getDomainObjectType().getName()); + assertEquals("CustomerId", customerIdsParam.getParameterType().getDomainObjectType().getName()); + assertEquals("List", customerIdsParam.getParameterType().getCollectionType().getName()); } @Test @@ -122,10 +194,10 @@ public void canConvertAttributes() { BoundedContext bc = model.getBoundedContexts().iterator().next(); assertEquals(1, bc.getAggregates().size()); Aggregate aggregate = bc.getAggregates().get(0); - Entity addressEntity = (Entity) aggregate.getDomainObjects().stream().filter(e -> e.getName().equals("Address")).findFirst().get(); - assertNotNull(addressEntity); - assertEquals(4, addressEntity.getAttributes().size()); - assertNotNull(addressEntity.getAttributes().stream().filter(a -> a.getName().equals("street")).findFirst().get()); + ValueObject addressValueObject = (ValueObject) aggregate.getDomainObjects().stream().filter(e -> e.getName().equals("Address")).findFirst().get(); + assertNotNull(addressValueObject); + assertEquals(4, addressValueObject.getAttributes().size()); + assertNotNull(addressValueObject.getAttributes().stream().filter(a -> a.getName().equals("street")).findFirst().get()); } @Test @@ -145,10 +217,10 @@ public void canConvertReferences() { BoundedContext bc = model.getBoundedContexts().iterator().next(); assertEquals(1, bc.getAggregates().size()); Aggregate aggregate = bc.getAggregates().get(0); - Entity customerEntity = (Entity) aggregate.getDomainObjects().stream().filter(e -> e.getName().equals("Customer")).findFirst().get(); - assertNotNull(customerEntity); - assertEquals(4, customerEntity.getReferences().size()); - assertEquals("CustomerId", customerEntity.getReferences().stream().filter(r -> r.getName().equals("id")).findFirst().get() + ValueObject customerValueObject = (ValueObject) aggregate.getDomainObjects().stream().filter(e -> e.getName().equals("Customer")).findFirst().get(); + assertNotNull(customerValueObject); + assertEquals(4, customerValueObject.getReferences().size()); + assertEquals("CustomerId", customerValueObject.getReferences().stream().filter(r -> r.getName().equals("id")).findFirst().get() .getDomainObjectType().getName()); } diff --git a/src/test/java/org/contextmapper/discovery/model/AggregateTest.java b/src/test/java/org/contextmapper/discovery/model/AggregateTest.java index 18921ba..8a61534 100644 --- a/src/test/java/org/contextmapper/discovery/model/AggregateTest.java +++ b/src/test/java/org/contextmapper/discovery/model/AggregateTest.java @@ -49,16 +49,16 @@ public void canCreateAggregateWithName() { } @Test - public void canAddEntity() { + public void canAddDomainObject() { // given Aggregate aggregate = new Aggregate("TestAggregate"); // when - aggregate.addEntity(new Entity("test.Entity", "Entity")); + aggregate.addDomainObject(new DomainObject(DomainObjectType.ENTITY, "Entity", "test.Entity")); // then - assertEquals(1, aggregate.getEntities().size()); - assertEquals(new Entity("test.Entity", "Entity"), aggregate.getEntities().iterator().next()); + assertEquals(1, aggregate.getDomainObjects().size()); + assertEquals(new DomainObject(DomainObjectType.ENTITY, "Entity", "test.Entity"), aggregate.getDomainObjects().iterator().next()); } @Test diff --git a/src/test/java/org/contextmapper/discovery/model/DomainObjectTest.java b/src/test/java/org/contextmapper/discovery/model/DomainObjectTest.java new file mode 100644 index 0000000..7f7dd43 --- /dev/null +++ b/src/test/java/org/contextmapper/discovery/model/DomainObjectTest.java @@ -0,0 +1,168 @@ +/* + * Copyright 2019 The Context Mapper Project Team + * + * 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 org.contextmapper.discovery.model; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.contextmapper.discovery.model.DomainObjectType.ENTITY; +import static org.contextmapper.discovery.model.DomainObjectType.VALUE_OBJECT; +import static org.junit.jupiter.api.Assertions.*; + +public class DomainObjectTest { + + @Test + public void canCreateEntity() { + // given + DomainObject domainObject; + + // when + domainObject = new DomainObject(ENTITY, "Customer", "test.Customer"); + + // then + assertEquals("test.Customer", domainObject.getOriginalType()); + assertEquals("Customer", domainObject.getName()); + } + + @Test + public void canAddAttribute() { + // given + DomainObject domainObject = new DomainObject(ENTITY, "Name", "Type"); + + // when + Attribute attribute = new Attribute("String", "attr1"); + domainObject.addAttribute(attribute); + + // then + assertEquals(1, domainObject.getAttributes().size()); + Attribute attribute1 = domainObject.getAttributes().iterator().next(); + assertEquals(attribute, attribute1); + assertEquals(domainObject, attribute1.getParent()); + assertEquals("String", attribute1.getType()); + assertEquals("attr1", attribute1.getName()); + assertFalse(attribute1.equals(new Object())); + } + + @Test + public void canAddReference() { + // given + DomainObject domainObject = new DomainObject(ENTITY, "Name", "test.Entity"); + DomainObject referencedDomainObject = new DomainObject(ENTITY, "ReferencedType", "test.ReferencedType"); + + // when + Reference reference = new Reference(referencedDomainObject, "reference"); + domainObject.addReference(reference); + + // then + assertEquals(1, domainObject.getReferences().size()); + Reference reference1 = domainObject.getReferences().iterator().next(); + assertEquals(reference, reference1); + assertEquals(domainObject, reference1.getParent()); + assertEquals(referencedDomainObject, reference1.getType()); + assertEquals("reference", reference1.getName()); + assertFalse(reference1.equals(new Object())); + } + + @Test + public void canAddMethod() { + // given + DomainObject domainObject = new DomainObject(ENTITY, "Name", "test.Entity"); + Method method = new Method("testMethod"); + + // when + domainObject.addMethod(method); + + // then + assertEquals(1, domainObject.getMethods().size()); + Method resultMethod = domainObject.getMethods().iterator().next(); + assertEquals("testMethod", resultMethod.getName()); + assertEquals(domainObject, resultMethod.getParent()); + assertEquals(resultMethod, method); + assertFalse(resultMethod.equals(new Object())); + } + + @Test + public void cannotCreateEntityWithNullName() { + Assertions.assertThrows(IllegalArgumentException.class, () -> { + new DomainObject(ENTITY, null, "Type"); + }); + } + + @Test + public void cannotCreateEntityWithEmptyName() { + Assertions.assertThrows(IllegalArgumentException.class, () -> { + new DomainObject(ENTITY, "", "Type"); + }); + } + + @Test + public void cannotCreateEntityWithNullType() { + Assertions.assertThrows(IllegalArgumentException.class, () -> { + new DomainObject(null, "Name"); + }); + } + + @Test + public void entitiesWithSameTypeAndNameAreEqual() { + // given + DomainObject domainObject1 = new DomainObject(ENTITY, "Name", "Type"); + DomainObject domainObject2 = new DomainObject(ENTITY, "Name", "Type"); + + // when + boolean equals = domainObject1.equals(domainObject1); + + // then + assertTrue(equals); + } + + @Test + public void otherObjectsAreNotEqual() { + // given + DomainObject domainObject = new DomainObject(ENTITY, "Name", "Type"); + + // when + boolean equals = domainObject.equals(new Object()); + + // then + assertFalse(equals); + } + + @Test + public void canAddDiscoveryComment() { + // given + DomainObject domainObject = new DomainObject(ENTITY, "Type", "test.Type"); + String discoveryComment = "This entity has been derived from the class test.Type."; + + // when + domainObject.setDiscoveryComment(discoveryComment); + + // then + assertEquals(discoveryComment, domainObject.getDiscoveryComment()); + } + + @Test + public void canSetType() { + // given + DomainObject domainObject = new DomainObject(VALUE_OBJECT, "TestType", "org.contextmapper.TestType"); + + // when + domainObject.setType(ENTITY); + + // then + assertEquals(ENTITY, domainObject.getType()); + } + +} diff --git a/src/test/java/org/contextmapper/discovery/model/EntityTest.java b/src/test/java/org/contextmapper/discovery/model/EntityTest.java deleted file mode 100644 index c75f83a..0000000 --- a/src/test/java/org/contextmapper/discovery/model/EntityTest.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2019 The Context Mapper Project Team - * - * 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 org.contextmapper.discovery.model; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -public class EntityTest { - - @Test - public void canCreateEntity() { - // given - Entity entity; - - // when - entity = new Entity("test.Customer", "Customer"); - - // then - assertEquals("test.Customer", entity.getType()); - assertEquals("Customer", entity.getName()); - } - - @Test - public void canAddAttribute() { - // given - Entity entity = new Entity("Type", "Name"); - - // when - Attribute attribute = new Attribute("String", "attr1"); - entity.addAttribute(attribute); - - // then - assertEquals(1, entity.getAttributes().size()); - Attribute attribute1 = entity.getAttributes().iterator().next(); - assertEquals(attribute, attribute1); - assertEquals(entity, attribute1.getParent()); - assertEquals("String", attribute1.getType()); - assertEquals("attr1", attribute1.getName()); - assertFalse(attribute1.equals(new Object())); - } - - @Test - public void canAddReference() { - // given - Entity entity = new Entity("test.Entity", "Name"); - Entity referencedEntity = new Entity("test.ReferencedType", "ReferencedType"); - - // when - Reference reference = new Reference(referencedEntity, "reference"); - entity.addReference(reference); - - // then - assertEquals(1, entity.getReferences().size()); - Reference reference1 = entity.getReferences().iterator().next(); - assertEquals(reference, reference1); - assertEquals(entity, reference1.getParent()); - assertEquals(referencedEntity, reference1.getType()); - assertEquals("reference", reference1.getName()); - assertFalse(reference1.equals(new Object())); - } - - @Test - public void cannotCreateEntityWithNullName() { - Assertions.assertThrows(IllegalArgumentException.class, () -> { - new Entity("Type", null); - }); - } - - @Test - public void cannotCreateEntityWithEmptyName() { - Assertions.assertThrows(IllegalArgumentException.class, () -> { - new Entity("Type", ""); - }); - } - - @Test - public void cannotCreateEntityWithNullType() { - Assertions.assertThrows(IllegalArgumentException.class, () -> { - new Entity(null, "Name"); - }); - } - - @Test - public void cannotCreateEntityWithEmptyType() { - Assertions.assertThrows(IllegalArgumentException.class, () -> { - new Entity("", "Name"); - }); - } - - @Test - public void entitiesWithSameTypeAndNameAreEqual() { - // given - Entity entity1 = new Entity("Type", "Name"); - Entity entity2 = new Entity("Type", "Name"); - - // when - boolean equals = entity1.equals(entity1); - - // then - assertTrue(equals); - } - - @Test - public void otherObjectsAreNotEqual() { - // given - Entity entity = new Entity("Type", "Name"); - - // when - boolean equals = entity.equals(new Object()); - - // then - assertFalse(equals); - } - - @Test - public void canAddDiscoveryComment() { - // given - Entity entity = new Entity("test.Type", "Type"); - String discoveryComment = "This entity has been derived from the class test.Type."; - - // when - entity.setDiscoveryComment(discoveryComment); - - // then - assertEquals(discoveryComment, entity.getDiscoveryComment()); - } - -} diff --git a/src/test/java/org/contextmapper/discovery/model/MethodTest.java b/src/test/java/org/contextmapper/discovery/model/MethodTest.java new file mode 100644 index 0000000..d3d70cf --- /dev/null +++ b/src/test/java/org/contextmapper/discovery/model/MethodTest.java @@ -0,0 +1,62 @@ +/* + * Copyright 2019 The Context Mapper Project Team + * + * 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 org.contextmapper.discovery.model; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class MethodTest { + + @Test + public void canCreateMethod() { + // when + Method method = new Method("testMethod"); + + // then + assertEquals("testMethod", method.getName()); + } + + @Test + public void canSetReturnType() { + // given + Method method = new Method("testMethod"); + + // then + DomainObject returnType = new DomainObject(DomainObjectType.VALUE_OBJECT, "ReturnType"); + method.setReturnType(returnType); + + // then + assertEquals("ReturnType", method.getReturnType().getName()); + } + + @Test + public void canAddParameter() { + // given + Method method = new Method("TestMethod"); + + // when + DomainObject parameterType = new DomainObject(DomainObjectType.VALUE_OBJECT, "ParameterType"); + Parameter parameter = new Parameter("testParam", parameterType); + method.addParameter(parameter); + + // then + Parameter resultParam = method.getParameters().iterator().next(); + assertEquals("testParam", resultParam.getName()); + assertEquals("ParameterType", resultParam.getType().getName()); + } + +} diff --git a/src/test/java/org/contextmapper/discovery/model/ParameterTest.java b/src/test/java/org/contextmapper/discovery/model/ParameterTest.java new file mode 100644 index 0000000..3176acb --- /dev/null +++ b/src/test/java/org/contextmapper/discovery/model/ParameterTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2019 The Context Mapper Project Team + * + * 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 org.contextmapper.discovery.model; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ParameterTest { + + @Test + public void canCreateParameter() { + // given + DomainObject type = new DomainObject(DomainObjectType.VALUE_OBJECT, "TestType"); + + // when + Parameter parameter = new Parameter("testParameter", type); + + // then + assertEquals("testParameter", parameter.getName()); + assertEquals("TestType", parameter.getType().getName()); + } + +} diff --git a/src/test/java/org/contextmapper/discovery/strategies/boundedcontexts/SpringBootBoundedContextDiscoveryStrategyTest.java b/src/test/java/org/contextmapper/discovery/strategies/boundedcontexts/SpringBootBoundedContextDiscoveryStrategyTest.java index 1713706..8b84679 100644 --- a/src/test/java/org/contextmapper/discovery/strategies/boundedcontexts/SpringBootBoundedContextDiscoveryStrategyTest.java +++ b/src/test/java/org/contextmapper/discovery/strategies/boundedcontexts/SpringBootBoundedContextDiscoveryStrategyTest.java @@ -64,7 +64,28 @@ public void canDiscoverResourcesAsAggregates() { } @Test - public void canDiscoverEntitiesFromResourceMethods() { + public void canCreateAggregateRootEntity() { + // given + ContextMapDiscoverer discoverer = new ContextMapDiscoverer() + .usingBoundedContextDiscoveryStrategies( + new SpringBootBoundedContextDiscoveryStrategy("test.application.spring.boot") + ); + + // when + Set boundedContexts = discoverer.discoverContextMap().getBoundedContexts(); + + // then + assertEquals(1, boundedContexts.size()); + BoundedContext bc = boundedContexts.iterator().next(); + assertEquals(1, bc.getAggregates().size()); + Aggregate customers = bc.getAggregates().stream().filter(a -> a.getName().equals("customers")).findFirst().get(); + Set domainObjects = customers.getDomainObjects().stream().filter(o -> o.getType().equals(DomainObjectType.ENTITY)).collect(Collectors.toSet()); + assertEquals(1, domainObjects.size()); + assertEquals("customers_RootEntity", domainObjects.iterator().next().getName()); + } + + @Test + public void canDiscoverDomainObjectsFromResourceMethods() { // given ContextMapDiscoverer discoverer = new ContextMapDiscoverer() .usingBoundedContextDiscoveryStrategies( @@ -80,14 +101,52 @@ public void canDiscoverEntitiesFromResourceMethods() { assertEquals(1, bc.getAggregates().size()); Aggregate aggregate = bc.getAggregates().iterator().next(); assertEquals("customers", aggregate.getName()); - assertEquals(3, aggregate.getEntities().size()); - assertTrue(aggregate.getEntities().contains(new Entity("test.application.spring.boot.model.Address", "Address"))); - assertTrue(aggregate.getEntities().contains(new Entity("test.application.spring.boot.model.CustomerId", "CustomerId"))); - assertTrue(aggregate.getEntities().contains(new Entity("test.application.spring.boot.model.Customer", "Customer"))); + Set resourceDomainObject = aggregate.getDomainObjects().stream().filter(o -> o.getType().equals(DomainObjectType.VALUE_OBJECT)).collect(Collectors.toSet()); + assertEquals(3, resourceDomainObject.size()); + assertTrue(resourceDomainObject.contains(new DomainObject(DomainObjectType.VALUE_OBJECT, "Address", "test.application.spring.boot.model.Address"))); + assertTrue(resourceDomainObject.contains(new DomainObject(DomainObjectType.VALUE_OBJECT, "CustomerId", "test.application.spring.boot.model.CustomerId"))); + assertTrue(resourceDomainObject.contains(new DomainObject(DomainObjectType.VALUE_OBJECT, "Customer", "test.application.spring.boot.model.Customer"))); + } + + @Test + public void canCreateAggregateRootMethodsFromResourceMethods() { + // given + ContextMapDiscoverer discoverer = new ContextMapDiscoverer() + .usingBoundedContextDiscoveryStrategies( + new SpringBootBoundedContextDiscoveryStrategy("test.application.spring.boot") + ); + + // when + Set boundedContexts = discoverer.discoverContextMap().getBoundedContexts(); + + // then + BoundedContext bc = boundedContexts.iterator().next(); + Aggregate aggregate = bc.getAggregates().iterator().next(); + DomainObject aggregateRoot = aggregate.getDomainObjects().stream().filter(o -> o.getType().equals(DomainObjectType.ENTITY)).findFirst().get(); + assertEquals(3, aggregateRoot.getMethods().size()); + Method changeAddress = aggregateRoot.getMethods().stream().filter(m -> m.getName().equals("changeAddress")).findFirst().get(); + Method getCustomer = aggregateRoot.getMethods().stream().filter(m -> m.getName().equals("getCustomer")).findFirst().get(); + Method getCustomers = aggregateRoot.getMethods().stream().filter(m -> m.getName().equals("getCustomers")).findFirst().get(); + assertNotNull(changeAddress); + assertNotNull(getCustomer); + assertNotNull(getCustomers); + assertEquals("Address", changeAddress.getReturnType().getName()); + assertEquals("Customer", getCustomer.getReturnType().getName()); + assertEquals("Customer", getCustomers.getReturnType().getName()); + assertEquals("List", getCustomers.getReturnCollectionType()); + Set changeAddressParameterTypes = changeAddress.getParameters().stream().map(p -> p.getType().getName()).collect(Collectors.toSet()); + Set getCustomerParameterTypes = getCustomer.getParameters().stream().map(p -> p.getType().getName()).collect(Collectors.toSet()); + Set getCustomersParameterTypes = getCustomer.getParameters().stream().map(p -> p.getType().getName()).collect(Collectors.toSet()); + Set getCustomersParameterCollectionTypes = getCustomers.getParameters().stream().map(p -> p.getCollectionType()).collect(Collectors.toSet()); + assertTrue(changeAddressParameterTypes.contains("CustomerId")); + assertTrue(changeAddressParameterTypes.contains("Address")); + assertTrue(getCustomerParameterTypes.contains("CustomerId")); + assertTrue(getCustomersParameterTypes.contains("CustomerId")); + assertTrue(getCustomersParameterCollectionTypes.contains("List")); } @Test - public void canDiscoverEntityAttributes() { + public void canDiscoverDomainObjectAttributes() { // given ContextMapDiscoverer discoverer = new ContextMapDiscoverer() .usingBoundedContextDiscoveryStrategies( @@ -100,21 +159,21 @@ public void canDiscoverEntityAttributes() { // then BoundedContext bc = boundedContexts.iterator().next(); Aggregate aggregate = bc.getAggregates().iterator().next(); - Entity addressEntity = aggregate.getEntities().stream().filter(e -> e.getName().equals("Address")).findFirst().get(); - assertNotNull(addressEntity); - assertEquals(4, addressEntity.getAttributes().size()); - Attribute streetAttribute = addressEntity.getAttributes().stream().filter(a -> a.getName().equals("street")).findFirst().get(); + DomainObject addressDomainObject = aggregate.getDomainObjects().stream().filter(e -> e.getName().equals("Address")).findFirst().get(); + assertNotNull(addressDomainObject); + assertEquals(4, addressDomainObject.getAttributes().size()); + Attribute streetAttribute = addressDomainObject.getAttributes().stream().filter(a -> a.getName().equals("street")).findFirst().get(); assertNotNull(streetAttribute); assertEquals("street", streetAttribute.getName()); assertEquals("String", streetAttribute.getType()); - Attribute plzAttribute = addressEntity.getAttributes().stream().filter(a -> a.getName().equals("plz")).findFirst().get(); + Attribute plzAttribute = addressDomainObject.getAttributes().stream().filter(a -> a.getName().equals("plz")).findFirst().get(); assertNotNull(plzAttribute); assertEquals("plz", plzAttribute.getName()); assertEquals("int", plzAttribute.getType()); } @Test - public void canDiscoverEntityReferences() { + public void canDiscoverDomainObjectReferences() { // given ContextMapDiscoverer discoverer = new ContextMapDiscoverer() .usingBoundedContextDiscoveryStrategies( @@ -127,13 +186,13 @@ public void canDiscoverEntityReferences() { // then BoundedContext bc = boundedContexts.iterator().next(); Aggregate aggregate = bc.getAggregates().iterator().next(); - Entity customerEntity = aggregate.getEntities().stream().filter(e -> e.getName().equals("Customer")).findFirst().get(); - Entity customerIdEntity = aggregate.getEntities().stream().filter(e -> e.getName().equals("CustomerId")).findFirst().get(); - assertNotNull(customerEntity); - assertNotNull(customerIdEntity); - Set singleReferences = customerEntity.getReferences().stream().filter(r -> r.getCollectionType() == null || "".equals(r.getCollectionType())).collect(Collectors.toSet()); + DomainObject customerDomainObject = aggregate.getDomainObjects().stream().filter(e -> e.getName().equals("Customer")).findFirst().get(); + DomainObject customerIdDomainObject = aggregate.getDomainObjects().stream().filter(e -> e.getName().equals("CustomerId")).findFirst().get(); + assertNotNull(customerDomainObject); + assertNotNull(customerIdDomainObject); + Set singleReferences = customerDomainObject.getReferences().stream().filter(r -> r.getCollectionType() == null || "".equals(r.getCollectionType())).collect(Collectors.toSet()); assertEquals(1, singleReferences.size()); - assertEquals(customerIdEntity, singleReferences.iterator().next().getType()); + assertEquals(customerIdDomainObject, singleReferences.iterator().next().getType()); } @Test @@ -150,10 +209,10 @@ public void canHandleCollectionTypes() { // then BoundedContext bc = boundedContexts.iterator().next(); Aggregate aggregate = bc.getAggregates().iterator().next(); - Entity customerEntity = aggregate.getEntities().stream().filter(e -> e.getName().equals("Customer")).findFirst().get(); - assertNotNull(customerEntity); - assertEquals(4, customerEntity.getReferences().size()); - Set collectionReferences = customerEntity.getReferences().stream().filter(r -> r.getCollectionType() != null).collect(Collectors.toSet()); + DomainObject customerDomainObject = aggregate.getDomainObjects().stream().filter(e -> e.getName().equals("Customer")).findFirst().get(); + assertNotNull(customerDomainObject); + assertEquals(4, customerDomainObject.getReferences().size()); + Set collectionReferences = customerDomainObject.getReferences().stream().filter(r -> r.getCollectionType() != null).collect(Collectors.toSet()); assertEquals("Address", collectionReferences.stream().filter(r -> r.getCollectionType().equals("List")).findFirst().get().getType().getName()); assertEquals("Address", collectionReferences.stream().filter(r -> r.getCollectionType().equals("Set")).findFirst().get().getType().getName()); assertEquals("Address", collectionReferences.stream().filter(r -> r.getCollectionType().equals("Collection")).findFirst().get().getType().getName()); @@ -173,9 +232,9 @@ public void canHandleArrays() { // then BoundedContext bc = boundedContexts.iterator().next(); Aggregate aggregate = bc.getAggregates().iterator().next(); - Entity addressEntity = aggregate.getEntities().stream().filter(e -> e.getName().equals("Address")).findFirst().get(); - assertNotNull(addressEntity); - Attribute arrayAttribute = addressEntity.getAttributes().stream().filter(a -> a.getName().equals("arrayTest")).findFirst().get(); + DomainObject addressDomainObject = aggregate.getDomainObjects().stream().filter(e -> e.getName().equals("Address")).findFirst().get(); + assertNotNull(addressDomainObject); + Attribute arrayAttribute = addressDomainObject.getAttributes().stream().filter(a -> a.getName().equals("arrayTest")).findFirst().get(); assertNotNull(arrayAttribute); assertEquals("String", arrayAttribute.getType()); assertEquals("List", arrayAttribute.getCollectionType()); @@ -202,11 +261,11 @@ public void canHandleMultipleAggregatesWithSameName() { } @Test - public void canHandleMultipleEntitiesWithSameName() { + public void canHandleMultipleDomainObjectsWithSameName() { // given ContextMapDiscoverer discoverer = new ContextMapDiscoverer() .usingBoundedContextDiscoveryStrategies( - new SpringBootBoundedContextDiscoveryStrategy("test.duplicate.entity.name") + new SpringBootBoundedContextDiscoveryStrategy("test.duplicate.domainobject.name") ); // when @@ -217,10 +276,10 @@ public void canHandleMultipleEntitiesWithSameName() { BoundedContext bc = boundedContexts.iterator().next(); assertEquals(1, bc.getAggregates().size()); Aggregate aggregate = bc.getAggregates().iterator().next(); - Set entityNames = aggregate.getEntities().stream().map(e -> e.getName()).collect(Collectors.toSet()); - assertTrue(entityNames.contains("CustomerId")); - assertTrue(entityNames.contains("customers_CustomerId")); - assertTrue(entityNames.contains("customers_CustomerId_1")); + Set domainObjectNames = aggregate.getDomainObjects().stream().map(e -> e.getName()).collect(Collectors.toSet()); + assertTrue(domainObjectNames.contains("CustomerId")); + assertTrue(domainObjectNames.contains("customers_CustomerId")); + assertTrue(domainObjectNames.contains("customers_CustomerId_1")); } @Test @@ -237,11 +296,11 @@ public void canCreateDiscoveryCommentOnAggregate() { // then BoundedContext bc = boundedContexts.iterator().next(); Aggregate aggregate = bc.getAggregates().iterator().next(); - assertEquals("This Aggregate has been created on the basis of the Spring REST controller test.application.spring.boot.interfaces.CustomerInformationHolder.", aggregate.getDiscoveryComment()); + assertEquals("This Aggregate has been created on the basis of the RESTful HTTP controller test.application.spring.boot.interfaces.CustomerInformationHolder.", aggregate.getDiscoveryComment()); } @Test - public void canCreateDiscoveryCommentOnEntity() { + public void canCreateDiscoveryCommentOnDomainObject() { // given ContextMapDiscoverer discoverer = new ContextMapDiscoverer() .usingBoundedContextDiscoveryStrategies( @@ -254,7 +313,7 @@ public void canCreateDiscoveryCommentOnEntity() { // then BoundedContext bc = boundedContexts.iterator().next(); Aggregate aggregate = bc.getAggregates().iterator().next(); - Entity entity = aggregate.getEntities().stream().filter(e -> e.getName().equals("Address")).findAny().get(); - assertEquals("This entity has been derived from the class test.application.spring.boot.model.Address.", entity.getDiscoveryComment()); + DomainObject domainObject = aggregate.getDomainObjects().stream().filter(e -> e.getName().equals("Address")).findAny().get(); + assertEquals("This value object has been derived from the class test.application.spring.boot.model.Address.", domainObject.getDiscoveryComment()); } } diff --git a/src/test/java/test/application/spring/boot/interfaces/CustomerInformationHolder.java b/src/test/java/test/application/spring/boot/interfaces/CustomerInformationHolder.java index 5b55d31..c2f05a6 100644 --- a/src/test/java/test/application/spring/boot/interfaces/CustomerInformationHolder.java +++ b/src/test/java/test/application/spring/boot/interfaces/CustomerInformationHolder.java @@ -21,6 +21,8 @@ import test.application.spring.boot.model.Customer; import test.application.spring.boot.model.CustomerId; +import java.util.List; + @RestController @RequestMapping({"/customers"}) public class CustomerInformationHolder { @@ -37,4 +39,10 @@ public ResponseEntity getCustomer(@PathVariable CustomerId customerId) return null; } + @GetMapping({"/{customerIds}/"}) + public ResponseEntity> getCustomers(@PathVariable List customerIds) { + // method will never be called; this is just for our reflection (scanning) tests; + return null; + } + } diff --git a/src/test/java/test/duplicate/entity/name/TestSpringBootApplication.java b/src/test/java/test/duplicate/domainobject/name/TestSpringBootApplication.java similarity index 94% rename from src/test/java/test/duplicate/entity/name/TestSpringBootApplication.java rename to src/test/java/test/duplicate/domainobject/name/TestSpringBootApplication.java index f595eca..794676f 100644 --- a/src/test/java/test/duplicate/entity/name/TestSpringBootApplication.java +++ b/src/test/java/test/duplicate/domainobject/name/TestSpringBootApplication.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package test.duplicate.entity.name; +package test.duplicate.domainobject.name; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/src/test/java/test/duplicate/entity/name/interfaces/CustomerInformationHolder.java b/src/test/java/test/duplicate/domainobject/name/interfaces/CustomerInformationHolder.java similarity index 79% rename from src/test/java/test/duplicate/entity/name/interfaces/CustomerInformationHolder.java rename to src/test/java/test/duplicate/domainobject/name/interfaces/CustomerInformationHolder.java index a11b5e2..0ba2798 100644 --- a/src/test/java/test/duplicate/entity/name/interfaces/CustomerInformationHolder.java +++ b/src/test/java/test/duplicate/domainobject/name/interfaces/CustomerInformationHolder.java @@ -13,32 +13,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package test.duplicate.entity.name.interfaces; +package test.duplicate.domainobject.name.interfaces; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import test.duplicate.entity.name.model.Address; -import test.duplicate.entity.name.model.Customer; -import test.duplicate.entity.name.model.CustomerId; +import test.duplicate.domainobject.name.model.Address; +import test.duplicate.domainobject.name.model.Customer; +import test.duplicate.domainobject.name.model.duplicate1.CustomerId; @RestController @RequestMapping({"customers", "second-ignored-mapping"}) public class CustomerInformationHolder { @PutMapping({"/{customerId}/address"}) - public ResponseEntity
changeAddress(@PathVariable test.duplicate.entity.name.model.duplicate1.CustomerId customerId, @RequestBody Address requestDto) { + public ResponseEntity
changeAddress(@PathVariable CustomerId customerId, @RequestBody Address requestDto) { // method will never be called; this is just for our reflection (scanning) tests; return null; } @PutMapping({"/{customerId}/address"}) - public ResponseEntity
changeAddress(@PathVariable CustomerId customerId, @RequestBody Address requestDto) { + public ResponseEntity
changeAddress(@PathVariable test.duplicate.domainobject.name.model.CustomerId customerId, @RequestBody Address requestDto) { // method will never be called; this is just for our reflection (scanning) tests; return null; } @GetMapping({"/{customerId}/"}) - public ResponseEntity getCustomer(@PathVariable test.duplicate.entity.name.model.duplicate2.CustomerId customerId) { + public ResponseEntity getCustomer(@PathVariable test.duplicate.domainobject.name.model.duplicate2.CustomerId customerId) { // method will never be called; this is just for our reflection (scanning) tests; return null; } diff --git a/src/test/java/test/duplicate/entity/name/model/Address.java b/src/test/java/test/duplicate/domainobject/name/model/Address.java similarity index 95% rename from src/test/java/test/duplicate/entity/name/model/Address.java rename to src/test/java/test/duplicate/domainobject/name/model/Address.java index 5a4e47a..becca62 100644 --- a/src/test/java/test/duplicate/entity/name/model/Address.java +++ b/src/test/java/test/duplicate/domainobject/name/model/Address.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package test.duplicate.entity.name.model; +package test.duplicate.domainobject.name.model; public class Address { diff --git a/src/test/java/test/duplicate/entity/name/model/Customer.java b/src/test/java/test/duplicate/domainobject/name/model/Customer.java similarity index 96% rename from src/test/java/test/duplicate/entity/name/model/Customer.java rename to src/test/java/test/duplicate/domainobject/name/model/Customer.java index 9018ae6..c72c17e 100644 --- a/src/test/java/test/duplicate/entity/name/model/Customer.java +++ b/src/test/java/test/duplicate/domainobject/name/model/Customer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package test.duplicate.entity.name.model; +package test.duplicate.domainobject.name.model; import java.util.Collection; import java.util.List; diff --git a/src/test/java/test/duplicate/entity/name/model/CustomerId.java b/src/test/java/test/duplicate/domainobject/name/model/CustomerId.java similarity index 93% rename from src/test/java/test/duplicate/entity/name/model/CustomerId.java rename to src/test/java/test/duplicate/domainobject/name/model/CustomerId.java index 4725619..9ac6db3 100644 --- a/src/test/java/test/duplicate/entity/name/model/CustomerId.java +++ b/src/test/java/test/duplicate/domainobject/name/model/CustomerId.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package test.duplicate.entity.name.model; +package test.duplicate.domainobject.name.model; public class CustomerId { diff --git a/src/test/java/test/duplicate/entity/name/model/duplicate2/CustomerId.java b/src/test/java/test/duplicate/domainobject/name/model/duplicate1/CustomerId.java similarity index 91% rename from src/test/java/test/duplicate/entity/name/model/duplicate2/CustomerId.java rename to src/test/java/test/duplicate/domainobject/name/model/duplicate1/CustomerId.java index e77e207..f122e9c 100644 --- a/src/test/java/test/duplicate/entity/name/model/duplicate2/CustomerId.java +++ b/src/test/java/test/duplicate/domainobject/name/model/duplicate1/CustomerId.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package test.duplicate.entity.name.model.duplicate2; +package test.duplicate.domainobject.name.model.duplicate1; public class CustomerId { diff --git a/src/test/java/test/duplicate/entity/name/model/duplicate1/CustomerId.java b/src/test/java/test/duplicate/domainobject/name/model/duplicate2/CustomerId.java similarity index 91% rename from src/test/java/test/duplicate/entity/name/model/duplicate1/CustomerId.java rename to src/test/java/test/duplicate/domainobject/name/model/duplicate2/CustomerId.java index b805616..01b2402 100644 --- a/src/test/java/test/duplicate/entity/name/model/duplicate1/CustomerId.java +++ b/src/test/java/test/duplicate/domainobject/name/model/duplicate2/CustomerId.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package test.duplicate.entity.name.model.duplicate1; +package test.duplicate.domainobject.name.model.duplicate2; public class CustomerId {