├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ └── build.yml ├── .gitignore ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── Dockerfile ├── LICENSE ├── README.md ├── bin └── enable-jigsaw.sh ├── docker-compose.yml ├── leasingninja-riskmanagement ├── pom.xml └── src │ ├── main │ ├── java │ │ ├── io │ │ │ └── leasingninja │ │ │ │ └── riskmanagement │ │ │ │ ├── application │ │ │ │ ├── CheckCreditRating.java │ │ │ │ ├── InboxApplicationService.java │ │ │ │ ├── InboxApplicationServiceImpl.java │ │ │ │ ├── ListContracts.java │ │ │ │ ├── ReadContract.java │ │ │ │ └── VoteContract.java │ │ │ │ ├── config │ │ │ │ ├── RiskmanagementConfig.java │ │ │ │ └── package-info.java │ │ │ │ ├── domain │ │ │ │ ├── Contract.java │ │ │ │ ├── ContractNumber.java │ │ │ │ ├── ContractRepository.java │ │ │ │ ├── CreditRating.java │ │ │ │ ├── SignDate.java │ │ │ │ └── VoteResult.java │ │ │ │ ├── infrastructure │ │ │ │ ├── ContractRepositoryMemImpl.java │ │ │ │ └── package-info.java │ │ │ │ └── userinterface │ │ │ │ ├── ContractModel.java │ │ │ │ ├── ContractModelMapper.java │ │ │ │ └── RiskManagementController.java │ │ └── module-info.java │ └── resources │ │ └── templates │ │ ├── contract.html │ │ ├── contracts.html │ │ ├── rating.html │ │ └── vote.html │ └── test │ └── java │ └── io │ └── leasingninja │ └── riskmanagement │ ├── application │ ├── CheckCreditRatingTest.java │ ├── InboxApplicationServiceTest.java │ ├── ListContractsTest.java │ ├── ReadContractTest.java │ └── VoteContractTest.java │ ├── domain │ ├── ContractTest.java │ └── CreditRatingTest.java │ └── userinterface │ └── ContractModelMapperTest.java ├── leasingninja-sales ├── pom.xml └── src │ ├── main │ ├── java │ │ ├── io │ │ │ └── leasingninja │ │ │ │ └── sales │ │ │ │ ├── application │ │ │ │ ├── FilloutContract.java │ │ │ │ ├── SignContract.java │ │ │ │ ├── UnterschreibeVertragApplicationService_MitEvent.java │ │ │ │ └── ViewContract.java │ │ │ │ ├── config │ │ │ │ ├── SalesConfig.java │ │ │ │ └── SalesConfiguration.java │ │ │ │ ├── domain │ │ │ │ ├── Amount.java │ │ │ │ ├── Car.java │ │ │ │ ├── Contract.java │ │ │ │ ├── ContractFactory.java │ │ │ │ ├── ContractNumber.java │ │ │ │ ├── ContractSigned.java │ │ │ │ ├── Contracts.java │ │ │ │ ├── Currency.java │ │ │ │ ├── Customer.java │ │ │ │ ├── FinancialCalculator.java │ │ │ │ ├── Interest.java │ │ │ │ ├── LeaseTerm.java │ │ │ │ └── SignDate.java │ │ │ │ ├── infrastructure │ │ │ │ ├── ContractDatabaseEntity.java │ │ │ │ ├── ContractDatabaseEntityRepository.java │ │ │ │ ├── ContractsJpaImpl.java │ │ │ │ ├── ContractsJsonImpl.java │ │ │ │ └── ContractsMemImpl.java │ │ │ │ └── ui │ │ │ │ ├── ContractModel.java │ │ │ │ ├── ContractModelMapper.java │ │ │ │ ├── MapStructContractModelMapper.java │ │ │ │ └── SalesController.java │ │ └── module-info.java │ └── resources │ │ └── templates │ │ └── contractView.html │ └── test │ └── java │ └── io │ └── leasingninja │ └── sales │ ├── application │ ├── FilloutContractTest.java │ ├── SignContractTest.java │ └── ViewContractTest.java │ ├── domain │ ├── AmountTest.java │ ├── ContractFactoryTest.java │ ├── ContractTest.java │ ├── CurrencyTest.java │ ├── CustomerTest.java │ ├── FinancialCalculatorTest.java │ ├── InterestTest.java │ ├── LeaseTermTest.java │ └── SignDateTest.java │ └── infrastructure │ └── ContractsMemImplTest.java ├── leasingninja-webapp ├── pom.xml └── src │ └── main │ ├── java │ ├── io │ │ └── leasingninja │ │ │ └── webapp │ │ │ └── LeasingNinja.java │ └── module-info.java │ └── resources │ ├── application.properties │ ├── banner.txt │ └── static │ └── index.html ├── mvnw ├── mvnw.cmd └── pom.xml /.dockerignore: -------------------------------------------------------------------------------- 1 | # Maven 2 | target/ -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.{bat,cmd}] 13 | end_of_line = crlf 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # These are explicitly windows files and should use crlf 5 | *.bat text eol=crlf 6 | *.cmd text eol=crlf 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for more information: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | # https://containers.dev/guide/dependabot 6 | 7 | version: 2 8 | updates: 9 | - package-ecosystem: "maven" 10 | directory: "/" 11 | schedule: 12 | interval: "weekly" 13 | 14 | - package-ecosystem: "devcontainers" 15 | directory: "/" 16 | schedule: 17 | interval: weekly 18 | 19 | - package-ecosystem: "github-actions" 20 | # Workflow files stored in the default location of `.github/workflows`. (You don't need to specify `/.github/workflows` for `directory`. You can use `directory: "/"`.) 21 | directory: "/" 22 | schedule: 23 | interval: "weekly" 24 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: LeasingNinja Java CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-24.04 8 | 9 | steps: 10 | - name: Checkout source code 11 | uses: actions/checkout@v4 12 | - name: Set up JDK 13 | uses: actions/setup-java@v4 14 | with: 15 | distribution: 'temurin' 16 | java-version: '24' 17 | cache: maven 18 | - name: Build with Maven without tests 19 | run: 20 | ./mvnw --batch-mode --update-snapshots package -Dmaven.test.skip=true 21 | - name: Build with Maven without Jigsaw 22 | run: | 23 | bin/enable-jigsaw.sh disable 24 | ./mvnw --batch-mode --update-snapshots package 25 | bin/enable-jigsaw.sh enable 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Maven 2 | target/ -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leasingninja/leasingninja-java-boundedcontexts-domainmodel/cf1cd599a7486d7b288bb55ff6cc2069881d72cc/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar 19 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM eclipse-temurin:23 3 | 4 | WORKDIR /app 5 | 6 | COPY .mvn/ .mvn 7 | COPY mvnw pom.xml ./ 8 | 9 | COPY leasingninja-sales ./leasingninja-sales 10 | COPY leasingninja-riskmanagement ./leasingninja-riskmanagement 11 | COPY leasingninja-webapp ./leasingninja-webapp 12 | 13 | RUN ./mvnw install -Dmaven.test.skip=true 14 | 15 | CMD ["./mvnw", "-pl", \ 16 | "leasingninja-webapp", \ 17 | "spring-boot:run", \ 18 | "-Dspring-boot.run.jvmArguments=-enableassertions", \ 19 | "-Dspring-boot.run.arguments=--logging.level.io.leasingninja=TRACE"] 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2023 J. Henning Schwentner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # leasingninja-java-boundedcontexts-domainmodel 2 | 3 | The LeasingNinja in Java with DDD style bounded contexts and domain model. UI in Spring WebMVC. 4 | 5 | If you haven't visited leasingninja.io yet, should do so now, as its the introduction to the further text. 6 | 7 | In Java we implement entities and value objects as classes. 8 | We start with the central entity _Contract_. 9 | 10 | ## Build Instructions 11 | 12 | For the time being we’re wrestling with the module system, so we have to play some tricks. It is either: 13 | 14 | * build with Maven without tests: 15 | 16 | ```fish 17 | ./mvnw package -Dmaven.test.skip=true 18 | ``` 19 | 20 | * build with Maven without Jigsaw 21 | 22 | ```fish 23 | bin/enable-jigsaw.sh disable && ./mvnw test && bin/enable-jigsaw.sh enable 24 | ``` 25 | 26 | ## Run with Docker 27 | 28 | Start the web app: 29 | 30 | ```fish 31 | docker-compose up 32 | ``` 33 | 34 | Open in your browser. 35 | 36 | ## Run without Docker 37 | 38 | Install LeasingNinja to your Maven repository: 39 | 40 | ```fish 41 | bin/enable-jigsaw.sh disable && ./mvnw install && bin/enable-jigsaw.sh enable 42 | ``` 43 | 44 | Start the web app: 45 | 46 | ```fish 47 | ./mvnw -pl leasingninja-webapp spring-boot:run -Dspring-boot.run.jvmArguments=-enableassertions 48 | ``` 49 | 50 | You might want to see more logging: 51 | 52 | ```fish 53 | ./mvnw -pl leasingninja-webapp spring-boot:run -Dspring-boot.run.jvmArguments=-enableassertions -Dspring-boot.run.arguments=--logging.level.io.leasingninja=TRACE 54 | ``` 55 | 56 | Open in your browser. 57 | -------------------------------------------------------------------------------- /bin/enable-jigsaw.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Renames module-info.java files to disable/enable the Java Module System. 3 | # Usage example: enable-jigsaw.sh disable 4 | 5 | if [[ $# != 1 ]]; then 6 | echo "Please specify exactly one parameter." 7 | exit 1 8 | fi 9 | 10 | enable="$1" 11 | 12 | case "${enable}" in 13 | enable) 14 | mv leasingninja-sales/src/main/java/module-info.java.hidden leasingninja-sales/src/main/java/module-info.java 15 | mv leasingninja-riskmanagement/src/main/java/module-info.java.hidden leasingninja-riskmanagement/src/main/java/module-info.java 16 | mv leasingninja-webapp/src/main/java/module-info.java.hidden leasingninja-webapp/src/main/java/module-info.java ;; 17 | disable) 18 | mv leasingninja-sales/src/main/java/module-info.java leasingninja-sales/src/main/java/module-info.java.hidden 19 | mv leasingninja-riskmanagement/src/main/java/module-info.java leasingninja-riskmanagement/src/main/java/module-info.java.hidden 20 | mv leasingninja-webapp/src/main/java/module-info.java leasingninja-webapp/src/main/java/module-info.java.hidden ;; 21 | *) 22 | exit -1 ;; 23 | esac 24 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.9" 2 | 3 | services: 4 | leasingninja: 5 | build: . 6 | image: leasingninja/leasingninja-java-boundedcontexts-domainmodel:latest 7 | # command: 8 | environment: 9 | - SERVER_PORT=8080 10 | volumes: 11 | - ./:/app 12 | ports: 13 | - 8080:8080 -------------------------------------------------------------------------------- /leasingninja-riskmanagement/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | 7 | io.leasingninja 8 | leasingninja 9 | 0.0.1-SNAPSHOT 10 | 11 | 12 | leasingninja-riskmanagement 13 | LeasingNinja risk management 14 | Risk management bounded context of LeasingNinja 15 | 16 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/io/leasingninja/riskmanagement/application/CheckCreditRating.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.application; 2 | 3 | import static java.util.Objects.*; 4 | 5 | import org.jmolecules.architecture.layered.ApplicationLayer; 6 | import org.jmolecules.ddd.annotation.Service; 7 | 8 | import org.springframework.stereotype.Component; 9 | 10 | import io.leasingninja.riskmanagement.domain.ContractNumber; 11 | import io.leasingninja.riskmanagement.domain.ContractRepository; 12 | import io.leasingninja.riskmanagement.domain.CreditRating; 13 | 14 | @Component 15 | @ApplicationLayer 16 | @Service 17 | public class CheckCreditRating { 18 | 19 | private final ContractRepository contracts; 20 | 21 | public CheckCreditRating(ContractRepository contracts) { 22 | requireNonNull(contracts); 23 | 24 | this.contracts = contracts; 25 | } 26 | 27 | public void checkCreditRating(ContractNumber contractnumber, CreditRating rating) { 28 | requireNonNull(contractnumber); 29 | requireNonNull(rating); 30 | 31 | var contract = this.contracts.findById(contractnumber); 32 | 33 | contract.checkCreditRating(rating); 34 | 35 | this.contracts.save(contract); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/io/leasingninja/riskmanagement/application/InboxApplicationService.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.application; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | @Component 6 | public interface InboxApplicationService { 7 | 8 | void confirmSignedContract(String lvnr, int year, int month, int dayOfMonth); 9 | 10 | } -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/io/leasingninja/riskmanagement/application/InboxApplicationServiceImpl.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.application; 2 | 3 | import io.leasingninja.riskmanagement.domain.Contract; 4 | import io.leasingninja.riskmanagement.domain.ContractNumber; 5 | import io.leasingninja.riskmanagement.domain.ContractRepository; 6 | import io.leasingninja.riskmanagement.domain.SignDate; 7 | import org.springframework.stereotype.Component; 8 | 9 | import static java.time.temporal.ChronoField.*; 10 | 11 | @Component 12 | class InboxApplicationServiceImpl implements InboxApplicationService { 13 | 14 | private ContractRepository contracts; 15 | 16 | public InboxApplicationServiceImpl(ContractRepository contracts) { 17 | this.contracts = contracts; 18 | } 19 | 20 | @Override 21 | public void confirmSignedContract(String lvnr, int year, int month, int dayOfMonth) { 22 | YEAR.checkValidValue(year); 23 | MONTH_OF_YEAR.checkValidValue(month); 24 | DAY_OF_MONTH.checkValidValue(dayOfMonth); 25 | 26 | contracts.save(new Contract(ContractNumber.of(lvnr), SignDate.of(year, month, dayOfMonth))); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/io/leasingninja/riskmanagement/application/ListContracts.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.application; 2 | 3 | import java.util.Collection; 4 | 5 | import org.jmolecules.architecture.layered.ApplicationLayer; 6 | import org.jmolecules.ddd.annotation.Service; 7 | import org.springframework.stereotype.Component; 8 | 9 | import io.leasingninja.riskmanagement.domain.Contract; 10 | import io.leasingninja.riskmanagement.domain.ContractRepository; 11 | 12 | @Component 13 | @ApplicationLayer 14 | @Service 15 | public class ListContracts { 16 | 17 | private final ContractRepository contracts; 18 | 19 | public ListContracts(ContractRepository contracts) { 20 | this.contracts = contracts; 21 | } 22 | 23 | public Collection all() { 24 | return contracts.findAll(); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/io/leasingninja/riskmanagement/application/ReadContract.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.application; 2 | 3 | import static java.util.Objects.*; 4 | 5 | import org.jmolecules.architecture.layered.ApplicationLayer; 6 | import org.jmolecules.ddd.annotation.Service; 7 | 8 | import org.springframework.stereotype.Component; 9 | 10 | import io.leasingninja.riskmanagement.domain.Contract; 11 | import io.leasingninja.riskmanagement.domain.ContractNumber; 12 | import io.leasingninja.riskmanagement.domain.ContractRepository; 13 | 14 | @Component 15 | @ApplicationLayer 16 | @Service 17 | public class ReadContract { 18 | 19 | private final ContractRepository contracts; 20 | 21 | public ReadContract(ContractRepository contracts) { 22 | requireNonNull(contracts); 23 | 24 | this.contracts = contracts; 25 | } 26 | 27 | public Contract readContract(ContractNumber number) { 28 | requireNonNull(number); 29 | 30 | return this.contracts.findById(number); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/io/leasingninja/riskmanagement/application/VoteContract.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.application; 2 | 3 | import static java.util.Objects.*; 4 | 5 | import org.jmolecules.architecture.layered.ApplicationLayer; 6 | import org.jmolecules.ddd.annotation.Service; 7 | 8 | import org.springframework.stereotype.Component; 9 | 10 | import io.leasingninja.riskmanagement.domain.ContractNumber; 11 | import io.leasingninja.riskmanagement.domain.ContractRepository; 12 | import io.leasingninja.riskmanagement.domain.VoteResult; 13 | 14 | @Component 15 | @ApplicationLayer 16 | @Service 17 | public class VoteContract { 18 | 19 | private final ContractRepository contracts; 20 | 21 | public VoteContract(ContractRepository contracts) { 22 | requireNonNull(contracts); 23 | 24 | this.contracts = contracts; 25 | } 26 | 27 | public void vote(ContractNumber contractnumber, VoteResult result) { 28 | requireNonNull(contractnumber); 29 | requireNonNull(result); 30 | 31 | var contract = this.contracts.findById(contractnumber); 32 | 33 | contract.vote(result); 34 | 35 | this.contracts.save(contract); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/io/leasingninja/riskmanagement/config/RiskmanagementConfig.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | import io.leasingninja.riskmanagement.domain.ContractRepository; 7 | import io.leasingninja.riskmanagement.infrastructure.ContractRepositoryMemImpl; 8 | 9 | 10 | @Configuration 11 | public class RiskmanagementConfig { 12 | 13 | @Bean 14 | public ContractRepository riskmanagementContractRepository() { 15 | return new ContractRepositoryMemImpl(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/io/leasingninja/riskmanagement/config/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | /** 5 | * @author henning 6 | * 7 | */ 8 | package io.leasingninja.riskmanagement.config; -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/io/leasingninja/riskmanagement/domain/Contract.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.domain; 2 | 3 | import org.jmolecules.ddd.annotation.Entity; 4 | import org.jmolecules.ddd.annotation.Identity; 5 | 6 | @Entity 7 | public class Contract extends io.hschwentner.dddbits.basetype.Entity { 8 | 9 | @Identity 10 | private final ContractNumber number; 11 | 12 | private CreditRating creditRating; 13 | private VoteResult voteResult; 14 | 15 | public Contract(ContractNumber number, SignDate signDate) { // TODO: do we need the signDate? 16 | super(number); 17 | assert number != null; 18 | assert signDate != null; 19 | 20 | this.number = number; 21 | } 22 | 23 | //TODO: @Factory 24 | public static Contract restore(ContractNumber nr, SignDate signDate, CreditRating rating, VoteResult voteResult) { 25 | assert nr != null; 26 | assert signDate != null; 27 | // assert voteResult == null || rating != null // VoteResult != null => rating != null 28 | 29 | var restoredContract = new Contract(nr, signDate); 30 | restoredContract.creditRating = rating; 31 | restoredContract.voteResult = voteResult; 32 | return restoredContract; 33 | } 34 | 35 | @Identity 36 | public ContractNumber number() { 37 | return this.number; 38 | } 39 | 40 | public void checkCreditRating(CreditRating creditRating) { 41 | assert creditRating != null; 42 | assert !isVoted(); 43 | 44 | this.creditRating = creditRating; 45 | 46 | assert isRated(); 47 | } 48 | 49 | public boolean isRated() { 50 | return creditRating != null; 51 | } 52 | 53 | public CreditRating rating() { 54 | assert isRated() : "Precondition violated: isRated()"; 55 | 56 | return creditRating; 57 | } 58 | 59 | public void vote(VoteResult result) { 60 | assert result != null; 61 | assert isRated(); // TODO: Decide DbC-Mechanism 62 | 63 | this.voteResult = result; 64 | 65 | assert isVoted(); 66 | } 67 | 68 | public boolean isVoted() { 69 | return voteResult != null; 70 | } 71 | 72 | public VoteResult votingResult() { 73 | assert isVoted(); 74 | 75 | return this.voteResult; 76 | } 77 | 78 | // TODO: equals() and hashCode() 79 | 80 | } 81 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/io/leasingninja/riskmanagement/domain/ContractNumber.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.domain; 2 | 3 | public record ContractNumber(String contractnumber) { 4 | 5 | public static ContractNumber of(String contractnumber) { 6 | return new ContractNumber(contractnumber); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/io/leasingninja/riskmanagement/domain/ContractRepository.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.domain; 2 | 3 | import java.util.Collection; 4 | 5 | import org.jmolecules.ddd.annotation.Repository; 6 | 7 | @Repository 8 | public interface ContractRepository { 9 | 10 | Contract findById(ContractNumber number); 11 | 12 | Collection findAll(); 13 | 14 | void save(Contract contract); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/io/leasingninja/riskmanagement/domain/CreditRating.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.domain; 2 | 3 | public record CreditRating(int value) { 4 | 5 | public CreditRating { 6 | assert isValid(value); 7 | } 8 | 9 | public static boolean isValid(int value) { 10 | return value >= 1 && value <= 10; 11 | } 12 | 13 | public static CreditRating of(int value) { 14 | return new CreditRating(value); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/io/leasingninja/riskmanagement/domain/SignDate.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.domain; 2 | 3 | import java.time.LocalDate; 4 | 5 | public record SignDate(LocalDate date) { 6 | 7 | public static SignDate of(int year, int month, int dayOfMonth) { 8 | return new SignDate(LocalDate.of(year, month, dayOfMonth)); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/io/leasingninja/riskmanagement/domain/VoteResult.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.domain; 2 | 3 | import org.jmolecules.ddd.annotation.ValueObject; 4 | 5 | @ValueObject 6 | public enum VoteResult { 7 | ACCEPTED, 8 | ACCEPTED_WITH_OBLIGATIONS, 9 | REJECTED 10 | } 11 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/io/leasingninja/riskmanagement/infrastructure/ContractRepositoryMemImpl.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.infrastructure; 2 | 3 | import java.util.Collection; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import io.leasingninja.riskmanagement.domain.Contract; 8 | import io.leasingninja.riskmanagement.domain.ContractNumber; 9 | import io.leasingninja.riskmanagement.domain.ContractRepository; 10 | 11 | public class ContractRepositoryMemImpl implements ContractRepository { 12 | 13 | private final Map repo; 14 | 15 | public ContractRepositoryMemImpl() { 16 | repo = new HashMap<>(); 17 | } 18 | 19 | @Override 20 | public Contract findById(ContractNumber number) { 21 | return repo.get(number); 22 | } 23 | 24 | @Override 25 | public Collection findAll() { 26 | return repo.values(); 27 | } 28 | 29 | @Override 30 | public void save(Contract contract) { 31 | repo.put(contract.identity(), contract); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/io/leasingninja/riskmanagement/infrastructure/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | /** 5 | * @author henning 6 | * 7 | */ 8 | package io.leasingninja.riskmanagement.infrastructure; -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/io/leasingninja/riskmanagement/userinterface/ContractModel.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.userinterface; 2 | 3 | class ContractModel { 4 | 5 | public String number; 6 | public Integer creditRating; 7 | public String voteResult; 8 | 9 | public ContractModel() { 10 | this("", null, ""); 11 | } 12 | 13 | public ContractModel(String number, Integer creditRating, String voteResult) { 14 | this.number = number; 15 | this.creditRating = creditRating; 16 | this.voteResult = voteResult; 17 | } 18 | 19 | @Override 20 | public String toString() { 21 | return "ContractModel[" + 22 | "number = '" + number + '\'' + 23 | ", creditRating = '" + creditRating + '\'' + 24 | ", voteResult = '" + voteResult + '\'' + 25 | ']'; 26 | } 27 | 28 | public String getNumber() { 29 | return number; 30 | } 31 | 32 | public void setNumber(String number) { 33 | this.number = number; 34 | } 35 | 36 | public Integer getCreditRating() { 37 | return creditRating; 38 | } 39 | 40 | public void setCreditRating(Integer creditRating) { 41 | this.creditRating = creditRating; 42 | } 43 | 44 | public String getVoteResult() { 45 | return voteResult; 46 | } 47 | 48 | public void setVoteResult(String voteResult) { 49 | this.voteResult = voteResult; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/io/leasingninja/riskmanagement/userinterface/ContractModelMapper.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.userinterface; 2 | 3 | import io.leasingninja.riskmanagement.domain.Contract; 4 | 5 | import java.util.Objects; 6 | 7 | public class ContractModelMapper { 8 | 9 | public static ContractModel modelFrom(Contract contract) { // TODO: static?? 10 | Objects.requireNonNull(contract); 11 | 12 | return new ContractModel( 13 | contract.identity().contractnumber(), 14 | //contract.isRated() ? contract.rating().toString() : "", 15 | contract.isRated() ? Integer.valueOf(contract.rating().value()) : null, 16 | contract.isVoted() ? contract.votingResult().toString() : ""); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/io/leasingninja/riskmanagement/userinterface/RiskManagementController.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.userinterface; 2 | 3 | import java.util.List; 4 | import java.util.stream.Collectors; 5 | 6 | import org.slf4j.LoggerFactory; 7 | import org.slf4j.Logger; 8 | 9 | import org.springframework.stereotype.Controller; 10 | import org.springframework.ui.Model; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.PostMapping; 13 | import org.springframework.web.bind.annotation.RequestParam; 14 | 15 | import io.leasingninja.riskmanagement.application.CheckCreditRating; 16 | import io.leasingninja.riskmanagement.application.ListContracts; 17 | import io.leasingninja.riskmanagement.application.ReadContract; 18 | import io.leasingninja.riskmanagement.application.VoteContract; 19 | import io.leasingninja.riskmanagement.domain.ContractNumber; 20 | import io.leasingninja.riskmanagement.domain.CreditRating; 21 | import io.leasingninja.riskmanagement.domain.VoteResult; 22 | 23 | @Controller 24 | public class RiskManagementController { 25 | 26 | private static Logger logger = LoggerFactory.getLogger(RiskManagementController.class); 27 | 28 | private final ListContracts listContracts; 29 | private final ReadContract readContract; 30 | private final CheckCreditRating checkCreditRating; 31 | private final VoteContract voteContract; 32 | 33 | public RiskManagementController(ListContracts listContracts, ReadContract readContract, CheckCreditRating checkCreditRating, VoteContract voteContract) { 34 | this.listContracts = listContracts; 35 | this.readContract = readContract; 36 | this.checkCreditRating = checkCreditRating; 37 | this.voteContract = voteContract; 38 | } 39 | 40 | @GetMapping("/riskmanagement/contracts") 41 | public String listContracts(Model model) 42 | { 43 | List contractModels = this.listContracts.all().stream() 44 | .map(ContractModelMapper::modelFrom) 45 | .collect(Collectors.toUnmodifiableList()); 46 | logger.debug("Contract models: " + contractModels); 47 | model.addAttribute( 48 | "contracts", 49 | contractModels); 50 | return "contracts"; 51 | } 52 | 53 | // Todo: really needed? 54 | @GetMapping("/riskmanagement/contract") 55 | public String showContract( 56 | @RequestParam(name="number", required = true) String vertragsnummerString, 57 | Model model) { 58 | // if (!Contractnumber.isValid(contractNumber)) { 59 | // // TODO: 60 | // return "Invalid Contract Number: " + contractNumber; 61 | // } 62 | 63 | var contract = this.readContract.readContract(ContractNumber.of(vertragsnummerString)); 64 | if (contract == null) { 65 | return "No contract with number " + vertragsnummerString + " in inbox."; 66 | } 67 | 68 | var vertragModel = ContractModelMapper.modelFrom(contract); 69 | model.addAttribute("contract", vertragModel); 70 | model.addAttribute("editing_disabled", contract.isVoted()); 71 | 72 | return "contract"; 73 | } 74 | 75 | @GetMapping("/riskmanagement/rating") 76 | public String showCreditRating( 77 | @RequestParam(name="contract_number", required = true) String contractNumberString, 78 | Model model) { 79 | 80 | var contract = this.readContract.readContract(ContractNumber.of(contractNumberString)); 81 | if (contract == null) { 82 | return "No contract with number " + contractNumberString + " in inbox."; 83 | } 84 | 85 | var contractModel = ContractModelMapper.modelFrom(contract); 86 | model.addAttribute("contract", contractModel); 87 | model.addAttribute("editing_disabled", contract.isVoted()); 88 | 89 | return "rating"; 90 | } 91 | 92 | @PostMapping("/riskmanagement/rate_contract") 93 | public String enterCreditRating( 94 | @RequestParam(name="contract_number") String contractNumber, 95 | @RequestParam(name="creditRating") Integer creditRatingString, 96 | Model model) { 97 | if (contractNumber == null || creditRatingString == null || !CreditRating.isValid(creditRatingString)) { 98 | return "Invalid parameters: contract number: " + contractNumber + ", credit rating: " + creditRatingString; 99 | } 100 | 101 | logger.debug("Trying to enter credit rating " + creditRatingString + " for contract " + contractNumber); 102 | this.checkCreditRating.checkCreditRating( 103 | ContractNumber.of(contractNumber), 104 | CreditRating.of(creditRatingString)); 105 | 106 | return "redirect:/riskmanagement/rating?contract_number=" + contractNumber; 107 | } 108 | 109 | @PostMapping("/riskmanagement/vote") 110 | public String voteContract( 111 | @RequestParam(name="contract_number") String contractNumber, 112 | @RequestParam(name="vote_result") String voteResult, 113 | Model model) { 114 | // if (!Contractnumber.isValid(contractNumber)) { 115 | // // TODO: 116 | // return "Invalid Contract Number: " + contractNumber; 117 | // } 118 | try { 119 | VoteResult.valueOf(voteResult); 120 | } catch(IllegalArgumentException e) { 121 | // TODO: 122 | return "Invalid vote result: " + voteResult; 123 | } 124 | 125 | this.voteContract.vote( 126 | ContractNumber.of(contractNumber), 127 | VoteResult.valueOf(voteResult)); 128 | return "redirect:/riskmanagement/vote?number=" + contractNumber; 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Henning Schwentner 3 | * 4 | */ 5 | module io.leasingninja.riskmanagement { 6 | exports io.leasingninja.riskmanagement.application; 7 | 8 | requires org.slf4j; 9 | 10 | requires org.jmolecules.architecture.layered; 11 | requires org.jmolecules.ddd; 12 | requires io.hschwentner.dddbits; 13 | 14 | requires spring.beans; 15 | // requires spring.core; 16 | requires spring.context; 17 | requires spring.web; 18 | requires spring.webmvc; 19 | // requires spring.boot; 20 | // requires spring.boot.autoconfigure; 21 | } 22 | 23 | 24 | 25 | 26 | 27 | 28 | //requires org.junit.jupiter.api; 29 | // eine Sache, damit man das auch mit Eclipse nicht braucht: JUnit in Classpath statt in Modulepath. 30 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/resources/templates/contract.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LeasingNinja – Sales 7 | 8 | 9 | 10 | 11 |

Leasing contract

12 |
13 |
14 |
15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 |
23 | 24 | 25 |
26 |
27 | 28 | 29 | 34 |
35 |
36 | 37 | 38 |
39 |
40 |
41 | 42 | 43 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/resources/templates/contracts.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | LeasingNinja – Risk management 6 | 7 | 8 | 9 | 10 |

Risk management

11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
Contract Credit rating Vote result
No contracts to vote
Contract Credit rating Vote result Rate
31 | 32 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/resources/templates/rating.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LeasingNinja – Risk management 7 | 8 | 9 | 10 | 11 |

Risk management—Rating a contract

12 |
13 |
14 |
15 | 16 | 21 |
22 |
23 | 24 | 29 |
30 |
31 | 32 |
33 |
34 |
35 | 36 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/main/resources/templates/vote.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LeasingNinja – Risk management 7 | 8 | 9 | 10 | 11 |

Risk management—Voting a contract

12 |
13 |
14 |
15 | 16 | 17 |
18 |
19 | 20 | 25 |
26 |
27 | 28 | 29 |
30 |
31 |
32 | 33 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/test/java/io/leasingninja/riskmanagement/application/CheckCreditRatingTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.application; 2 | 3 | import static org.assertj.core.api.Assertions.*; 4 | import static org.mockito.ArgumentMatchers.*; 5 | //import static org.mockito.ArgumentMatchers.*; 6 | import static org.mockito.BDDMockito.*; 7 | 8 | import org.junit.jupiter.api.Test; 9 | import org.junit.jupiter.api.extension.ExtendWith; 10 | import org.mockito.InjectMocks; 11 | import org.mockito.Mock; 12 | import org.mockito.junit.jupiter.MockitoExtension; 13 | 14 | import io.leasingninja.riskmanagement.domain.Contract; 15 | import io.leasingninja.riskmanagement.domain.ContractNumber; 16 | import io.leasingninja.riskmanagement.domain.ContractRepository; 17 | import io.leasingninja.riskmanagement.domain.CreditRating; 18 | import io.leasingninja.riskmanagement.domain.SignDate; 19 | import io.leasingninja.riskmanagement.domain.VoteResult; 20 | 21 | @ExtendWith(MockitoExtension.class) 22 | class CheckCreditRatingTest { 23 | 24 | @Mock 25 | private ContractRepository contractRepositoryMock; 26 | 27 | @InjectMocks 28 | private CheckCreditRating serviceUnderTest; 29 | 30 | @Test 31 | void testDomainLayerIntegrationTest() { 32 | // given 33 | var contract = new Contract(ContractNumber.of("4711"), SignDate.of(2018, 4, 1)); 34 | 35 | // when 36 | contract.checkCreditRating(CreditRating.of(3)); 37 | // contract.calculateResaleValue(); //TODO: wollen wir das? 38 | contract.vote(VoteResult.ACCEPTED); 39 | 40 | // then 41 | assertThat(contract.isRated()).isTrue(); 42 | assertThat(contract.rating()).isEqualTo(CreditRating.of(3)); 43 | assertThat(contract.isVoted()).isTrue(); 44 | } 45 | 46 | // @Test 47 | // void test() { 48 | // // given 49 | // ein unterschriebener Vertrag 50 | // 51 | // // when 52 | // checkCreditRating() 53 | // calculateResaleValue() 54 | // voteContract(); 55 | // votiere 56 | // 57 | // // then 58 | // Vertrag hat Bonität und Zustand isVoted 59 | // } 60 | 61 | @Test 62 | void givenAContract_whenCheckCreditRating_thenContractRated() { 63 | // given 64 | given(contractRepositoryMock.findById(ContractNumber.of("4711"))) 65 | .willReturn(new Contract(ContractNumber.of("4711"), SignDate.of(2018, 4, 1))); 66 | 67 | // when 68 | serviceUnderTest.checkCreditRating(ContractNumber.of("4711"), CreditRating.of(3)); 69 | 70 | // then 71 | then(contractRepositoryMock).should().save(refEq(Contract.restore( 72 | ContractNumber.of("4711"), 73 | SignDate.of(2018, 4, 1), 74 | CreditRating.of(3), 75 | null))); 76 | // Contract contract = serviceUnderTest.readContract(Contractnumber.of("4711")); // TODO: or showContract 77 | // assertThat(contract.isRated()).isTrue(); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/test/java/io/leasingninja/riskmanagement/application/InboxApplicationServiceTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.application; 2 | 3 | import static org.junit.jupiter.api.Assertions.*; 4 | import static org.mockito.ArgumentMatchers.refEq; 5 | import static org.mockito.BDDMockito.then; 6 | 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.extension.ExtendWith; 9 | import org.mockito.InjectMocks; 10 | import org.mockito.Mock; 11 | import org.mockito.junit.jupiter.MockitoExtension; 12 | 13 | import io.leasingninja.riskmanagement.domain.Contract; 14 | import io.leasingninja.riskmanagement.domain.ContractNumber; 15 | import io.leasingninja.riskmanagement.domain.ContractRepository; 16 | import io.leasingninja.riskmanagement.domain.SignDate; 17 | 18 | @ExtendWith(MockitoExtension.class) 19 | class InboxApplicationServiceTest { 20 | 21 | @Mock 22 | private ContractRepository contracts; 23 | 24 | @InjectMocks 25 | private InboxApplicationServiceImpl inboxUnderTest; 26 | 27 | @Test 28 | void givenAnEmptyInbox_whenConfirmSignedContract_thenContractIsSaved() { 29 | // Given 30 | 31 | // When 32 | inboxUnderTest.confirmSignedContract("4711", 2019, 12, 24); 33 | 34 | // Then 35 | then(contracts).should().save(refEq(new Contract( 36 | ContractNumber.of("4711"), 37 | SignDate.of(2019, 12, 24)))); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/test/java/io/leasingninja/riskmanagement/application/ListContractsTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.application; 2 | 3 | import static org.assertj.core.api.Assertions.*; 4 | //import static org.mockito.ArgumentMatchers.*; 5 | import static org.mockito.BDDMockito.*; 6 | 7 | import java.util.List; 8 | 9 | import org.junit.jupiter.api.Test; 10 | import org.junit.jupiter.api.extension.ExtendWith; 11 | import org.mockito.InjectMocks; 12 | import org.mockito.Mock; 13 | import org.mockito.junit.jupiter.MockitoExtension; 14 | 15 | import io.leasingninja.riskmanagement.domain.Contract; 16 | import io.leasingninja.riskmanagement.domain.ContractNumber; 17 | import io.leasingninja.riskmanagement.domain.ContractRepository; 18 | import io.leasingninja.riskmanagement.domain.SignDate; 19 | 20 | @ExtendWith(MockitoExtension.class) 21 | class ListContractsTest { 22 | 23 | @Mock 24 | private ContractRepository contractRepositoryMock; 25 | 26 | @InjectMocks 27 | private ListContracts listContractsUnderTest; 28 | 29 | @Test 30 | void givenAContract_whenRead_thenContractIsRead() { 31 | // given 32 | given(contractRepositoryMock.findAll()) 33 | .willReturn(List.of(new Contract( 34 | ContractNumber.of("4711"), 35 | SignDate.of(2018, 4, 1)))); 36 | 37 | // when 38 | var contracts = listContractsUnderTest.all(); 39 | 40 | // then 41 | // TODO: use refEq() to compare all fields 42 | assertThat(contracts).isEqualTo(List.of(new Contract( 43 | ContractNumber.of("4711"), 44 | SignDate.of(2018, 4, 1)))); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/test/java/io/leasingninja/riskmanagement/application/ReadContractTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.application; 2 | 3 | import static org.assertj.core.api.Assertions.*; 4 | //import static org.mockito.ArgumentMatchers.*; 5 | import static org.mockito.BDDMockito.*; 6 | 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.extension.ExtendWith; 9 | import org.mockito.InjectMocks; 10 | import org.mockito.Mock; 11 | import org.mockito.junit.jupiter.MockitoExtension; 12 | 13 | import io.leasingninja.riskmanagement.domain.Contract; 14 | import io.leasingninja.riskmanagement.domain.ContractNumber; 15 | import io.leasingninja.riskmanagement.domain.ContractRepository; 16 | import io.leasingninja.riskmanagement.domain.SignDate; 17 | 18 | @ExtendWith(MockitoExtension.class) 19 | class ReadContractTest { 20 | 21 | @Mock 22 | private ContractRepository contractRepositoryMock; 23 | 24 | @InjectMocks 25 | private ReadContract serviceUnderTest; 26 | 27 | @Test 28 | void givenAContract_whenRead_thenContractIsRead() { 29 | // given 30 | given(contractRepositoryMock.findById(ContractNumber.of("4711"))) 31 | .willReturn(new Contract( 32 | ContractNumber.of("4711"), 33 | SignDate.of(2018, 4, 1))); 34 | 35 | // when 36 | var contract = serviceUnderTest.readContract(ContractNumber.of("4711")); 37 | 38 | // then 39 | assertThat(contract).usingRecursiveComparison().isEqualTo(new Contract( 40 | ContractNumber.of("4711"), 41 | SignDate.of(2018, 4, 1))); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/test/java/io/leasingninja/riskmanagement/application/VoteContractTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.application; 2 | 3 | import static org.mockito.ArgumentMatchers.*; 4 | //import static org.mockito.ArgumentMatchers.*; 5 | import static org.mockito.BDDMockito.*; 6 | 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.extension.ExtendWith; 9 | import org.mockito.InjectMocks; 10 | import org.mockito.Mock; 11 | import org.mockito.junit.jupiter.MockitoExtension; 12 | 13 | import io.leasingninja.riskmanagement.domain.Contract; 14 | import io.leasingninja.riskmanagement.domain.ContractNumber; 15 | import io.leasingninja.riskmanagement.domain.ContractRepository; 16 | import io.leasingninja.riskmanagement.domain.CreditRating; 17 | import io.leasingninja.riskmanagement.domain.SignDate; 18 | import io.leasingninja.riskmanagement.domain.VoteResult; 19 | 20 | @ExtendWith(MockitoExtension.class) 21 | class VoteContractTest { 22 | 23 | @Mock 24 | private ContractRepository contractRepositoryMock; 25 | 26 | @InjectMocks 27 | private VoteContract serviceUnderTest; 28 | 29 | @Test 30 | void givenARatedContract_whenVote_thenContractVoted() { 31 | // given 32 | given(contractRepositoryMock.findById(ContractNumber.of("4711"))) 33 | .willReturn(Contract.restore( 34 | ContractNumber.of("4711"), 35 | SignDate.of(2018, 4, 1), 36 | CreditRating.of(3), 37 | null)); 38 | 39 | // when 40 | serviceUnderTest.vote(ContractNumber.of("4711"), VoteResult.ACCEPTED); 41 | 42 | // then 43 | then(contractRepositoryMock).should().save(refEq(Contract.restore( 44 | ContractNumber.of("4711"), 45 | SignDate.of(2018, 4, 1), 46 | CreditRating.of(3), 47 | VoteResult.ACCEPTED))); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/test/java/io/leasingninja/riskmanagement/domain/ContractTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.domain; 2 | 3 | import static org.assertj.core.api.Assertions.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class ContractTest { 8 | 9 | @Test 10 | void givenASignedContract_whenCheckCreditRating_ThenRated() { 11 | // given 12 | var contract = new Contract(ContractNumber.of("4711"), SignDate.of(2018, 4, 1)); 13 | 14 | // when 15 | contract.checkCreditRating(CreditRating.of(3)); 16 | 17 | // then 18 | assertThat(contract.isRated()).isTrue(); 19 | assertThat(contract.rating()).isEqualTo(CreditRating.of(3)); 20 | } 21 | 22 | @Test 23 | void givenARatedContract_whenVote_ThenVoted() { 24 | // given 25 | var contract = new Contract(ContractNumber.of("4711"), SignDate.of(2018, 4, 1)); 26 | contract.checkCreditRating(CreditRating.of(3)); 27 | 28 | // when 29 | contract.vote(VoteResult.ACCEPTED); 30 | 31 | // then 32 | assertThat(contract.isVoted()).isTrue(); 33 | } 34 | 35 | @Test 36 | void restoreContract() { 37 | // given 38 | 39 | // when 40 | var contract = Contract.restore( 41 | ContractNumber.of("4711"), 42 | SignDate.of(2018, 4, 1), 43 | CreditRating.of(3), 44 | VoteResult.ACCEPTED_WITH_OBLIGATIONS); 45 | 46 | // then 47 | assertThat(contract.identity()).isEqualTo(ContractNumber.of("4711")); 48 | // assertThat(contract.signDate()).isEqualTo(SignDate.of(2018, 4, 1)); 49 | assertThat(contract.rating()).isEqualTo(CreditRating.of(3)); 50 | assertThat(contract.isVoted()).isTrue(); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/test/java/io/leasingninja/riskmanagement/domain/CreditRatingTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.domain; 2 | 3 | import static org.assertj.core.api.Assertions.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class CreditRatingTest { 8 | @Test 9 | void testIsValid() { 10 | assertThat(CreditRating.isValid(0)).isFalse(); 11 | assertThat(CreditRating.isValid(1)).isTrue(); 12 | assertThat(CreditRating.isValid(3)).isTrue(); 13 | assertThat(CreditRating.isValid(10)).isTrue(); 14 | assertThat(CreditRating.isValid(11)).isFalse(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /leasingninja-riskmanagement/src/test/java/io/leasingninja/riskmanagement/userinterface/ContractModelMapperTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.riskmanagement.userinterface; 2 | 3 | import static org.assertj.core.api.Assertions.*; 4 | 5 | import io.leasingninja.riskmanagement.domain.*; 6 | import org.junit.jupiter.api.Test; 7 | 8 | class ContractModelMapperTest { 9 | 10 | @Test 11 | void givenAnUnratedContract_whenMapped_thenUnvotedModel() { 12 | // Given 13 | var contract = new Contract(ContractNumber.of("12345"), SignDate.of(2019, 3, 5)); 14 | 15 | // When 16 | var model = ContractModelMapper.modelFrom(contract); 17 | 18 | // Then 19 | assertThat(model.number).isEqualTo("12345"); 20 | assertThat(model.creditRating).isEqualTo(null); 21 | assertThat(model.voteResult).isEqualTo(""); 22 | } 23 | 24 | @Test 25 | void givenARatedButUnvotedContract_whenMapped_thenUnvotedModel() { 26 | // Given 27 | var contract = new Contract(ContractNumber.of("12345"), SignDate.of(2019, 3, 5)); 28 | contract.checkCreditRating(CreditRating.of(3)); 29 | 30 | // When 31 | var model = ContractModelMapper.modelFrom(contract); 32 | 33 | // Then 34 | assertThat(model.number).isEqualTo("12345"); 35 | assertThat(model.creditRating).isEqualTo(3); 36 | assertThat(model.voteResult).isEqualTo(""); 37 | } 38 | 39 | @Test 40 | void givenAVotedContract_whenMapped_thenUnvotedModel() { 41 | // Given 42 | var contract = new Contract(ContractNumber.of("12345"), SignDate.of(2019, 3, 5)); 43 | contract.checkCreditRating(CreditRating.of(3)); 44 | contract.vote(VoteResult.ACCEPTED); 45 | 46 | // When 47 | var model = ContractModelMapper.modelFrom(contract); 48 | 49 | // Then 50 | assertThat(model.number).isEqualTo("12345"); 51 | assertThat(model.creditRating).isEqualTo(3); 52 | assertThat(model.voteResult).isEqualTo("ACCEPTED"); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /leasingninja-sales/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | 7 | io.leasingninja 8 | leasingninja 9 | 0.0.1-SNAPSHOT 10 | 11 | 12 | leasingninja-sales 13 | LeasingNinja sales 14 | Sales Bounded Context of LeasingNinja 15 | 16 | 17 | 18 | io.leasingninja 19 | leasingninja-riskmanagement 20 | 0.0.1-SNAPSHOT 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/application/FilloutContract.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.application; 2 | 3 | import org.jmolecules.architecture.layered.ApplicationLayer; 4 | import org.jmolecules.ddd.annotation.Service; 5 | 6 | import io.leasingninja.sales.domain.Amount; 7 | import io.leasingninja.sales.domain.Car; 8 | import io.leasingninja.sales.domain.Contract; 9 | import io.leasingninja.sales.domain.ContractNumber; 10 | import io.leasingninja.sales.domain.Contracts; 11 | import io.leasingninja.sales.domain.Customer; 12 | 13 | @ApplicationLayer 14 | @Service 15 | public class FilloutContract { 16 | 17 | private final Contracts contracts; 18 | 19 | public FilloutContract(Contracts contracts) { 20 | this.contracts = contracts; 21 | } 22 | 23 | public void with(ContractNumber number, Customer customer, Car car, Amount price) { 24 | contracts.save(new Contract( 25 | number, 26 | customer, 27 | car, 28 | price)); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/application/SignContract.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.application; 2 | 3 | import io.hschwentner.dddbits.annotation.ApplicationService; 4 | import io.leasingninja.riskmanagement.application.InboxApplicationService; 5 | import io.leasingninja.sales.domain.ContractNumber; 6 | import io.leasingninja.sales.domain.Contracts; 7 | import io.leasingninja.sales.domain.SignDate; 8 | 9 | @ApplicationService 10 | public class SignContract { 11 | 12 | private final Contracts contracts; 13 | private final InboxApplicationService riskmanagementInbox; 14 | 15 | public SignContract(Contracts contracts, InboxApplicationService riskmanagementInbox) { 16 | this.contracts = contracts; 17 | this.riskmanagementInbox = riskmanagementInbox; 18 | } 19 | 20 | public void with(ContractNumber number, SignDate signDate) { 21 | assert number != null; 22 | assert signDate != null; 23 | 24 | var contract = this.contracts.with(number); 25 | 26 | contract.sign(signDate); 27 | 28 | this.contracts.save(contract); 29 | 30 | riskmanagementInbox.confirmSignedContract(number.number(), signDate.year(), signDate.month(), signDate.day()); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/application/UnterschreibeVertragApplicationService_MitEvent.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.application; 2 | 3 | import java.time.LocalDate; 4 | //import java.beans. 5 | import java.util.concurrent.SubmissionPublisher; 6 | 7 | import io.leasingninja.sales.domain.*; 8 | 9 | //public class UnterschreibeVertragApplicationService extends Observable implements Flow.Publisher{ 10 | public class UnterschreibeVertragApplicationService_MitEvent extends SubmissionPublisher{ 11 | 12 | public void fuelleVertragsformularAus(String vertragsnummer, String kundenname, String fahrzeug, int preis, String waehrung) { 13 | new Contract( 14 | ContractNumber.of(vertragsnummer), 15 | Customer.of(kundenname), 16 | Car.of(fahrzeug), 17 | Amount.of(preis, Currency.valueOf(waehrung))); 18 | } 19 | 20 | public void unterschreibeVertrag(String vnr, String unterschriftsdatum) { 21 | // TODO Auto-generated method stub 22 | 23 | submit(new ContractSigned(ContractNumber.of(vnr), SignDate.of(LocalDate.parse(unterschriftsdatum)))); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/application/ViewContract.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.application; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import io.hschwentner.dddbits.annotation.ApplicationService; 7 | import io.leasingninja.sales.domain.Contract; 8 | import io.leasingninja.sales.domain.ContractNumber; 9 | import io.leasingninja.sales.domain.Contracts; 10 | 11 | @ApplicationService 12 | public class ViewContract { 13 | 14 | private static Logger logger = LoggerFactory.getLogger(ViewContract.class); 15 | 16 | private final Contracts contracts; 17 | 18 | public ViewContract(Contracts contracts) { 19 | this.contracts = contracts; 20 | } 21 | 22 | public Contract with(ContractNumber number) { 23 | var contract = this.contracts.with(number); 24 | logger.debug("Repository returned contract: " + contract); 25 | return contract; 26 | } 27 | 28 | // public VertragModel with(String vertragsnummer) { 29 | // var vertrag = this.vertragRepo.findById(Vertragsnummer.of(vertragsnummer)); 30 | // logger.debug("UnterschreibeVertragApplicationService: vertrag: " + vertrag); 31 | // return vertrag != null 32 | // ? VertragModelMapper.modelFrom(vertrag) 33 | // : new VertragModel(); 34 | //// return VertragModelMapper.INSTANCE.vertragToVertragModel(vertrag); 35 | // } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/config/SalesConfig.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | import io.leasingninja.riskmanagement.application.InboxApplicationService; 7 | import io.leasingninja.sales.application.FilloutContract; 8 | import io.leasingninja.sales.application.SignContract; 9 | import io.leasingninja.sales.application.ViewContract; 10 | import io.leasingninja.sales.domain.Contracts; 11 | import io.leasingninja.sales.infrastructure.ContractDatabaseEntityRepository; 12 | import io.leasingninja.sales.infrastructure.ContractsJpaImpl; 13 | 14 | @Configuration 15 | public class SalesConfig { 16 | 17 | // Arbeitshypothese: DI hierüber, höherwertige Spring-Technologien (Repo-Magic, Web-MVC) über Stereotypen 18 | 19 | // Infrastructure layer 20 | @Bean 21 | public Contracts salesContractRepository(ContractDatabaseEntityRepository repo) { 22 | return new ContractsJpaImpl(repo); 23 | } 24 | 25 | // Application layer 26 | @Bean 27 | public FilloutContract salesFilloutContract(Contracts contracts) { 28 | return new FilloutContract(contracts); 29 | } 30 | 31 | @Bean 32 | public SignContract salesSignContract(Contracts contracts, InboxApplicationService riskmanagementInbox) { 33 | return new SignContract(contracts, riskmanagementInbox); 34 | } 35 | 36 | @Bean 37 | public ViewContract salesViewContract(Contracts contracts) { 38 | return new ViewContract(contracts); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/config/SalesConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.config; 2 | 3 | //import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | //import io.leasingninja.risikomanagement.application.InboxApplicationService; 7 | //import io.leasingninja.vertrieb.infrastructure.VertragRepositoryMemImpl; 8 | 9 | @Configuration 10 | public class SalesConfiguration { 11 | 12 | // @Bean 13 | // public UnterschreibeVertragApplicationService unterschreibeVertragApplicationService() { 14 | // return new UnterschreibeVertragApplicationService( 15 | //// new VertragRepositoryMemImpl(), 16 | // new VertragRepositoryJpaImpl(), 17 | // new InboxApplicationService() { 18 | // 19 | // @Override 20 | // public void meldeUnterschriebenenVertrag(String lvnr, int year, int month, int dayOfMonth) { 21 | // // TODO Auto-generated method stub 22 | // 23 | // } 24 | // }); 25 | // } 26 | } 27 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/domain/Amount.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | 4 | import org.jmolecules.ddd.annotation.ValueObject; 5 | 6 | @ValueObject 7 | public record Amount(long amountInCents, Currency currency) { 8 | 9 | public static Amount of(double amount, Currency currency) { 10 | assert currency != null; 11 | 12 | return ofCents(Math.round(amount * 100), currency); 13 | } 14 | 15 | public static Amount ofCents(long amountInCents, Currency currency) { 16 | assert currency != null; 17 | 18 | return new Amount(amountInCents, currency); 19 | } 20 | 21 | public double amount() { 22 | return amountInCents / 100d; 23 | } 24 | 25 | public Amount add(Amount otherAmount) { 26 | assert this.currency == otherAmount.currency; 27 | 28 | return ofCents(this.amountInCents + otherAmount.amountInCents, this.currency); 29 | } 30 | 31 | public Amount subtract(Amount otherAmount) { 32 | assert this.currency == otherAmount.currency; 33 | 34 | return ofCents(this.amountInCents - otherAmount.amountInCents, this.currency); 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return currency() + " " + amount(); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/domain/Car.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import org.jmolecules.ddd.annotation.ValueObject; 4 | 5 | @ValueObject 6 | public record Car(String car) { 7 | 8 | public static Car of(String car) { 9 | return new Car(car); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/domain/Contract.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import static java.util.Objects.requireNonNull; 4 | 5 | import java.util.Optional; 6 | 7 | import org.jmolecules.ddd.annotation.Entity; 8 | import org.jmolecules.ddd.annotation.Identity; 9 | 10 | @Entity 11 | public class Contract { 12 | 13 | @Identity 14 | private final ContractNumber number; 15 | 16 | private final Customer lessee; 17 | private final Car car; 18 | private final Amount price; 19 | 20 | private record Calculation(LeaseTerm leaseTerm, Interest interest, Amount installment) {} 21 | private Optional calculation; 22 | 23 | private Optional signDate; 24 | 25 | public Contract(ContractNumber number, Customer lessee, Car car, Amount price) { 26 | requireNonNull(number); 27 | requireNonNull(lessee); 28 | requireNonNull(car); 29 | requireNonNull(price); 30 | 31 | this.number = number; 32 | this.lessee = lessee; 33 | this.car = car; 34 | this.price = price; 35 | this.calculation = Optional.empty(); 36 | this.signDate = Optional.empty(); 37 | } 38 | 39 | @Identity 40 | public ContractNumber number() { 41 | return this.number; 42 | } 43 | 44 | public Customer lessee() { 45 | return this.lessee; 46 | } 47 | 48 | public Car car() { 49 | return this.car; 50 | } 51 | 52 | public Amount price() { 53 | return this.price; 54 | } 55 | 56 | public boolean isCalculated() { 57 | return this.calculation.isPresent(); 58 | } 59 | 60 | public void calculateInstallmentFor(LeaseTerm leaseTerm, Interest interest) { 61 | requireNonNull(leaseTerm); 62 | requireNonNull(interest); 63 | assert !isSigned(); 64 | 65 | double inAdvance = 0; 66 | double residualValue = 0; 67 | 68 | double pmt = FinancialCalculator.pmt( 69 | leaseTerm.noOfMonths(), 70 | interest.perMonth(), 71 | -1 * price().amount(), 72 | residualValue, 73 | inAdvance); 74 | 75 | this.calculation = Optional.of(new Calculation(leaseTerm, interest, Amount.of(pmt, price.currency()))); 76 | 77 | assert isCalculated(); 78 | } 79 | 80 | public LeaseTerm leaseTerm() { 81 | assert isCalculated(); 82 | return this.calculation.get().leaseTerm(); 83 | } 84 | 85 | public Interest interest() { 86 | assert isCalculated(); 87 | return this.calculation.get().interest(); 88 | } 89 | 90 | public Amount installment() { 91 | assert isCalculated(); 92 | return this.calculation.get().installment(); 93 | } 94 | 95 | public void sign(SignDate date) { 96 | requireNonNull(date); 97 | assert isCalculated(); 98 | assert !isSigned(); 99 | 100 | this.signDate = Optional.of(date); 101 | 102 | assert isSigned(); 103 | } 104 | 105 | public boolean isSigned() { 106 | return this.signDate.isPresent(); 107 | } 108 | 109 | public SignDate signDate() { 110 | assert isSigned(); 111 | return this.signDate.get(); 112 | } 113 | 114 | @Override 115 | public String toString() { 116 | return "Contract [number=" + number() + ", lessee=" + this.lessee + ", car=" + this.car 117 | + ", price=" + this.price + ", signDate=" + this.signDate + "]"; 118 | } 119 | 120 | @Override 121 | public final int hashCode() { 122 | return this.number.hashCode(); 123 | } 124 | 125 | @Override 126 | public final boolean equals(Object obj) { 127 | if (this == obj) 128 | return true; 129 | if (obj == null) 130 | return false; 131 | if (getClass() != obj.getClass()) 132 | return false; 133 | Contract other = (Contract) obj; 134 | if (this.number == null) { 135 | if (other.number() != null) 136 | return false; 137 | } else if (!this.number.equals(other.number())) 138 | return false; 139 | return true; 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/domain/ContractFactory.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import static java.util.Objects.requireNonNull; 4 | 5 | import java.util.Optional; 6 | 7 | import org.jmolecules.ddd.annotation.Factory; 8 | 9 | @Factory 10 | public class ContractFactory { 11 | 12 | public static Contract restoreContract(ContractNumber number, Customer lessee, Car car, Amount price, Optional leaseTerm, Optional interest, Optional signDate) { 13 | requireNonNull(number); 14 | requireNonNull(lessee); 15 | requireNonNull(car); 16 | requireNonNull(price); 17 | 18 | var contract = new Contract(number, lessee, car, price); 19 | if (leaseTerm.isPresent() && interest.isPresent()) { 20 | contract.calculateInstallmentFor(leaseTerm.get(), interest.get()); 21 | } 22 | //leaseTerm.ifPresent(contract::calculateInstallmentFor(leaseTerm.get(), interest.get())); 23 | signDate.ifPresent(contract::sign); 24 | 25 | return contract; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/domain/ContractNumber.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import org.jmolecules.ddd.annotation.ValueObject; 4 | 5 | @ValueObject 6 | public record ContractNumber(String number) { 7 | 8 | public static ContractNumber of(String number) { 9 | return new ContractNumber(number); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/domain/ContractSigned.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import org.jmolecules.event.annotation.DomainEvent; 4 | 5 | @DomainEvent 6 | public final record ContractSigned(ContractNumber contract, SignDate signDate) { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/domain/Contracts.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import org.jmolecules.ddd.annotation.Repository; 4 | 5 | @Repository 6 | public interface Contracts { 7 | 8 | Contract with(ContractNumber number); 9 | 10 | void save(Contract contract); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/domain/Currency.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import org.jmolecules.ddd.annotation.ValueObject; 4 | 5 | @ValueObject 6 | public enum Currency { 7 | EUR, GBP, USD 8 | } 9 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/domain/Customer.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import org.jmolecules.ddd.annotation.ValueObject; 4 | 5 | @ValueObject 6 | public record Customer(String customer) { 7 | 8 | public static Customer of(String customer) { 9 | assert isValid(customer); 10 | return new Customer(customer); 11 | } 12 | 13 | public static boolean isValid(String nameString) { 14 | return nameString.matches("^\\p{L}+(\\s\\p{L}+)*$"); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/domain/FinancialCalculator.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import org.jmolecules.ddd.annotation.Service; 4 | 5 | /** 6 | * Simulates the infamous HP12c calculator that is widely used in the leasing industry. 7 | */ 8 | @Service 9 | public class FinancialCalculator { 10 | 11 | /** 12 | * @param n number of periods 13 | * @param iInPercent percentage interest rate per period 14 | * @param pv present value 15 | * @param fv future value 16 | * @param s 17 | * @return payment per period 18 | */ 19 | public static double pmt(double n, double iInPercent, double pv, double fv, double s) { 20 | double i = iInPercent / 100.0; 21 | 22 | return pmtWithDecimalInterestRate(n, i, pv, fv, s); 23 | } 24 | 25 | /** 26 | * @param n number of periods 27 | * @param i decimal interest rate per period 28 | * @param pv present value 29 | * @param fv future value 30 | * @param s 31 | * @return payment per period 32 | */ 33 | private static double pmtWithDecimalInterestRate(double n, double i, double pv, double fv, double s) { 34 | if (i == 0.0) { 35 | return (-1.0 * pv - fv) / n; 36 | } 37 | 38 | return (i * (fv + pv * Math.pow(1.0 + i, n))) / ((1.0 + i * s) * (1.0 - Math.pow(1.0 + i, n))); 39 | } 40 | 41 | } 42 | 43 | // Alternative implementation: 44 | 45 | /* 46 | return 47 | (-1 * pv * Math.pow(1 + i, n) - fv) 48 | / 49 | ( 50 | (1 + i * s) 51 | * 52 | ( 53 | (Math.pow(1 + i, n) - 1) 54 | / 55 | i 56 | ) 57 | ); 58 | */ 59 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/domain/Interest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import org.jmolecules.ddd.annotation.ValueObject; 4 | 5 | /** 6 | * Interest in percent. 7 | */ 8 | @ValueObject 9 | public record Interest(double perYear) { 10 | public Interest { 11 | assert perYear >= 0; 12 | } 13 | 14 | public static Interest of(double perYear) { 15 | return new Interest(perYear); 16 | } 17 | 18 | public double perMonth() { 19 | return perYear() / 12; 20 | } 21 | 22 | @Override 23 | public String toString() { 24 | return "Interest [" + perYear() + "% p.a.]"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/domain/LeaseTerm.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import org.jmolecules.ddd.annotation.ValueObject; 4 | 5 | @ValueObject 6 | public record LeaseTerm(int noOfMonths) { 7 | public LeaseTerm { 8 | assert noOfMonths > 0; 9 | } 10 | 11 | public static LeaseTerm ofMonths(int noOfMonths) { 12 | return new LeaseTerm(noOfMonths); 13 | } 14 | 15 | public static LeaseTerm ofYears(int noOfYears) { 16 | return ofMonths(noOfYears * 12); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/domain/SignDate.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import java.time.LocalDate; 4 | 5 | import org.jmolecules.ddd.annotation.ValueObject; 6 | 7 | @ValueObject 8 | public record SignDate(LocalDate date) { 9 | 10 | // TODO: both of() variants? 11 | public static SignDate of(LocalDate date) { 12 | assert date != null; 13 | return new SignDate(date); 14 | } 15 | 16 | public static SignDate of(int year, int month, int dayOfMonth) { 17 | return of(LocalDate.of(year, month, dayOfMonth)); 18 | } 19 | 20 | // TODO: year() vs. getYear im Domain Model? 21 | public final int year() { 22 | return date.getYear(); 23 | } 24 | 25 | public final int month() { 26 | return date.getMonthValue(); 27 | } 28 | 29 | public final int day() { 30 | return date.getDayOfMonth(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/infrastructure/ContractDatabaseEntity.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.infrastructure; 2 | 3 | import java.time.LocalDate; 4 | import java.util.Optional; 5 | 6 | import jakarta.persistence.Column; 7 | import jakarta.persistence.Entity; 8 | import jakarta.persistence.Id; 9 | import jakarta.persistence.Table; 10 | 11 | import io.leasingninja.sales.domain.Amount; 12 | import io.leasingninja.sales.domain.Car; 13 | import io.leasingninja.sales.domain.Contract; 14 | import io.leasingninja.sales.domain.ContractFactory; 15 | import io.leasingninja.sales.domain.ContractNumber; 16 | import io.leasingninja.sales.domain.Currency; 17 | import io.leasingninja.sales.domain.Customer; 18 | import io.leasingninja.sales.domain.Interest; 19 | import io.leasingninja.sales.domain.LeaseTerm; 20 | import io.leasingninja.sales.domain.SignDate; 21 | 22 | @Entity 23 | @Table(name = "contracts") 24 | public class ContractDatabaseEntity { 25 | 26 | @Id 27 | private String number; 28 | 29 | private String lessee; 30 | 31 | private String car; 32 | 33 | @Column(name = "price_amount") 34 | private long priceAmount; 35 | 36 | @Column(name = "price_currency") 37 | private String priceCurrency; 38 | 39 | private int leaseTermInMonths; 40 | 41 | private double interestPerYear; 42 | 43 | private LocalDate signDate; 44 | 45 | private ContractDatabaseEntity() { 46 | } 47 | 48 | public static ContractDatabaseEntity from(Contract contract) { 49 | var dbEntity = new ContractDatabaseEntity(); 50 | dbEntity.number = contract.number().number(); 51 | dbEntity.lessee = contract.lessee().customer(); 52 | dbEntity.car = contract.car().car(); 53 | dbEntity.priceAmount = contract.price().amountInCents(); 54 | dbEntity.priceCurrency = contract.price().currency().name(); 55 | if(contract.isCalculated()) { 56 | dbEntity.leaseTermInMonths = contract.leaseTerm().noOfMonths(); 57 | dbEntity.interestPerYear = contract.interest().perYear(); 58 | } 59 | if(contract.isSigned()) { 60 | dbEntity.signDate = contract.signDate().date(); 61 | } 62 | return dbEntity; 63 | } 64 | 65 | public Contract toContract() { 66 | 67 | return ContractFactory.restoreContract( 68 | // TODO: check that priceCurrency is a real Currency 69 | ContractNumber.of(number), 70 | Customer.of(lessee), 71 | Car.of(car), 72 | Amount.of(priceAmount, Currency.valueOf(priceCurrency)), 73 | leaseTermInMonths != 0 74 | ? Optional.of(LeaseTerm.ofMonths(leaseTermInMonths)) 75 | : Optional.empty(), 76 | interestPerYear != 0 77 | ? Optional.of(Interest.of(interestPerYear)) 78 | : Optional.empty(), 79 | signDate != null 80 | ? Optional.of(SignDate.of(signDate)) 81 | : Optional.empty()); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/infrastructure/ContractDatabaseEntityRepository.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.infrastructure; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | @Repository 7 | public interface ContractDatabaseEntityRepository extends CrudRepository{ 8 | 9 | } 10 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/infrastructure/ContractsJpaImpl.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.infrastructure; 2 | 3 | import io.leasingninja.sales.domain.Contract; 4 | import io.leasingninja.sales.domain.ContractNumber; 5 | import io.leasingninja.sales.domain.Contracts; 6 | 7 | public class ContractsJpaImpl implements Contracts { 8 | 9 | private final ContractDatabaseEntityRepository repo; 10 | 11 | public ContractsJpaImpl(ContractDatabaseEntityRepository repo) { 12 | this.repo = repo; 13 | } 14 | 15 | @Override 16 | public void save(Contract contract) { 17 | repo.save(ContractDatabaseEntity.from(contract)); 18 | } 19 | 20 | @Override 21 | public Contract with(ContractNumber number) { 22 | return repo.findById(number.number()).get().toContract(); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/infrastructure/ContractsJsonImpl.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.infrastructure; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import com.google.gson.Gson; 7 | 8 | import io.leasingninja.sales.domain.Contract; 9 | import io.leasingninja.sales.domain.ContractNumber; 10 | import io.leasingninja.sales.domain.Contracts; 11 | 12 | public class ContractsJsonImpl implements Contracts { 13 | 14 | private static Logger logger = LoggerFactory.getLogger(ContractsJsonImpl.class); 15 | 16 | @Override 17 | public void save(Contract contract) { 18 | String serializedContract = new Gson().toJson(contract); 19 | logger.debug("Contract as JSON: " + serializedContract); 20 | } 21 | 22 | // @Override //JACKSON 23 | // public void save(LeasingVertrag leasingVertrag) { 24 | // var mapper = new ObjectMapper(); 25 | // 26 | // try { 27 | // mapper.writeValue(System.out, leasingVertrag); 28 | // } catch (IOException e) { 29 | // // TODO Auto-generated catch block 30 | // e.printStackTrace(); 31 | // } 32 | // } 33 | // 34 | @Override 35 | public Contract with(ContractNumber number) { 36 | // TODO hier weitermachen mit: https://vaughnvernon.co/?p=942 37 | return null; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/infrastructure/ContractsMemImpl.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.infrastructure; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import io.leasingninja.sales.domain.Contract; 7 | import io.leasingninja.sales.domain.ContractNumber; 8 | import io.leasingninja.sales.domain.Contracts; 9 | 10 | public class ContractsMemImpl implements Contracts { 11 | 12 | private final Map repo; 13 | 14 | public ContractsMemImpl() { 15 | repo = new HashMap<>(); 16 | } 17 | 18 | @Override 19 | public void save(Contract contract) { 20 | repo.put(contract.number(), contract); 21 | } 22 | 23 | @Override 24 | public Contract with(ContractNumber number) { 25 | return repo.get(number); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/ui/ContractModel.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.ui; 2 | 3 | public class ContractModel { 4 | public ContractModel() { 5 | } 6 | 7 | public ContractModel(String number, String lessee, String car, double price_amount, 8 | String price_currency) { 9 | this.number = number; 10 | this.lessee = lessee; 11 | this.car = car; 12 | this.price_amount = price_amount; 13 | this.price_currency = price_currency; 14 | } 15 | 16 | public String number = ""; 17 | public String lessee = ""; 18 | public String car = ""; 19 | public double price_amount; 20 | public String price_currency = ""; 21 | } 22 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/ui/ContractModelMapper.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.ui; 2 | 3 | import io.leasingninja.sales.domain.Contract; 4 | 5 | import java.util.Objects; 6 | 7 | class ContractModelMapper { 8 | 9 | public static ContractModel modelFrom(Contract contract) { 10 | Objects.requireNonNull(contract); 11 | 12 | return new ContractModel( 13 | contract.number().number(), 14 | contract.lessee().customer(), 15 | contract.car().car(), 16 | contract.price().amount(), 17 | contract.price().currency().name()); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/ui/MapStructContractModelMapper.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.ui; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.factory.Mappers; 5 | 6 | import io.leasingninja.sales.domain.Car; 7 | import io.leasingninja.sales.domain.Contract; 8 | import io.leasingninja.sales.domain.ContractNumber; 9 | import io.leasingninja.sales.domain.Customer; 10 | 11 | @Mapper(componentModel = "spring") 12 | public interface MapStructContractModelMapper { 13 | 14 | MapStructContractModelMapper INSTANCE = Mappers.getMapper(MapStructContractModelMapper.class); 15 | 16 | ContractModel vertragToVertragModel(Contract contract); 17 | String vertragsnummer2String(ContractNumber number); 18 | // Vertragsnummer string2Vertragsnummer(String nummer); 19 | String fahrzeug2String(Car car); 20 | // Fahrzeug string2Fahrzeug(String fahrzeug); 21 | String map(Customer customer); 22 | // Kundenname map(String name); 23 | } 24 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/io/leasingninja/sales/ui/SalesController.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.ui; 2 | 3 | import java.time.LocalDate; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import org.springframework.stereotype.Controller; 9 | import org.springframework.ui.Model; 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | import org.springframework.web.bind.annotation.PostMapping; 12 | import org.springframework.web.bind.annotation.RequestParam; 13 | 14 | import io.leasingninja.sales.application.FilloutContract; 15 | import io.leasingninja.sales.application.SignContract; 16 | import io.leasingninja.sales.application.ViewContract; 17 | import io.leasingninja.sales.domain.Amount; 18 | import io.leasingninja.sales.domain.Car; 19 | import io.leasingninja.sales.domain.ContractNumber; 20 | import io.leasingninja.sales.domain.Currency; 21 | import io.leasingninja.sales.domain.Customer; 22 | import io.leasingninja.sales.domain.SignDate; 23 | 24 | @Controller 25 | public class SalesController { 26 | 27 | private static Logger logger = LoggerFactory.getLogger(SalesController.class); 28 | 29 | private final FilloutContract filloutContract; 30 | private final ViewContract viewContract; 31 | private final SignContract signContract; 32 | 33 | public SalesController( 34 | FilloutContract filloutContract, 35 | ViewContract viewContract, 36 | SignContract signContract) { 37 | this.filloutContract = filloutContract; 38 | this.viewContract = viewContract; 39 | this.signContract = signContract; 40 | } 41 | 42 | @GetMapping("/sales/view_contract") 43 | public String viewContract( 44 | @RequestParam(name="contractNumber", required = false) String contractNumberString, 45 | Model model) { 46 | // var vertrag = 47 | // vertragsnummer != null 48 | // ? this.vertragService.liesVertrag(vertragsnummer) : null; 49 | // var vertragModel = 50 | // vertrag != null 51 | // ? VertragModelMapper.modelFrom(vertrag) 52 | // : new VertragModel(); 53 | // model.addAttribute("vertrag", vertragModel); 54 | // logger.debug("VertriebController: vertragnummer:" + vertragModel.nummer); 55 | // model.addAttribute("editing_disabled", !vertrag.isUnterschrieben()); 56 | // return "fillout_contract"; 57 | 58 | logger.debug("Trying to show contract " + contractNumberString); 59 | 60 | model.addAttribute("contract", new ContractModel()); 61 | model.addAttribute("editing_disabled", false); 62 | if (contractNumberString != null) { 63 | var contract = this.viewContract.with(ContractNumber.of(contractNumberString)); 64 | var contractModel = ContractModelMapper.modelFrom(contract); 65 | model.addAttribute("contract", contractModel); 66 | logger.trace("Contract number in model: " + contractModel.number); 67 | model.addAttribute("editing_disabled", contract.isSigned()); 68 | logger.trace("editing_disabled: " + contract.isSigned()); 69 | } 70 | return "contractView"; 71 | } 72 | 73 | // @GetMapping("/sales/view_contract") 74 | // public String viewContract( 75 | // @RequestParam(name="vertragsnummer", required = false) String vertragsnummer, 76 | // Model model) { 77 | // VertragModel vertragModel = 78 | // vertragsnummer != null ? 79 | // this.vertragService.liesVertrag(vertragsnummer) : 80 | // new VertragModel(); 81 | // model.addAttribute("vertrag", vertragModel); 82 | // logger.debug("VertriebController: vertragnummer:" + vertragModel.nummer); 83 | // model.addAttribute("editing_disabled", vertragModel.); 84 | // return "view_contract"; 85 | // } 86 | 87 | @PostMapping("/sales/fillout_contract") 88 | public String filloutContract( 89 | @RequestParam(name="contractNumber") String contractNumberString, 90 | @RequestParam(name="lessee") String lesseeString, 91 | @RequestParam(name="car") String carString, 92 | @RequestParam(name="price_amount") int priceAmount, 93 | @RequestParam(name="price_currency") String priceCurrency, 94 | Model model) { 95 | //TODO: Check that priceCurrency is a valid Currency() 96 | this.filloutContract.with( 97 | ContractNumber.of(contractNumberString), 98 | Customer.of(lesseeString), 99 | Car.of(carString), 100 | Amount.of(priceAmount, Currency.valueOf(priceCurrency))); 101 | return "redirect:/sales/view_contract?contractNumber=" + contractNumberString; 102 | } 103 | 104 | @PostMapping("/sales/sign_contract") 105 | public String signContract( 106 | @RequestParam(name="contractNumber") String contractNumberString, 107 | Model model) { 108 | // CheckResult result = ContractNumber.checkValidity(contractNumberString); 109 | // if(!result.valid) { 110 | // return 400 result.errors; 111 | // } 112 | this.signContract.with(ContractNumber.of(contractNumberString), SignDate.of(LocalDate.now().getYear(), LocalDate.now().getMonth().getValue(), LocalDate.now().getDayOfMonth())); 113 | return "redirect:/sales/view_contract?contractNumber=" + contractNumberString; 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | //import io.hschwentner.dddbits.annotation.BoundedContext; 2 | 3 | /** 4 | * @author Henning Schwentner 5 | * 6 | */ 7 | //@BoundedContext TODO: comment in again when DDDBITS support Java 9 8 | module io.leasingninja.sales { 9 | // exports io.leasingninja.sales.infrastructure; // for jpa 10 | // opens io.leasingninja.sales.infrastructure to spring.data.jpa; // for jpa 11 | // opens io.leasingninja.sales.infrastructure to io.leasingninja.webapp; 12 | 13 | // all layers 14 | requires org.slf4j; 15 | requires org.jmolecules.architecture.layered; 16 | 17 | // Domain Layer 18 | requires org.jmolecules.ddd; 19 | requires org.jmolecules.event; 20 | requires io.leasingninja.riskmanagement; 21 | 22 | // Application Layer 23 | requires spring.beans; 24 | // requires spring.core; 25 | requires spring.context; 26 | requires spring.web; 27 | requires spring.webmvc; 28 | // requires spring.boot; 29 | // requires spring.boot.autoconfigure; 30 | 31 | requires org.mapstruct; 32 | opens io.leasingninja.sales.application to org.mapstruct; 33 | requires transitive java.sql; 34 | requires transitive jakarta.annotation; 35 | 36 | // Infrastructure Layer 37 | requires jakarta.persistence; 38 | // requires java.xml.bind; 39 | requires spring.data.commons; 40 | // requires spring.data.jpa; 41 | /* JSON implementation */ 42 | // requires com.fasterxml.jackson.databind; 43 | // requires transitive com.fasterxml.jackson.core; 44 | requires com.google.gson; 45 | } 46 | 47 | 48 | // exports * to de.wps.leasingninja.vertriebtest 49 | 50 | // requires org.junit.jupiter.api; 51 | // requires org.assertj; 52 | -------------------------------------------------------------------------------- /leasingninja-sales/src/main/resources/templates/contractView.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LeasingNinja – Sales 7 | 8 | 9 | 10 | 11 |

Leasing contract

12 |
13 |
14 |
15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 |
23 | 24 | 25 |
26 |
27 | 28 | 29 | 34 |
35 |
36 | 37 | 38 |
39 |
40 |
41 | 42 | 43 | -------------------------------------------------------------------------------- /leasingninja-sales/src/test/java/io/leasingninja/sales/application/FilloutContractTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.application; 2 | 3 | import static org.mockito.ArgumentMatchers.*; 4 | import static org.mockito.BDDMockito.*; 5 | 6 | import org.junit.jupiter.api.Test; 7 | import org.junit.jupiter.api.extension.ExtendWith; 8 | import org.mockito.InjectMocks; 9 | import org.mockito.Mock; 10 | import org.mockito.junit.jupiter.MockitoExtension; 11 | 12 | import io.leasingninja.sales.domain.Amount; 13 | import io.leasingninja.sales.domain.Car; 14 | import io.leasingninja.sales.domain.Contract; 15 | import io.leasingninja.sales.domain.ContractNumber; 16 | import io.leasingninja.sales.domain.Contracts; 17 | import io.leasingninja.sales.domain.Currency; 18 | import io.leasingninja.sales.domain.Customer; 19 | 20 | @ExtendWith(MockitoExtension.class) 21 | class FilloutContractTest { 22 | 23 | @Mock 24 | private Contracts contractsMock; 25 | 26 | @InjectMocks 27 | private FilloutContract filloutContractUnderTest; 28 | 29 | @Test 30 | void givenEmptyContract_WhenFillout_ThenSave() { 31 | // Given 32 | 33 | // When 34 | filloutContractUnderTest.with( 35 | ContractNumber.of("4711"), 36 | Customer.of("Bob Smith"), 37 | Car.of("Mercedes Benz E-Class"), 38 | Amount.of(10_000, Currency.EUR)); 39 | 40 | 41 | // Then 42 | then(contractsMock).should().save(refEq(new Contract( 43 | ContractNumber.of("4711"), 44 | Customer.of("Bob Smith"), 45 | Car.of("Mercedes Benz E-Class"), 46 | Amount.of(10_000, Currency.EUR)))); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /leasingninja-sales/src/test/java/io/leasingninja/sales/application/SignContractTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.application; 2 | 3 | 4 | import static org.mockito.ArgumentMatchers.*; 5 | import static org.mockito.BDDMockito.*; 6 | 7 | import java.util.Optional; 8 | 9 | import org.junit.jupiter.api.Test; 10 | import org.junit.jupiter.api.extension.ExtendWith; 11 | import org.mockito.InjectMocks; 12 | import org.mockito.Mock; 13 | import org.mockito.junit.jupiter.MockitoExtension; 14 | 15 | import io.leasingninja.riskmanagement.application.InboxApplicationService; 16 | import io.leasingninja.sales.application.SignContract; 17 | import io.leasingninja.sales.domain.Amount; 18 | import io.leasingninja.sales.domain.Car; 19 | import io.leasingninja.sales.domain.Contract; 20 | import io.leasingninja.sales.domain.ContractFactory; 21 | import io.leasingninja.sales.domain.ContractNumber; 22 | import io.leasingninja.sales.domain.Contracts; 23 | import io.leasingninja.sales.domain.Currency; 24 | import io.leasingninja.sales.domain.Customer; 25 | import io.leasingninja.sales.domain.Interest; 26 | import io.leasingninja.sales.domain.LeaseTerm; 27 | import io.leasingninja.sales.domain.SignDate; 28 | 29 | @ExtendWith(MockitoExtension.class) 30 | class SignContractTest { 31 | 32 | @Mock 33 | private Contracts contractsMock; 34 | 35 | @Mock 36 | private InboxApplicationService inboxApplicationServiceMock; 37 | 38 | @InjectMocks 39 | private SignContract applicationServiceUnderTest; 40 | 41 | @Test 42 | void givenAContract_WhenSign_ThenSignedContractIsSaved() { 43 | // Given 44 | var contract = new Contract( 45 | ContractNumber.of("4711"), 46 | Customer.of("Bob Smith"), 47 | Car.of("Mercedes Benz E-Class"), 48 | Amount.of(10_000, Currency.EUR)); 49 | contract.calculateInstallmentFor(LeaseTerm.ofMonths(48), Interest.of(3.7)); 50 | given(contractsMock.with(ContractNumber.of("4711"))).willReturn(contract); 51 | 52 | // When 53 | applicationServiceUnderTest.with( 54 | ContractNumber.of("4711"), 55 | // "2018-04-12"); 56 | SignDate.of(2018, 4, 12)); 57 | 58 | // Then 59 | then(contractsMock).should().save(refEq(ContractFactory.restoreContract( 60 | ContractNumber.of("4711"), 61 | Customer.of("Bob Smith"), 62 | Car.of("Mercedes Benz E-Class"), 63 | Amount.of(10_000, Currency.EUR), 64 | Optional.of(LeaseTerm.ofMonths(48)), 65 | Optional.of(Interest.of(3.7)), 66 | Optional.of(SignDate.of(2018, 04, 12))))); 67 | // then(inboxApplicationServiceMock).should().meldeUnterschriebenenVertrag("4711", "2018-04-12"); 68 | then(inboxApplicationServiceMock).should().confirmSignedContract("4711", 2018, 04, 12); 69 | //liesVertrag() liefert unterschriebenen Vertrag? 70 | //Event unterschrieben verschickt 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /leasingninja-sales/src/test/java/io/leasingninja/sales/application/ViewContractTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.application; 2 | 3 | import static org.assertj.core.api.Assertions.*; 4 | import static org.mockito.BDDMockito.*; 5 | 6 | import org.junit.jupiter.api.Test; 7 | import org.junit.jupiter.api.extension.ExtendWith; 8 | import org.mockito.InjectMocks; 9 | import org.mockito.Mock; 10 | import org.mockito.junit.jupiter.MockitoExtension; 11 | 12 | import io.leasingninja.sales.domain.Amount; 13 | import io.leasingninja.sales.domain.Car; 14 | import io.leasingninja.sales.domain.Contract; 15 | import io.leasingninja.sales.domain.ContractNumber; 16 | import io.leasingninja.sales.domain.Contracts; 17 | import io.leasingninja.sales.domain.Currency; 18 | import io.leasingninja.sales.domain.Customer; 19 | 20 | @ExtendWith(MockitoExtension.class) 21 | class ViewContractTest { 22 | 23 | @Mock 24 | private Contracts contractsMock; 25 | 26 | @InjectMocks 27 | private ViewContract viewContractUnderTest; 28 | 29 | @Test 30 | void givenAContract_WhenViewContract_ThenContractIsReturned() { 31 | // Given 32 | given(contractsMock.with(ContractNumber.of("4711"))).willReturn( 33 | new Contract( 34 | ContractNumber.of("4711"), 35 | Customer.of("Bob Smith"), 36 | Car.of("Mercedes Benz E class"), 37 | Amount.of(10_000, Currency.EUR))); 38 | 39 | // When 40 | Contract contract = viewContractUnderTest.with(ContractNumber.of("4711")); 41 | 42 | // Then 43 | //TODO: Fix this! 44 | assertThat(contract).usingRecursiveComparison().isEqualTo( 45 | new Contract( 46 | ContractNumber.of("4711"), 47 | Customer.of("Bob Smith"), 48 | Car.of("Mercedes Benz E class"), 49 | Amount.of(10_000, Currency.EUR))); 50 | } 51 | 52 | // @Test 53 | // void testLiesVertrag() { 54 | // // Given 55 | // var vertrag = new LeasingVertrag( 56 | // Vertragsnummer.of("4711"), 57 | // Kundenname.of("Hans Schmidt"), 58 | // Fahrzeug.of("Mercedes Benz E-Klasse"), 59 | // Betrag.of(10_000, Currency.EUR)); 60 | // given(vertragRepoMock.findById(Vertragsnummer.of("4711"))).willReturn(vertrag); 61 | // 62 | // // When 63 | // VertragModel model = viewContractUnderTest.liesVertrag("4711"); 64 | // 65 | // // Then 66 | // assertThat(model).usingRecursiveComparison().isEqualTo(new VertragModel("4711", "Hans Schmidt", "Mercedes Benz E-Klasse", 10_000, Currency.EUR)); 67 | // } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /leasingninja-sales/src/test/java/io/leasingninja/sales/domain/AmountTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.assertj.core.api.Assertions.assertThat; 6 | 7 | class AmountTest { 8 | 9 | /* 10 | * We don’t need to test equals() here, since we’re using records. 11 | * Please compare the following project to get an idea of what 12 | * is required for versions without record types: 13 | * https://github.com/leasingninja/leasingninja-javabefore14-boundedcontexts-domainmodel 14 | */ 15 | 16 | @Test 17 | void givenTwoAmountsWithRoundingAfterThePoint_whenEquals_thenAreEqual() { 18 | // given 19 | var amount1 = Amount.of(100.45, Currency.EUR); 20 | var amount2 = Amount.of(100.447123, Currency.EUR); 21 | 22 | // when 23 | boolean areEqual = amount1.equals(amount2); 24 | 25 | // then 26 | assertThat(areEqual).isTrue(); 27 | } 28 | 29 | @Test 30 | void givenAnAmountsWithCents_whenToString_thenAfterThePointIsCorrectlyPrinted() { 31 | // given 32 | var amount = Amount.of(100.45, Currency.EUR); 33 | 34 | // when 35 | String amountString = amount.toString(); 36 | 37 | // then 38 | assertThat(amountString).isEqualTo("EUR 100.45"); 39 | } 40 | 41 | @Test 42 | void givenTwoAmountsOfEurosAndCents_whenEquals_thenAreEqual() { 43 | // given 44 | var amount1 = Amount.of(100.45, Currency.EUR); 45 | var amount2 = Amount.ofCents(10045, Currency.EUR); 46 | 47 | // when 48 | boolean areEqual = amount1.equals(amount2); 49 | 50 | // then 51 | assertThat(areEqual).isTrue(); 52 | } 53 | 54 | @Test 55 | void givenTwoAmounts_whenAdd_thenSumIsCorrect() { 56 | // given 57 | var amount1 = Amount.of(100, Currency.EUR); 58 | var amount2 = Amount.of(200, Currency.EUR); 59 | 60 | // when 61 | var sum = amount1.add(amount2); 62 | 63 | // then 64 | assertThat(sum).isEqualTo(Amount.of(300, Currency.EUR)); 65 | } 66 | 67 | @Test 68 | void givenTwoAmounts_whenSubtract_thenDifferenceIsCorrect() { 69 | // given 70 | var amount1 = Amount.of(300, Currency.EUR); 71 | var amount2 = Amount.of(200, Currency.EUR); 72 | 73 | // when 74 | var sum = amount1.subtract(amount2); 75 | 76 | // then 77 | assertThat(sum).isEqualTo(Amount.of(100, Currency.EUR)); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /leasingninja-sales/src/test/java/io/leasingninja/sales/domain/ContractFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import static org.assertj.core.api.Assertions.*; 4 | 5 | import java.util.Optional; 6 | 7 | import org.junit.jupiter.api.Test; 8 | 9 | public class ContractFactoryTest { 10 | 11 | @Test 12 | void given_whenRestore_thenContractContainsRestoredData() { 13 | // given 14 | 15 | // when 16 | Contract contract = ContractFactory.restoreContract( 17 | ContractNumber.of("4711"), 18 | Customer.of("John Buyer"), 19 | Car.of("Mercedes Benz C-Class"), 20 | Amount.of(20_000, Currency.EUR), 21 | Optional.of(LeaseTerm.ofMonths(48)), 22 | Optional.of(Interest.of(3.6)), 23 | Optional.of(SignDate.of(2018, 04, 12))); 24 | 25 | // then 26 | assertThat(contract.number()).isEqualTo(ContractNumber.of("4711")); 27 | assertThat(contract.lessee()).isEqualTo(Customer.of("John Buyer")); 28 | assertThat(contract.car()).isEqualTo(Car.of("Mercedes Benz C-Class")); 29 | assertThat(contract.price()).isEqualTo(Amount.of(20_000, Currency.EUR)); 30 | assertThat(contract.isCalculated()).isTrue(); 31 | assertThat(contract.isSigned()).isTrue(); 32 | // check that event ContractSigned is fired 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /leasingninja-sales/src/test/java/io/leasingninja/sales/domain/ContractTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import static org.assertj.core.api.Assertions.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class ContractTest { 8 | 9 | @Test 10 | void givenAFilledOutContract_whenCalculate_thenInstallmentIsX() { 11 | // given 12 | var contract = new Contract(ContractNumber.of("4711"), 13 | Customer.of("John Buyer"), 14 | Car.of("Volkswagen ID.3"), 15 | Amount.of(40_000, Currency.EUR)); 16 | 17 | // when 18 | contract.calculateInstallmentFor(LeaseTerm.ofMonths(48), Interest.of(3.7)); 19 | 20 | // then 21 | assertThat(contract.isCalculated()).isTrue(); 22 | assertThat(contract.leaseTerm()).isEqualTo(LeaseTerm.ofMonths(48)); 23 | assertThat(contract.interest()).isEqualTo(Interest.of(3.7)); 24 | assertThat(contract.installment()).isEqualTo(Amount.of(897.80, Currency.EUR)); 25 | } 26 | 27 | @Test 28 | void givenAFilledOutContractWith0Interest_whenCalculate_thenInstallmentIsX() { 29 | // given 30 | var contract = new Contract(ContractNumber.of("4711"), 31 | Customer.of("John Buyer"), 32 | Car.of("Volkswagen ID.3"), 33 | Amount.of(40_000, Currency.EUR)); 34 | 35 | // when 36 | contract.calculateInstallmentFor(LeaseTerm.ofMonths(48), Interest.of(0)); 37 | 38 | // then 39 | assertThat(contract.isCalculated()).isTrue(); 40 | assertThat(contract.installment()).isEqualTo(Amount.of(833.33, Currency.EUR)); 41 | } 42 | 43 | @Test 44 | void givenACalculatedContract_whenSign_thenContractIsSigned() { 45 | // given 46 | var contract = new Contract(ContractNumber.of("4711"), 47 | Customer.of("John Buyer"), 48 | Car.of("Mercedes Benz C-Class"), 49 | Amount.of(20_000, Currency.EUR)); 50 | contract.calculateInstallmentFor(LeaseTerm.ofMonths(48), Interest.of(3.7)); 51 | 52 | // when 53 | contract.sign(SignDate.of(2018, 12, 24)); 54 | 55 | // then 56 | assertThat(contract.isSigned()).isTrue(); 57 | assertThat(contract.signDate()).isEqualTo(SignDate.of(2018, 12, 24)); 58 | // check that event ContractSigned is fired 59 | } 60 | 61 | @Test 62 | void givenTwoContractsWithSameIdButDifferentFields_whenEquals_thenShouldReturnTrue() { 63 | // given 64 | var contract1 = new Contract(ContractNumber.of("4711"), 65 | Customer.of("John Buyer"), 66 | Car.of("Mercedes Benz C-Class"), 67 | Amount.of(40_000, Currency.EUR)); 68 | var contract2 = new Contract(ContractNumber.of("4711"), 69 | Customer.of("Bob Myers"), 70 | Car.of("Volkswagen ID.3"), 71 | Amount.of(30_000, Currency.EUR)); 72 | 73 | // when 74 | var equal = contract1.equals(contract2); 75 | 76 | // then 77 | assertThat(equal).isTrue(); 78 | } 79 | 80 | @Test 81 | void givenTwoContractsWithDifferentIdButSameFields_whenEquals_thenShouldReturnFalse() { 82 | // given 83 | var contract1 = new Contract(ContractNumber.of("4711"), 84 | Customer.of("John Buyer"), 85 | Car.of("Mercedes Benz C-Class"), 86 | Amount.of(40_000, Currency.EUR)); 87 | var contract2 = new Contract(ContractNumber.of("4712"), 88 | Customer.of("John Buyer"), 89 | Car.of("Mercedes Benz C-Class"), 90 | Amount.of(40_000, Currency.EUR)); 91 | 92 | // when 93 | var equal = contract1.equals(contract2); 94 | 95 | // then 96 | assertThat(equal).isFalse(); 97 | } 98 | 99 | @Test 100 | void givenTwoContractsWithSameId_whenHashcode_thenShouldBeEqual() { 101 | // given 102 | var contract1 = new Contract(ContractNumber.of("4711"), 103 | Customer.of("John Buyer"), 104 | Car.of("Mercedes Benz C-Class"), 105 | Amount.of(40_000, Currency.EUR)); 106 | var contract2 = new Contract(ContractNumber.of("4711"), 107 | Customer.of("Bob Myers"), 108 | Car.of("Volkswagen ID.3"), 109 | Amount.of(30_000, Currency.EUR)); 110 | 111 | // when 112 | var hashcode1 = contract1.hashCode(); 113 | var hashcode2 = contract2.hashCode(); 114 | 115 | // then 116 | assertThat(hashcode1).isEqualTo(hashcode2); 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /leasingninja-sales/src/test/java/io/leasingninja/sales/domain/CurrencyTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.assertj.core.api.Assertions.assertThat; 6 | 7 | public class CurrencyTest { 8 | 9 | @Test 10 | void givenTwoUnequalCurrencies_whenEquals_thenAreNotEqual() { 11 | // given 12 | var currency1 = Currency.EUR; 13 | var currency2 = Currency.USD; 14 | 15 | // when 16 | boolean areEqual = currency1.equals(currency2); 17 | 18 | // then 19 | assertThat(areEqual).isFalse(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /leasingninja-sales/src/test/java/io/leasingninja/sales/domain/CustomerTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.assertj.core.api.Assertions.assertThat; 6 | 7 | class CustomerTest { 8 | 9 | @Test 10 | void givenAStringWithOnlyLetters_whenIsValid_thenTrue() { 11 | // given 12 | var nameString = "John"; 13 | 14 | // when 15 | boolean isValid = Customer.isValid(nameString); 16 | 17 | // then 18 | assertThat(isValid).isTrue(); 19 | } 20 | 21 | @Test 22 | void givenAStringWithNonStandardLatinCharacter_whenIsValid_thenTrue() { 23 | // given 24 | var nameString = "Björn"; 25 | 26 | // when 27 | boolean isValid = Customer.isValid(nameString); 28 | 29 | // then 30 | assertThat(isValid).isTrue(); 31 | } 32 | 33 | @Test 34 | void givenAStringWithOnlyLettersAndSpace_whenIsValid_thenTrue() { 35 | // given 36 | var nameString = "John Buyer"; 37 | 38 | // when 39 | boolean isValid = Customer.isValid(nameString); 40 | 41 | // then 42 | assertThat(isValid).isTrue(); 43 | } 44 | 45 | @Test 46 | void givenAStringWithNonStandardLatinCharacterAndSpace_whenIsValid_thenTrue() { 47 | // given 48 | var nameString = "John le Carré"; 49 | 50 | // when 51 | boolean isValid = Customer.isValid(nameString); 52 | 53 | // then 54 | assertThat(isValid).isTrue(); 55 | } 56 | 57 | @Test 58 | void givenAStringWithNumbers_whenIsValid_thenFalse() { 59 | // given 60 | var nameString = "John42"; 61 | 62 | // when 63 | boolean isValid = Customer.isValid(nameString); 64 | 65 | // then 66 | assertThat(isValid).isFalse(); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /leasingninja-sales/src/test/java/io/leasingninja/sales/domain/FinancialCalculatorTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import static org.assertj.core.api.Assertions.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | public class FinancialCalculatorTest { 8 | @Test 9 | void pmt() { 10 | // given 11 | 12 | // when 13 | double pmt = FinancialCalculator.pmt(48, 3.7 / 12, -40_000, 0, 0); 14 | 15 | // then 16 | assertThat(pmt).isEqualTo(897.8022814470006); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /leasingninja-sales/src/test/java/io/leasingninja/sales/domain/InterestTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.assertj.core.api.Assertions.assertThat; 6 | 7 | public class InterestTest { 8 | 9 | @Test 10 | void givenAnInterest_whenPerMonth_thenCorrectValue() { 11 | // given 12 | var interest = Interest.of(3.6); 13 | 14 | // when 15 | double perMonth = interest.perMonth(); 16 | 17 | // then 18 | assertThat(perMonth).isEqualTo(0.3); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /leasingninja-sales/src/test/java/io/leasingninja/sales/domain/LeaseTermTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.assertj.core.api.Assertions.assertThat; 6 | 7 | class LeaseTermTest { 8 | @Test 9 | void given_whenALeaseTermIsCreatedOfYears_thenNoOfMonthsIsCorrect() { 10 | // given 11 | 12 | // when 13 | var leaseTerm = LeaseTerm.ofYears(4); 14 | 15 | // then 16 | assertThat(leaseTerm.noOfMonths()).isEqualTo(48); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /leasingninja-sales/src/test/java/io/leasingninja/sales/domain/SignDateTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.domain; 2 | 3 | import static org.assertj.core.api.Assertions.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import io.leasingninja.sales.domain.SignDate; 8 | 9 | class SignDateTest { 10 | 11 | @Test 12 | void test() { 13 | // when 14 | SignDate signDate1 = SignDate.of(2018, 8, 4); 15 | SignDate signDate2 = SignDate.of(2018, 8, 4); 16 | 17 | // then 18 | assertThat(signDate1).isEqualTo(signDate2); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /leasingninja-sales/src/test/java/io/leasingninja/sales/infrastructure/ContractsMemImplTest.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.sales.infrastructure; 2 | 3 | import static org.assertj.core.api.Assertions.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import io.leasingninja.sales.domain.Amount; 8 | import io.leasingninja.sales.domain.Car; 9 | import io.leasingninja.sales.domain.Contract; 10 | import io.leasingninja.sales.domain.ContractNumber; 11 | import io.leasingninja.sales.domain.Currency; 12 | import io.leasingninja.sales.domain.Customer; 13 | 14 | class ContractsMemImplTest { 15 | 16 | private ContractsMemImpl repoUnderTest; 17 | 18 | @Test 19 | void test() { 20 | // given 21 | repoUnderTest = new ContractsMemImpl(); 22 | 23 | // when 24 | repoUnderTest.save(new Contract( 25 | ContractNumber.of("4711"), 26 | Customer.of("John Buyer"), 27 | Car.of("Mercedes Benz C class"), 28 | Amount.of(20_000, Currency.EUR))); 29 | var contract = repoUnderTest.with(ContractNumber.of("4711")); 30 | 31 | // then 32 | assertThat(contract).usingRecursiveComparison().isEqualTo( 33 | new Contract( 34 | ContractNumber.of("4711"), 35 | Customer.of("John Buyer"), 36 | Car.of("Mercedes Benz C class"), 37 | Amount.of(20_000, Currency.EUR))); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /leasingninja-webapp/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | 7 | io.leasingninja 8 | leasingninja 9 | 0.0.1-SNAPSHOT 10 | 12 | 13 | 14 | leasingninja-webapp 15 | LeasingNinja web app 16 | Web version of LeasingNinja 17 | 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-dependencies 24 | ${spring.boot.version} 25 | pom 26 | import 27 | 28 | 29 | 30 | 31 | 32 | 38 | 39 | io.leasingninja 40 | leasingninja-sales 41 | 0.0.1-SNAPSHOT 42 | 43 | 44 | io.leasingninja 45 | leasingninja-riskmanagement 46 | 0.0.1-SNAPSHOT 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-maven-plugin 56 | ${spring.boot.version} 57 | 62 | 63 | 64 | 65 | repackage 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /leasingninja-webapp/src/main/java/io/leasingninja/webapp/LeasingNinja.java: -------------------------------------------------------------------------------- 1 | package io.leasingninja.webapp; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.autoconfigure.domain.EntityScan; 6 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 7 | 8 | //import io.leasingninja.vertrieb.infrastructure.VertragDatabaseEntity; 9 | 10 | @SpringBootApplication(scanBasePackages="io.leasingninja.*") 11 | //@EntityScan("io.leasingninja.*.domain") 12 | @EntityScan("io.leasingninja.*.infrastructure") 13 | //@EntityScan(basePackageClasses=VertragDatabaseEntity.class) 14 | @EnableJpaRepositories("io.leasingninja.*.infrastructure") 15 | public class LeasingNinja { 16 | 17 | public static void main(String[] args) { 18 | SpringApplication.run(LeasingNinja.class, args); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /leasingninja-webapp/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | /** 5 | * @author henning 6 | * 7 | */ 8 | open module io.leasingninja.webapp { 9 | exports io.leasingninja.webapp; 10 | 11 | // requires io.hschwentner.dddbits; 12 | requires spring.boot; 13 | requires spring.boot.autoconfigure; 14 | requires spring.context; 15 | requires spring.data.commons; 16 | requires spring.data.jpa; 17 | } -------------------------------------------------------------------------------- /leasingninja-webapp/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # H2 2 | spring.h2.console.enabled=true 3 | #spring.h2.console.path=/h2-console 4 | 5 | # Datasource 6 | spring.jpa.generate-ddl=true 7 | spring.jpa.hibernate.ddl-auto=create 8 | spring.datasource.url=jdbc:h2:file:~/test 9 | spring.datasource.username=sa 10 | spring.datasource.password= 11 | spring.datasource.driver-class-name=org.h2.Driver -------------------------------------------------------------------------------- /leasingninja-webapp/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | 2 | _ _ _ _ _ _ 3 | | | (_) | \ | | (_) (_) 4 | | | ___ __ _ ___ _ _ __ __ _ | \| | _ _ __ _ __ _ 5 | | | / _ \ / _` | / __| | | | '_ \ / _` | | . ` | | | | '_ \ | | / _` | 6 | | |____ | __/ | (_| | \__ \ | | | | | | | (_| | | |\ | | | | | | | | | | (_| | 7 | |______| \___| \__,_| |___/ |_| |_| |_| \__, | |_| \_| |_| |_| |_| | | \__,_| 8 | __/ | _/ | 9 | |___/ |__/ 10 | ${application.title} ${application.version} 11 | Running on Spring Boot ${spring-boot.version} 12 | -------------------------------------------------------------------------------- /leasingninja-webapp/src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | LeasingNinja 6 | 7 | 8 |

Welcome to LeasingNinja

9 |

Fill out a new contract here

10 |

Vote an existing contract here

11 | 12 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Apache Maven Wrapper startup batch script, version 3.1.1 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | # e.g. to debug Maven itself, use 32 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | # ---------------------------------------------------------------------------- 35 | 36 | if [ -z "$MAVEN_SKIP_RC" ] ; then 37 | 38 | if [ -f /usr/local/etc/mavenrc ] ; then 39 | . /usr/local/etc/mavenrc 40 | fi 41 | 42 | if [ -f /etc/mavenrc ] ; then 43 | . /etc/mavenrc 44 | fi 45 | 46 | if [ -f "$HOME/.mavenrc" ] ; then 47 | . "$HOME/.mavenrc" 48 | fi 49 | 50 | fi 51 | 52 | # OS specific support. $var _must_ be set to either true or false. 53 | cygwin=false; 54 | darwin=false; 55 | mingw=false 56 | case "`uname`" in 57 | CYGWIN*) cygwin=true ;; 58 | MINGW*) mingw=true;; 59 | Darwin*) darwin=true 60 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 61 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 62 | if [ -z "$JAVA_HOME" ]; then 63 | if [ -x "/usr/libexec/java_home" ]; then 64 | JAVA_HOME="`/usr/libexec/java_home`"; export JAVA_HOME 65 | else 66 | JAVA_HOME="/Library/Java/Home"; export JAVA_HOME 67 | fi 68 | fi 69 | ;; 70 | esac 71 | 72 | if [ -z "$JAVA_HOME" ] ; then 73 | if [ -r /etc/gentoo-release ] ; then 74 | JAVA_HOME=`java-config --jre-home` 75 | fi 76 | fi 77 | 78 | # For Cygwin, ensure paths are in UNIX format before anything is touched 79 | if $cygwin ; then 80 | [ -n "$JAVA_HOME" ] && 81 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 82 | [ -n "$CLASSPATH" ] && 83 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 84 | fi 85 | 86 | # For Mingw, ensure paths are in UNIX format before anything is touched 87 | if $mingw ; then 88 | [ -n "$JAVA_HOME" ] && 89 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 90 | fi 91 | 92 | if [ -z "$JAVA_HOME" ]; then 93 | javaExecutable="`which javac`" 94 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 95 | # readlink(1) is not available as standard on Solaris 10. 96 | readLink=`which readlink` 97 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 98 | if $darwin ; then 99 | javaHome="`dirname \"$javaExecutable\"`" 100 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 101 | else 102 | javaExecutable="`readlink -f \"$javaExecutable\"`" 103 | fi 104 | javaHome="`dirname \"$javaExecutable\"`" 105 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 106 | JAVA_HOME="$javaHome" 107 | export JAVA_HOME 108 | fi 109 | fi 110 | fi 111 | 112 | if [ -z "$JAVACMD" ] ; then 113 | if [ -n "$JAVA_HOME" ] ; then 114 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 115 | # IBM's JDK on AIX uses strange locations for the executables 116 | JAVACMD="$JAVA_HOME/jre/sh/java" 117 | else 118 | JAVACMD="$JAVA_HOME/bin/java" 119 | fi 120 | else 121 | JAVACMD="`\\unset -f command; \\command -v java`" 122 | fi 123 | fi 124 | 125 | if [ ! -x "$JAVACMD" ] ; then 126 | echo "Error: JAVA_HOME is not defined correctly." >&2 127 | echo " We cannot execute $JAVACMD" >&2 128 | exit 1 129 | fi 130 | 131 | if [ -z "$JAVA_HOME" ] ; then 132 | echo "Warning: JAVA_HOME environment variable is not set." 133 | fi 134 | 135 | # traverses directory structure from process work directory to filesystem root 136 | # first directory with .mvn subdirectory is considered project base directory 137 | find_maven_basedir() { 138 | if [ -z "$1" ] 139 | then 140 | echo "Path not specified to find_maven_basedir" 141 | return 1 142 | fi 143 | 144 | basedir="$1" 145 | wdir="$1" 146 | while [ "$wdir" != '/' ] ; do 147 | if [ -d "$wdir"/.mvn ] ; then 148 | basedir=$wdir 149 | break 150 | fi 151 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 152 | if [ -d "${wdir}" ]; then 153 | wdir=`cd "$wdir/.."; pwd` 154 | fi 155 | # end of workaround 156 | done 157 | printf '%s' "$(cd "$basedir"; pwd)" 158 | } 159 | 160 | # concatenates all lines of a file 161 | concat_lines() { 162 | if [ -f "$1" ]; then 163 | echo "$(tr -s '\n' ' ' < "$1")" 164 | fi 165 | } 166 | 167 | BASE_DIR=$(find_maven_basedir "$(dirname $0)") 168 | if [ -z "$BASE_DIR" ]; then 169 | exit 1; 170 | fi 171 | 172 | MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR 173 | if [ "$MVNW_VERBOSE" = true ]; then 174 | echo $MAVEN_PROJECTBASEDIR 175 | fi 176 | 177 | ########################################################################################## 178 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 179 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 180 | ########################################################################################## 181 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 182 | if [ "$MVNW_VERBOSE" = true ]; then 183 | echo "Found .mvn/wrapper/maven-wrapper.jar" 184 | fi 185 | else 186 | if [ "$MVNW_VERBOSE" = true ]; then 187 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 188 | fi 189 | if [ -n "$MVNW_REPOURL" ]; then 190 | wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" 191 | else 192 | wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" 193 | fi 194 | while IFS="=" read key value; do 195 | case "$key" in (wrapperUrl) wrapperUrl="$value"; break ;; 196 | esac 197 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 198 | if [ "$MVNW_VERBOSE" = true ]; then 199 | echo "Downloading from: $wrapperUrl" 200 | fi 201 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 202 | if $cygwin; then 203 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 204 | fi 205 | 206 | if command -v wget > /dev/null; then 207 | QUIET="--quiet" 208 | if [ "$MVNW_VERBOSE" = true ]; then 209 | echo "Found wget ... using wget" 210 | QUIET="" 211 | fi 212 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 213 | wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" 214 | else 215 | wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" 216 | fi 217 | [ $? -eq 0 ] || rm -f "$wrapperJarPath" 218 | elif command -v curl > /dev/null; then 219 | QUIET="--silent" 220 | if [ "$MVNW_VERBOSE" = true ]; then 221 | echo "Found curl ... using curl" 222 | QUIET="" 223 | fi 224 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 225 | curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L 226 | else 227 | curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L 228 | fi 229 | [ $? -eq 0 ] || rm -f "$wrapperJarPath" 230 | else 231 | if [ "$MVNW_VERBOSE" = true ]; then 232 | echo "Falling back to using Java to download" 233 | fi 234 | javaSource="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 235 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" 236 | # For Cygwin, switch paths to Windows format before running javac 237 | if $cygwin; then 238 | javaSource=`cygpath --path --windows "$javaSource"` 239 | javaClass=`cygpath --path --windows "$javaClass"` 240 | fi 241 | if [ -e "$javaSource" ]; then 242 | if [ ! -e "$javaClass" ]; then 243 | if [ "$MVNW_VERBOSE" = true ]; then 244 | echo " - Compiling MavenWrapperDownloader.java ..." 245 | fi 246 | # Compiling the Java class 247 | ("$JAVA_HOME/bin/javac" "$javaSource") 248 | fi 249 | if [ -e "$javaClass" ]; then 250 | # Running the downloader 251 | if [ "$MVNW_VERBOSE" = true ]; then 252 | echo " - Running MavenWrapperDownloader.java ..." 253 | fi 254 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 255 | fi 256 | fi 257 | fi 258 | fi 259 | ########################################################################################## 260 | # End of extension 261 | ########################################################################################## 262 | 263 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 264 | 265 | # For Cygwin, switch paths to Windows format before running java 266 | if $cygwin; then 267 | [ -n "$JAVA_HOME" ] && 268 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 269 | [ -n "$CLASSPATH" ] && 270 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 271 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 272 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 273 | fi 274 | 275 | # Provide a "standardized" way to retrieve the CLI args that will 276 | # work with both Windows and non-Windows executions. 277 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 278 | export MAVEN_CMD_LINE_ARGS 279 | 280 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 281 | 282 | exec "$JAVACMD" \ 283 | $MAVEN_OPTS \ 284 | $MAVEN_DEBUG_OPTS \ 285 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 286 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 287 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 288 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Apache Maven Wrapper startup batch script, version 3.1.1 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 28 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 29 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 30 | @REM e.g. to debug Maven itself, use 31 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 32 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 33 | @REM ---------------------------------------------------------------------------- 34 | 35 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 36 | @echo off 37 | @REM set title of command window 38 | title %0 39 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 40 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 41 | 42 | @REM set %HOME% to equivalent of $HOME 43 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 44 | 45 | @REM Execute a user defined script before this one 46 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 47 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 48 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 49 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 50 | :skipRcPre 51 | 52 | @setlocal 53 | 54 | set ERROR_CODE=0 55 | 56 | @REM To isolate internal variables from possible post scripts, we use another setlocal 57 | @setlocal 58 | 59 | @REM ==== START VALIDATION ==== 60 | if not "%JAVA_HOME%" == "" goto OkJHome 61 | 62 | echo. 63 | echo Error: JAVA_HOME not found in your environment. >&2 64 | echo Please set the JAVA_HOME variable in your environment to match the >&2 65 | echo location of your Java installation. >&2 66 | echo. 67 | goto error 68 | 69 | :OkJHome 70 | if exist "%JAVA_HOME%\bin\java.exe" goto init 71 | 72 | echo. 73 | echo Error: JAVA_HOME is set to an invalid directory. >&2 74 | echo JAVA_HOME = "%JAVA_HOME%" >&2 75 | echo Please set the JAVA_HOME variable in your environment to match the >&2 76 | echo location of your Java installation. >&2 77 | echo. 78 | goto error 79 | 80 | @REM ==== END VALIDATION ==== 81 | 82 | :init 83 | 84 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 85 | @REM Fallback to current working directory if not found. 86 | 87 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 88 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 89 | 90 | set EXEC_DIR=%CD% 91 | set WDIR=%EXEC_DIR% 92 | :findBaseDir 93 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 94 | cd .. 95 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 96 | set WDIR=%CD% 97 | goto findBaseDir 98 | 99 | :baseDirFound 100 | set MAVEN_PROJECTBASEDIR=%WDIR% 101 | cd "%EXEC_DIR%" 102 | goto endDetectBaseDir 103 | 104 | :baseDirNotFound 105 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 106 | cd "%EXEC_DIR%" 107 | 108 | :endDetectBaseDir 109 | 110 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 111 | 112 | @setlocal EnableExtensions EnableDelayedExpansion 113 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 114 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 115 | 116 | :endReadAdditionalConfig 117 | 118 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" 123 | 124 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 125 | IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | if "%MVNW_VERBOSE%" == "true" ( 132 | echo Found %WRAPPER_JAR% 133 | ) 134 | ) else ( 135 | if not "%MVNW_REPOURL%" == "" ( 136 | SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" 137 | ) 138 | if "%MVNW_VERBOSE%" == "true" ( 139 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 140 | echo Downloading from: %WRAPPER_URL% 141 | ) 142 | 143 | powershell -Command "&{"^ 144 | "$webclient = new-object System.Net.WebClient;"^ 145 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 146 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 147 | "}"^ 148 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ 149 | "}" 150 | if "%MVNW_VERBOSE%" == "true" ( 151 | echo Finished downloading %WRAPPER_JAR% 152 | ) 153 | ) 154 | @REM End of extension 155 | 156 | @REM Provide a "standardized" way to retrieve the CLI args that will 157 | @REM work with both Windows and non-Windows executions. 158 | set MAVEN_CMD_LINE_ARGS=%* 159 | 160 | %MAVEN_JAVA_EXE% ^ 161 | %JVM_CONFIG_MAVEN_PROPS% ^ 162 | %MAVEN_OPTS% ^ 163 | %MAVEN_DEBUG_OPTS% ^ 164 | -classpath %WRAPPER_JAR% ^ 165 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 166 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 167 | if ERRORLEVEL 1 goto error 168 | goto end 169 | 170 | :error 171 | set ERROR_CODE=1 172 | 173 | :end 174 | @endlocal & set ERROR_CODE=%ERROR_CODE% 175 | 176 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 177 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 178 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 179 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 180 | :skipRcPost 181 | 182 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 183 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 184 | 185 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 186 | 187 | cmd /C exit /B %ERROR_CODE% 188 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.leasingninja 8 | leasingninja 9 | 0.0.1-SNAPSHOT 10 | pom 11 | 12 | LeasingNinja 13 | The DDD demonstration app 14 | 15 | 16 | 17 | 18 | UTF-8 19 | 24 20 | 3.14.0 21 | 3.5.3 22 | 3.5.3 23 | 24 | 25 | 1.10.0 26 | 1.6.0 27 | 0.0.1 28 | 3.5.0 29 | 1.6.3 30 | 2.13.1 31 | 32 | 33 | 5.12.2 34 | 1.10.3 35 | 3.27.3 36 | 5.18.0 37 | 1.4.0 38 | 39 | 40 | 41 | 42 | leasingninja-sales 43 | leasingninja-riskmanagement 44 | leasingninja-webapp 45 | 46 | 47 | 48 | 49 | 50 | org.junit 51 | junit-bom 52 | ${junit.jupiter.version} 53 | pom 54 | import 55 | 56 | 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-dependencies 61 | ${spring.boot.version} 62 | pom 63 | import 64 | 65 | 66 | 67 | 68 | 69 | 70 | org.jmolecules 71 | jmolecules-layered-architecture 72 | ${jmolecules.version} 73 | 74 | 75 | org.jmolecules 76 | jmolecules-ddd 77 | ${jmolecules.version} 78 | 79 | 80 | org.jmolecules 81 | jmolecules-events 82 | ${jmolecules.version} 83 | 84 | 85 | org.jmolecules.integrations 86 | jmolecules-archunit 87 | ${jmolecules-integrations.version} 88 | test 89 | 90 | 91 | io.hschwentner.dddbits 92 | dddbits 93 | ${dddbits.version} 94 | 95 | 96 | 97 | org.springframework.boot 98 | spring-boot-starter-web 99 | 100 | 101 | org.springframework.boot 102 | spring-boot-starter-thymeleaf 103 | 104 | 105 | org.springframework.boot 106 | spring-boot-starter-data-jpa 107 | 108 | 109 | com.h2database 110 | h2 111 | runtime 112 | 113 | 114 | 115 | org.springframework.boot 116 | spring-boot-devtools 117 | true 118 | 119 | 120 | 121 | org.mapstruct 122 | mapstruct 123 | ${mapstruct.version} 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | com.google.code.gson 134 | gson 135 | ${gson.version} 136 | compile 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 158 | 159 | 160 | org.assertj 161 | assertj-core 162 | ${assertj.version} 163 | test 164 | 165 | 166 | 167 | org.mockito 168 | mockito-core 169 | ${mockito.version} 170 | test 171 | 172 | 173 | 174 | org.mockito 175 | mockito-junit-jupiter 176 | ${mockito.version} 177 | test 178 | 179 | 180 | 181 | com.tngtech.archunit 182 | archunit-junit5 183 | ${archunit.version} 184 | test 185 | 186 | 187 | 188 | 189 | 190 | 191 | org.springframework.boot 192 | spring-boot-maven-plugin 193 | ${spring.boot.version} 194 | 195 | 196 | org.apache.maven.plugins 197 | maven-compiler-plugin 198 | ${maven.compiler.version} 199 | 200 | ${java.version} 201 | 202 | 203 | org.mapstruct 204 | mapstruct-processor 205 | ${mapstruct.version} 206 | 207 | 208 | 209 | 210 | 211 | org.apache.maven.plugins 212 | maven-surefire-plugin 213 | ${surefire.version} 214 | 223 | 224 | 233 | 234 | 235 | org.apache.maven.plugins 236 | maven-failsafe-plugin 237 | ${failsafe.version} 238 | 239 | 240 | 241 | 242 | 243 | --------------------------------------------------------------------------------