├── .gitignore ├── LICENSE ├── README.md ├── chapter-11 ├── .gitignore ├── build.gradle.kts ├── gradle │ ├── libs.versions.toml │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src │ ├── main │ ├── kotlin │ │ ├── EventEnvelope.kt │ │ └── org │ │ │ └── example │ │ │ └── household │ │ │ ├── HouseholdRepository.kt │ │ │ └── telemetry │ │ │ └── Tracing.kt │ └── resources │ │ ├── logback.xml │ │ └── sample.json │ └── test │ └── kotlin │ └── Logging.kt ├── chapter-12 ├── .gitignore ├── build.gradle.kts ├── docs │ └── demo-720.mp4 ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src │ └── main │ ├── kotlin │ └── com │ │ └── example │ │ └── demo │ │ ├── AsSequence.kt │ │ ├── Coroutine.kt │ │ ├── HighThroughput.kt │ │ ├── Measurement.kt │ │ └── MicrobenchmarkingTest.kt │ └── resources │ └── application.properties ├── chapter-13 ├── .gitignore ├── build.gradle.kts ├── docs │ └── demo-720.mp4 ├── gradle.properties ├── gradle │ ├── libs.versions.toml │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src │ ├── main │ ├── kotlin │ │ └── example │ │ │ ├── Exercise.kt │ │ │ ├── FindBiggestNumber.kt │ │ │ └── server │ │ │ └── Application.kt │ └── resources │ │ └── openapi.yaml │ └── test │ └── kotlin │ └── example │ ├── ExerciseExecutorIntegrationTest.kt │ ├── ExerciseExecutorTest.kt │ ├── FindBiggestNumberKtTest.kt │ ├── bdd │ └── household.feature │ └── tdd │ ├── round1_step1 │ └── test-scenarios.txt │ ├── round1_step2 │ └── HouseholdServiceTest.kt │ ├── round1_step3a │ └── HouseholdServiceTest.kt │ ├── round1_step3b │ └── HouseholdServiceTest.kt │ ├── round2_step2 │ └── HouseholdServiceTest.kt │ ├── round2_step3 │ └── HouseholdServiceTest.kt │ └── round2_step4 │ └── HouseholdServiceTest.kt ├── chapter-14 ├── .gitignore ├── build.gradle.kts ├── docs │ └── demo-720.mp4 ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src │ └── main │ └── kotlin │ ├── override │ └── UserAccount.kt │ └── redacted │ ├── Main.kt │ ├── Redacted.kt │ └── SensitiveData.kt ├── chapter-15 ├── .gitignore ├── build.gradle.kts ├── docs │ └── demo-720.mp4 ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src │ └── main │ ├── java │ ├── Account.kt │ └── Household.java │ └── kotlin │ ├── extension │ ├── Name.kt │ ├── dto │ │ ├── NameDto.kt │ │ └── subpack │ │ │ └── Subpack.kt │ ├── member │ │ └── Name.kt │ ├── nonlocal │ │ └── toJson.kt │ ├── repository │ │ └── NameSql.kt │ └── vanilla │ │ └── ToJson.kt │ ├── infix │ └── Bdd.kt │ └── scoping │ ├── Main.kt │ └── custom │ └── Validation.kt ├── chapter-2 ├── .gradle │ ├── 8.4 │ │ ├── checksums │ │ │ ├── checksums.lock │ │ │ ├── md5-checksums.bin │ │ │ └── sha1-checksums.bin │ │ ├── dependencies-accessors │ │ │ ├── dependencies-accessors.lock │ │ │ └── gc.properties │ │ ├── executionHistory │ │ │ ├── executionHistory.bin │ │ │ └── executionHistory.lock │ │ ├── fileChanges │ │ │ └── last-build.bin │ │ ├── fileHashes │ │ │ ├── fileHashes.bin │ │ │ ├── fileHashes.lock │ │ │ └── resourceHashesCache.bin │ │ └── gc.properties │ ├── buildOutputCleanup │ │ ├── buildOutputCleanup.lock │ │ ├── cache.properties │ │ └── outputFiles.bin │ ├── file-system.probe │ └── vcs-1 │ │ └── gc.properties ├── .idea │ ├── .gitignore │ ├── .name │ ├── aws.xml │ ├── codeStyles │ │ ├── Project.xml │ │ └── codeStyleConfig.xml │ ├── compiler.xml │ ├── gradle.xml │ ├── jarRepositories.xml │ ├── ktlint.xml │ ├── misc.xml │ └── vcs.xml ├── README.md ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── main │ └── kotlin │ │ └── packt │ │ └── architecturekotlin │ │ ├── chapter2 │ │ ├── HelloWorld.kt │ │ ├── example1 │ │ │ └── ExchangeService.kt │ │ ├── example2 │ │ │ └── Enums.kt │ │ ├── example3 │ │ │ ├── dip │ │ │ │ └── ContractWorkflowService.kt │ │ │ ├── isp │ │ │ │ └── Human.kt │ │ │ ├── lsp │ │ │ │ └── PhoneNotificationService.kt │ │ │ ├── ocp │ │ │ │ └── NotificationService.kt │ │ │ └── srp │ │ │ │ └── WithoutSrp.kt │ │ ├── example4 │ │ │ └── demeter.kt │ │ ├── formats │ │ │ └── JacksonMessage.kt │ │ └── routes │ │ │ └── ExampleContractRoute.kt │ │ └── chapter3 │ │ └── polymorphic │ │ └── Service.kt │ └── test │ └── kotlin │ └── packt │ └── architecturekotlin │ └── chapter2 │ ├── HelloWorldClient.kt │ └── HelloWorldTest.kt ├── chapter-3 ├── .idea │ ├── .gitignore │ ├── aws.xml │ ├── compiler.xml │ ├── gradle.xml │ ├── inspectionProfiles │ │ └── Project_Default.xml │ ├── jarRepositories.xml │ ├── kotlinc.xml │ ├── ktlint-plugin.xml │ ├── misc.xml │ └── vcs.xml ├── build.gradle.kts ├── docs │ └── demo-720.mp4 ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src │ └── main │ └── kotlin │ ├── delegation │ └── Household.kt │ ├── functional │ └── Household.kt │ ├── polymorphic │ └── Service.kt │ ├── sealedclass1 │ └── Service.kt │ └── sealedclass2 │ └── Service.kt ├── chapter-4 ├── ClientServer │ ├── README.md │ ├── build.gradle │ ├── docs │ │ └── demo-720.mp4 │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ ├── main │ │ ├── kotlin │ │ │ └── clientserver │ │ │ │ ├── ClientServer.kt │ │ │ │ └── formats │ │ │ │ └── JacksonMessage.kt │ │ └── resources │ │ │ └── openapi.yaml │ │ └── test │ │ └── kotlin │ │ └── clientserver │ │ └── ClientServerClient.kt └── P2P │ ├── build.gradle.kts │ ├── docs │ └── demo-720.mp4 │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ └── src │ └── main │ └── kotlin │ └── p2p │ ├── Dto.kt │ ├── DtoConvertor.kt │ ├── HouseholdA.kt │ ├── HouseholdB.kt │ ├── Loop.kt │ └── UdpNode.kt ├── chapter-5 ├── base │ ├── .gitignore │ ├── app │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── androidTest │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── base │ │ │ │ └── ExampleInstrumentedTest.kt │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── base │ │ │ │ │ ├── controller │ │ │ │ │ └── MainActivity.kt │ │ │ │ │ ├── model │ │ │ │ │ ├── ContractRepository.kt │ │ │ │ │ └── DraftContractInput.kt │ │ │ │ │ └── view │ │ │ │ │ ├── ConfirmationFragment.kt │ │ │ │ │ └── ContractDraftFragment.kt │ │ │ └── res │ │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ │ ├── drawable │ │ │ │ └── ic_launcher_background.xml │ │ │ │ ├── layout │ │ │ │ ├── activity_main.xml │ │ │ │ ├── fragment_confirmation.xml │ │ │ │ └── fragment_contract_draft.xml │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── mipmap-anydpi-v33 │ │ │ │ └── ic_launcher.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── values-night │ │ │ │ └── themes.xml │ │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── strings.xml │ │ │ │ └── themes.xml │ │ │ │ └── xml │ │ │ │ ├── backup_rules.xml │ │ │ │ └── data_extraction_rules.xml │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── base │ │ │ └── ExampleUnitTest.kt │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── mvc │ ├── .gitignore │ ├── app │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── androidTest │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── base │ │ │ │ └── ExampleInstrumentedTest.kt │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── base │ │ │ │ │ ├── controller │ │ │ │ │ └── MainActivity.kt │ │ │ │ │ ├── model │ │ │ │ │ ├── ContractRepository.kt │ │ │ │ │ └── DraftContractInput.kt │ │ │ │ │ └── view │ │ │ │ │ ├── ConfirmationFragment.kt │ │ │ │ │ └── ContractDraftFragment.kt │ │ │ └── res │ │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ │ ├── drawable │ │ │ │ └── ic_launcher_background.xml │ │ │ │ ├── layout │ │ │ │ ├── activity_main.xml │ │ │ │ ├── fragment_confirmation.xml │ │ │ │ └── fragment_contract_draft.xml │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── mipmap-anydpi-v33 │ │ │ │ └── ic_launcher.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── values-night │ │ │ │ └── themes.xml │ │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── strings.xml │ │ │ │ └── themes.xml │ │ │ │ └── xml │ │ │ │ ├── backup_rules.xml │ │ │ │ └── data_extraction_rules.xml │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── base │ │ │ └── ExampleUnitTest.kt │ ├── build.gradle │ ├── docs │ │ └── demo-720.mp4 │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── mvp │ ├── .gitignore │ ├── app │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── androidTest │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── base │ │ │ │ └── ExampleInstrumentedTest.kt │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── base │ │ │ │ │ ├── model │ │ │ │ │ ├── ContractRepository.kt │ │ │ │ │ └── DraftContractInput.kt │ │ │ │ │ ├── presenter │ │ │ │ │ ├── MainActivity.kt │ │ │ │ │ └── Presenter.kt │ │ │ │ │ └── view │ │ │ │ │ ├── ConfirmationFragment.kt │ │ │ │ │ └── ContractDraftFragment.kt │ │ │ └── res │ │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ │ ├── drawable │ │ │ │ └── ic_launcher_background.xml │ │ │ │ ├── layout │ │ │ │ ├── activity_main.xml │ │ │ │ ├── fragment_confirmation.xml │ │ │ │ └── fragment_contract_draft.xml │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── mipmap-anydpi-v33 │ │ │ │ └── ic_launcher.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── values-night │ │ │ │ └── themes.xml │ │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── strings.xml │ │ │ │ └── themes.xml │ │ │ │ └── xml │ │ │ │ ├── backup_rules.xml │ │ │ │ └── data_extraction_rules.xml │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── base │ │ │ ├── ExampleUnitTest.kt │ │ │ └── model │ │ │ └── ContractRepositoryTest.kt │ ├── build.gradle │ ├── docs │ │ └── demo-720.mp4 │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle └── mvvm │ ├── .gitignore │ ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── base │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── base │ │ │ │ ├── model │ │ │ │ ├── ContractRepository.kt │ │ │ │ └── DraftContractInput.kt │ │ │ │ ├── view │ │ │ │ ├── ConfirmationFragment.kt │ │ │ │ └── ContractDraftFragment.kt │ │ │ │ └── viewmodel │ │ │ │ ├── Command.kt │ │ │ │ ├── DraftContractViewModel.kt │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── layout │ │ │ ├── activity_main.xml │ │ │ ├── fragment_confirmation.xml │ │ │ └── fragment_contract_draft.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-anydpi-v33 │ │ │ └── ic_launcher.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── values-night │ │ │ └── themes.xml │ │ │ ├── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── themes.xml │ │ │ └── xml │ │ │ ├── backup_rules.xml │ │ │ └── data_extraction_rules.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── base │ │ ├── model │ │ └── ContractRepositoryTest.kt │ │ └── viewmodel │ │ └── DraftContractViewModelTest.kt │ ├── build.gradle │ ├── docs │ └── demo-720.mp4 │ ├── gradle.properties │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── chapter-6 ├── .gitignore ├── build.gradle.kts ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src │ └── main │ ├── kotlin │ └── com │ │ └── example │ │ ├── Application.kt │ │ └── plugins │ │ ├── Entities.kt │ │ └── Functions.kt │ └── resources │ ├── application.yaml │ └── logback.xml ├── chapter-7 ├── clean │ ├── .gitignore │ ├── build.gradle.kts │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ ├── entity │ │ ├── Contract.kt │ │ ├── ContractState.kt │ │ ├── Household.kt │ │ └── Party.kt │ │ └── usecase │ │ └── DraftContract.kt ├── connect │ ├── README.md │ ├── build.gradle.kts │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ └── src │ │ ├── main │ │ └── kotlin │ │ │ └── connect │ │ │ ├── Connect.kt │ │ │ └── Entities.kt │ │ └── test │ │ └── kotlin │ │ └── connect │ │ ├── ConnectTest.kt │ │ ├── HouseholdApiAction.kt │ │ └── http │ │ ├── Actions.kt │ │ ├── Adapters.kt │ │ └── Usage.kt ├── fcis │ ├── .gitignore │ ├── build.gradle.kts │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ └── src │ │ ├── main │ │ ├── kotlin │ │ │ └── fcis │ │ │ │ ├── core │ │ │ │ ├── Common.kt │ │ │ │ ├── Data.kt │ │ │ │ └── Functions.kt │ │ │ │ ├── fcis │ │ │ │ └── FcisApplication.kt │ │ │ │ └── shell │ │ │ │ └── rest │ │ │ │ ├── ContractControllerShell.kt │ │ │ │ ├── Dtos.kt │ │ │ │ ├── Functions.kt │ │ │ │ ├── Mappings.kt │ │ │ │ └── Validations.kt │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── kotlin │ │ └── fcis │ │ └── fcis │ │ ├── FcisApplicationTests.kt │ │ └── LayeredArchitectureTest.kt └── hexagonal │ ├── .gitignore │ ├── build.gradle.kts │ ├── gradle.properties │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ └── src │ └── main │ └── kotlin │ ├── adapter │ ├── ContractServiceImpl.kt │ └── rest │ │ ├── ContractController.kt │ │ └── DraftContractRequest.kt │ └── core │ ├── Contract.kt │ ├── ContractState.kt │ ├── Household.kt │ ├── Party.kt │ └── port │ ├── ContractRepository.kt │ ├── ContractService.kt │ └── HouseholdRepository.kt ├── chapter-8 ├── .gitignore ├── build.gradle.kts ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src │ └── main │ └── kotlin │ ├── Elements.kt │ └── Household.kt ├── chapter-9 ├── .gitattributes ├── .gitignore ├── app │ ├── build.gradle.kts │ └── src │ │ ├── main │ │ └── kotlin │ │ │ ├── Aggregate.kt │ │ │ ├── Commands.kt │ │ │ ├── EventSourcing.kt │ │ │ ├── EventStore.kt │ │ │ ├── Events.kt │ │ │ ├── Queries.kt │ │ │ └── ReadModels.kt │ │ └── test │ │ └── kotlin │ │ ├── Cqrs-usage.kt │ │ └── Event-source-usage.kt ├── docs │ └── demo-720.mp4 ├── gradle │ ├── libs.versions.toml │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts └── chatper-10 ├── .gitignore ├── build.gradle.kts ├── docs └── demo-720.mp4 ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src └── main └── kotlin ├── Household.kt ├── HouseholdRepository.kt └── Usage.kt /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Packt 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 | # Software-Architecture-with-Kotlin 2 | Software Architecture with Kotlin, published by Packt 3 | 4 | ## Prerequisite 5 | 6 | These software need to be installed: 7 | 1. JDK 20+ 8 | 2. Gradle 8.x 9 | 3. Git Command Line Tool 2.x 10 | 4. IntelliJ IDEA Community or Ultimate Editon 11 | 5. Kotest plugin for IntelliJ 12 | 6. Android Studio 13 | 14 | ## Additional set up in IntelliJ 15 | 1. Set Gradle home 16 | 2. Set Java SDK home 17 | 3. Some projects might need to set SDK explicitly 18 | 19 | ## Demo videos 20 | There are demo videos of the program execution in each /docs folders. 21 | -------------------------------------------------------------------------------- /chapter-11/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /chapter-11/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | alias(libs.plugins.jvm) 4 | 5 | } 6 | 7 | group = "org.example" 8 | version = "1.0-SNAPSHOT" 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | implementation("io.github.oshai:kotlin-logging-jvm:7.0.0") 16 | implementation("org.slf4j:slf4j-api:2.0.16") 17 | implementation("ch.qos.logback:logback-classic:1.5.7") 18 | implementation("net.logstash.logback:logstash-logback-encoder:8.0") 19 | implementation("io.opentelemetry:opentelemetry-api:1.43.0") 20 | implementation("io.opentelemetry:opentelemetry-sdk:1.43.0") 21 | implementation("io.opentelemetry:opentelemetry-exporter-otlp:1.43.0") 22 | implementation("io.opentelemetry:opentelemetry-extension-annotations:1.18.0") 23 | testImplementation(platform("org.junit:junit-bom:5.10.0")) 24 | testImplementation("org.junit.jupiter:junit-jupiter") 25 | } 26 | 27 | 28 | tasks.test { 29 | useJUnitPlatform() 30 | } -------------------------------------------------------------------------------- /chapter-11/gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | # This file was generated by the Gradle 'init' task. 2 | # https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format 3 | 4 | [versions] 5 | 6 | [libraries] 7 | 8 | [plugins] 9 | jvm = { id = "org.jetbrains.kotlin.jvm", version = "2.0.0" } 10 | -------------------------------------------------------------------------------- /chapter-11/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-11/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-11/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Aug 11 19:55:32 BST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /chapter-11/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "chapter-11" 2 | 3 | -------------------------------------------------------------------------------- /chapter-11/src/main/kotlin/EventEnvelope.kt: -------------------------------------------------------------------------------- 1 | import java.time.Instant 2 | import java.util.UUID 3 | 4 | data class EventEnvelope( 5 | val id: UUID, 6 | val sessionId: UUID? = null, 7 | val correlationId: UUID? = null, 8 | val happenedAt: Instant, 9 | val action: String, 10 | val outcome: String, 11 | val actor: Actor, 12 | val otherActors: Set? = null, 13 | val resource: Resource, 14 | val otherResources: Set? = null, 15 | val content: E, 16 | val diffs: List? = null, 17 | ) 18 | 19 | data class Actor( 20 | val id: UUID, 21 | val type: String, 22 | val involvement: String, 23 | ) 24 | 25 | data class Resource ( 26 | val id: UUID, 27 | val type: String, 28 | val applicationId: String, 29 | val version: Int? = null 30 | ) 31 | 32 | data class Difference( 33 | val op: String, 34 | val path: String, 35 | val fromValue: Any? = null, 36 | val toValue: Any? = null 37 | ) 38 | -------------------------------------------------------------------------------- /chapter-11/src/main/kotlin/org/example/household/HouseholdRepository.kt: -------------------------------------------------------------------------------- 1 | package org.example.household 2 | 3 | import io.github.oshai.kotlinlogging.KotlinLogging.logger 4 | import io.github.oshai.kotlinlogging.withLoggingContext 5 | import java.util.UUID 6 | 7 | class HouseholdRepository { 8 | private val log = logger {} 9 | 10 | fun create() { 11 | val householdName = "Whittington" 12 | val sessionId = UUID.randomUUID().toString() 13 | withLoggingContext("session" to sessionId) { 14 | log.atInfo { 15 | message = "Created a new household '$householdName'" 16 | payload = mapOf( 17 | "householdName" to householdName 18 | ) 19 | } 20 | } 21 | } 22 | } 23 | 24 | fun main() { 25 | HouseholdRepository().create() 26 | } -------------------------------------------------------------------------------- /chapter-11/src/main/kotlin/org/example/household/telemetry/Tracing.kt: -------------------------------------------------------------------------------- 1 | package org.example.household.telemetry 2 | 3 | import io.opentelemetry.api.GlobalOpenTelemetry 4 | import io.opentelemetry.api.trace.Span 5 | import io.opentelemetry.api.trace.Tracer 6 | import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter 7 | import io.opentelemetry.sdk.OpenTelemetrySdk 8 | import io.opentelemetry.sdk.trace.SdkTracerProvider 9 | import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor 10 | 11 | val tracer: Tracer = run { 12 | val oltpEndpont = "http://localhost:8123" 13 | val otlpExporter = OtlpGrpcSpanExporter.builder() 14 | .setEndpoint(oltpEndpont) 15 | .build() 16 | val spanProcessor = SimpleSpanProcessor.create(otlpExporter) 17 | val tracerProvider = SdkTracerProvider.builder() 18 | .addSpanProcessor(spanProcessor) 19 | .build() 20 | OpenTelemetrySdk.builder() 21 | .setTracerProvider(tracerProvider) 22 | .buildAndRegisterGlobal() 23 | GlobalOpenTelemetry.getTracer("example-tracer") 24 | } 25 | 26 | fun main() { 27 | val span: Span = tracer 28 | .spanBuilder("process data") 29 | .startSpan() 30 | .apply { setAttribute("data.source", "memory") } 31 | try { 32 | println("process finished") 33 | } catch (e: Exception) { 34 | span.recordException(e) 35 | } finally { 36 | span.end() 37 | } 38 | } -------------------------------------------------------------------------------- /chapter-11/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} MDC=%X{session} - %msg%n 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /chapter-11/src/main/resources/sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "@timestamp": "2024-08-20T12:16:20.898527+01:00", 3 | "@version": "1", 4 | "message": "Created a new household 'Whittington'", 5 | "logger_name": "org.example.household.HouseholdRepository", 6 | "thread_name": "main", 7 | "level": "INFO", 8 | "level_value": 20000, 9 | "session": "57fa4035-0390-406c-9f2b-7dfcfc131d5a", 10 | "householdName": "Whittington" 11 | } -------------------------------------------------------------------------------- /chapter-11/src/test/kotlin/Logging.kt: -------------------------------------------------------------------------------- 1 | import io.github.oshai.kotlinlogging.KotlinLogging.logger 2 | 3 | class Logging { 4 | private val log = logger {} 5 | 6 | fun test() { 7 | log.atInfo { 8 | message = "" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /chapter-12/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | 39 | ### Kotlin ### 40 | .kotlin 41 | -------------------------------------------------------------------------------- /chapter-12/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") version "1.9.25" 3 | id("org.jetbrains.kotlinx.benchmark") version "0.4.11" 4 | kotlin("plugin.allopen") version "2.0.20" 5 | } 6 | 7 | group = "com.example" 8 | version = "0.0.1-SNAPSHOT" 9 | 10 | java { 11 | toolchain { 12 | languageVersion = JavaLanguageVersion.of(21) 13 | } 14 | } 15 | 16 | repositories { 17 | mavenCentral() 18 | gradlePluginPortal() 19 | } 20 | 21 | dependencies { 22 | implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.11") 23 | implementation("org.jetbrains.kotlin:kotlin-reflect") 24 | testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") 25 | testRuntimeOnly("org.junit.platform:junit-platform-launcher") 26 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0") 27 | } 28 | 29 | kotlin { 30 | compilerOptions { 31 | freeCompilerArgs.addAll("-Xjsr305=strict") 32 | } 33 | } 34 | 35 | allOpen { 36 | annotation("org.openjdk.jmh.annotations.State") 37 | } 38 | 39 | benchmark { 40 | targets { 41 | register("main") 42 | } 43 | configurations { 44 | named("main") { 45 | } 46 | } 47 | } 48 | tasks.withType { 49 | useJUnitPlatform() 50 | } 51 | -------------------------------------------------------------------------------- /chapter-12/docs/demo-720.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-12/docs/demo-720.mp4 -------------------------------------------------------------------------------- /chapter-12/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-12/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-12/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /chapter-12/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "demo" 2 | -------------------------------------------------------------------------------- /chapter-12/src/main/kotlin/com/example/demo/AsSequence.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | fun someExpensiveOp(n: Int): Int = n 4 | 5 | fun processAsList() { 6 | val result = 7 | listOf(1, 7, 3, 23, 63) 8 | .filter { 9 | println("filter:$it") 10 | it > 3 11 | }.map { 12 | println("expensive:$it") 13 | someExpensiveOp(it) 14 | }.take(2) 15 | println(result) 16 | } 17 | 18 | fun processAsSequence() { 19 | val result = 20 | listOf(1, 7, 3, 23, 63) 21 | .asSequence() 22 | .filter { 23 | println("filter:$it") 24 | it > 3 25 | }.map { 26 | println("expensive:$it") 27 | someExpensiveOp(it) 28 | }.take(2) 29 | println(result.toList()) 30 | } 31 | 32 | fun main() { 33 | println("-----as list-----") 34 | processAsList() 35 | println("----------") 36 | 37 | println("-----as sequence-----") 38 | processAsSequence() 39 | println("----------") 40 | } 41 | -------------------------------------------------------------------------------- /chapter-12/src/main/kotlin/com/example/demo/Coroutine.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | import kotlinx.coroutines.async 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.runBlocking 6 | 7 | fun main() = 8 | runBlocking { 9 | val result1 = async { task1() } 10 | val result2 = async { task2() } 11 | val combinedResult = result1.await() + result2.await() 12 | println("Combined Result: $combinedResult") 13 | } 14 | 15 | suspend fun task1(): Int { 16 | delay(1000) 17 | println("Task 1 completed") 18 | return 42 19 | } 20 | 21 | suspend fun task2(): Int { 22 | delay(1500) // Simulate a 1.5-second delay 23 | println("Task 2 completed") 24 | return 58 25 | } 26 | -------------------------------------------------------------------------------- /chapter-12/src/main/kotlin/com/example/demo/HighThroughput.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | import java.util.concurrent.ConcurrentLinkedQueue 4 | import kotlin.random.Random 5 | 6 | fun main() { 7 | val queue = RequestQueue() 8 | 9 | repeat(10000) { 10 | queue.put(Random.nextInt()) 11 | } 12 | 13 | var count = 0 14 | while (queue.size() > 0) { 15 | println("expensive remove: ${++count}") 16 | 17 | queue.expensiveRemove()?.also { 18 | someProcessing(it) 19 | } 20 | } 21 | } 22 | 23 | inline fun someProcessing(value: Int): Int = value 24 | 25 | class RequestQueue { 26 | private val requests = ConcurrentLinkedQueue() 27 | 28 | fun put(request: T) { 29 | requests.add(request) 30 | } 31 | 32 | fun expensiveRemove(): T? { 33 | Thread.sleep(2000) 34 | return requests.remove() 35 | } 36 | 37 | fun size(): Int = requests.size 38 | } 39 | -------------------------------------------------------------------------------- /chapter-12/src/main/kotlin/com/example/demo/Measurement.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | import kotlin.system.measureTimeMillis 4 | import kotlin.time.measureTimedValue 5 | 6 | fun main() { 7 | val iterations = 1_000 8 | val operationTime = measureTotalTimeElapsed(iterations) { sampleOperation() } 9 | println("Total time elapsed: ${operationTime / 1000.0} second") 10 | println("Throughput: ${iterations / (operationTime / 1000.0)} operations per second") 11 | println("Latency (average): ${operationTime / iterations} ms") 12 | } 13 | 14 | fun sampleOperation() { 15 | Thread.sleep(1) 16 | measureTimedValue { } 17 | } 18 | 19 | fun measureTotalTimeElapsed( 20 | iterations: Int, 21 | operation: (Int) -> Unit, 22 | ): Long = 23 | measureTimeMillis { 24 | repeat(iterations, operation) 25 | } 26 | 27 | inline fun measureTime(block: () -> T): T { 28 | val start = System.nanoTime() 29 | val result = block() 30 | val timeTaken = System.nanoTime() - start 31 | return result.also { println("taken: $timeTaken") } 32 | } 33 | -------------------------------------------------------------------------------- /chapter-12/src/main/kotlin/com/example/demo/MicrobenchmarkingTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | import org.openjdk.jmh.annotations.Benchmark 4 | import org.openjdk.jmh.annotations.Fork 5 | import org.openjdk.jmh.annotations.Measurement 6 | import org.openjdk.jmh.annotations.Scope 7 | import org.openjdk.jmh.annotations.Setup 8 | import org.openjdk.jmh.annotations.State 9 | import org.openjdk.jmh.annotations.Warmup 10 | import java.util.UUID 11 | import java.util.concurrent.TimeUnit 12 | 13 | @State(Scope.Benchmark) 14 | @Fork(1) 15 | @Warmup(iterations = 10) 16 | @Measurement(iterations = 20, time = 1, timeUnit = TimeUnit.MILLISECONDS) 17 | class MicrobenchmarkingTest { 18 | private var data = emptyList() 19 | 20 | @Setup 21 | fun setUp() { 22 | data = (1..2).map { UUID.randomUUID() } 23 | } 24 | 25 | @Benchmark 26 | fun combineUUIDBenchmark(): UUID = data.reduce { one, two -> one + two } 27 | 28 | private operator fun UUID.plus(another: UUID): UUID { 29 | val mostSignificant = mostSignificantBits xor another.mostSignificantBits 30 | val leastSignficant = leastSignificantBits xor another.leastSignificantBits 31 | return UUID(mostSignificant, leastSignficant) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chapter-12/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=demo 2 | -------------------------------------------------------------------------------- /chapter-13/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /chapter-13/docs/demo-720.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-13/docs/demo-720.mp4 -------------------------------------------------------------------------------- /chapter-13/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | -------------------------------------------------------------------------------- /chapter-13/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-13/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-13/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Sep 29 07:58:45 BST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /chapter-13/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0" 3 | } 4 | rootProject.name = "chapter-13" 5 | 6 | -------------------------------------------------------------------------------- /chapter-13/src/main/kotlin/example/FindBiggestNumber.kt: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | fun findBiggestNumber(numbers: List): Int? = numbers.maxOrNull() 4 | -------------------------------------------------------------------------------- /chapter-13/src/main/kotlin/example/server/Application.kt: -------------------------------------------------------------------------------- 1 | package example.server 2 | 3 | import io.ktor.server.application.call 4 | import io.ktor.server.engine.embeddedServer 5 | import io.ktor.server.netty.Netty 6 | import io.ktor.server.response.respondText 7 | import io.ktor.server.routing.get 8 | import io.ktor.server.routing.routing 9 | 10 | fun main() { 11 | embeddedServer(Netty, port = 8080) { 12 | routing { 13 | get("/") { 14 | call.respondText("Hello, world!") 15 | } 16 | } 17 | }.start(wait = true) 18 | } 19 | -------------------------------------------------------------------------------- /chapter-13/src/test/kotlin/example/ExerciseExecutorIntegrationTest.kt: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import example.Weather.CLOUDY 4 | import example.Weather.RAINY 5 | import io.kotest.core.spec.style.StringSpec 6 | import io.kotest.matchers.shouldBe 7 | import java.io.File 8 | import java.io.FileReader 9 | import java.time.Instant 10 | import java.time.ZoneId 11 | import java.time.temporal.ChronoUnit.HOURS 12 | 13 | class ExerciseExecutorIntegrationTest : 14 | StringSpec({ 15 | "Gym when cloudy and run in the park when rainy as recorded in file log" { 16 | val file = 17 | File 18 | .createTempFile("Exer", "cise") 19 | .apply { deleteOnExit() } 20 | val exec = ExerciseExecutor(ExerciseFileLog(file)) 21 | val now = Instant.now() 22 | val fourHoursLater = now.plus(4, HOURS) 23 | val utc = ZoneId.of("UTC") 24 | 25 | exec.doExercise(RAINY, now) 26 | exec.doExercise(CLOUDY, fourHoursLater) 27 | 28 | FileReader(file).readLines() shouldBe 29 | listOf( 30 | "${now.atZone(utc).toLocalDateTime()}: GoToGym", 31 | "${fourHoursLater.atZone(utc).toLocalDateTime()}: RunInThePark", 32 | ) 33 | } 34 | }) 35 | -------------------------------------------------------------------------------- /chapter-13/src/test/kotlin/example/ExerciseExecutorTest.kt: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import io.kotest.core.spec.style.BehaviorSpec 4 | import io.kotest.matchers.shouldBe 5 | import io.mockk.every 6 | import io.mockk.mockk 7 | import io.mockk.verify 8 | import java.time.Instant 9 | 10 | class ExerciseExecutorTest : 11 | BehaviorSpec({ 12 | Given("Today is sunny") { 13 | val exerciseLog = mockk() 14 | val executor = ExerciseExecutor(exerciseLog) 15 | every { exerciseLog.record(any(), any()) } returns Unit 16 | val weather = Weather.SUNNY 17 | When("doing an exercise") { 18 | val now = Instant.now() 19 | Then("running in the park") { 20 | executor.doExercise(weather, now) shouldBe Exercise.RunInThePark 21 | } 22 | And("the exercise is logged") { 23 | verify { exerciseLog.record(now, Exercise.RunInThePark) } 24 | } 25 | } 26 | } 27 | }) 28 | -------------------------------------------------------------------------------- /chapter-13/src/test/kotlin/example/FindBiggestNumberKtTest.kt: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import io.kotest.core.spec.style.FunSpec 4 | import io.kotest.datatest.withData 5 | import io.kotest.matchers.shouldBe 6 | 7 | class FindBiggestNumberKtTest : 8 | FunSpec({ 9 | test("Find the biggest out of positive numbers") { 10 | findBiggestNumber(listOf(17, 18, 6)) shouldBe 18 11 | } 12 | }) 13 | 14 | class FindBiggestNumberParameterizedTest : 15 | FunSpec({ 16 | context("Find the biggest out of positive numbers") { 17 | withData( 18 | emptyList() to null, 19 | listOf(8) to 8, 20 | listOf(99, 8) to 99, 21 | listOf(17, 18, 6) to 18, 22 | listOf(944, 0, 633) to 944, 23 | listOf(0, -32, 76) to 76, 24 | listOf(-11, -32, -102) to -11, 25 | listOf(-25, -57, 0) to 0, 26 | listOf( 27 | Integer.MAX_VALUE + 1, 28 | Integer.MAX_VALUE, 29 | 0, 30 | Int.MIN_VALUE, 31 | -Int.MIN_VALUE - 1, 32 | -Int.MAX_VALUE, 33 | Int.MIN_VALUE - 1, 34 | ) to Integer.MAX_VALUE, 35 | ) { (allNumbers, expectedMax) -> 36 | findBiggestNumber(allNumbers) shouldBe expectedMax 37 | } 38 | } 39 | }) 40 | -------------------------------------------------------------------------------- /chapter-13/src/test/kotlin/example/bdd/household.feature: -------------------------------------------------------------------------------- 1 | Feature: Household creation 2 | 3 | Scenario: Creation of households with non-empty surnames 4 | 5 | Given the household surname is non-empty 6 | 7 | When the user requests to create the household 8 | 9 | Then the household is created -------------------------------------------------------------------------------- /chapter-13/src/test/kotlin/example/tdd/round1_step1/test-scenarios.txt: -------------------------------------------------------------------------------- 1 | fail to create household of empty surname 2 | successfully create a household 3 | fail to create household if surname already exists -------------------------------------------------------------------------------- /chapter-13/src/test/kotlin/example/tdd/round1_step2/HouseholdServiceTest.kt: -------------------------------------------------------------------------------- 1 | package example.tdd.round1_step2 2 | 3 | /* 4 | import io.kotest.core.spec.style.StringSpec 5 | import io.kotest.matchers.shouldBe 6 | 7 | class HouseholdServiceTest : StringSpec({ 8 | "fail to create household of empty surname" { 9 | val service = HouseholdService() 10 | service.createHousehold(Household(surname = "")) shouldBe Failure( 11 | "Surname must be non-empty" 12 | ) 13 | } 14 | }) 15 | */ 16 | -------------------------------------------------------------------------------- /chapter-13/src/test/kotlin/example/tdd/round1_step3a/HouseholdServiceTest.kt: -------------------------------------------------------------------------------- 1 | package example.tdd.round1_step3a 2 | 3 | import io.kotest.core.spec.style.StringSpec 4 | import io.kotest.matchers.shouldBe 5 | 6 | class HouseholdServiceTest : StringSpec({ 7 | "fail to create household of empty surname" { 8 | val service = HouseholdService() 9 | service.createHousehold(Household(surname = "")) shouldBe Failure( 10 | "Surname must be non-empty" 11 | ) 12 | } 13 | }) 14 | 15 | class Failure(reason: String) { 16 | 17 | } 18 | 19 | class Household(surname: String) { 20 | 21 | } 22 | 23 | class HouseholdService { 24 | fun createHousehold(household: Household): Failure { 25 | TODO("Not yet implemented") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter-13/src/test/kotlin/example/tdd/round1_step3b/HouseholdServiceTest.kt: -------------------------------------------------------------------------------- 1 | package example.tdd.round1_step3b 2 | 3 | import io.kotest.core.spec.style.StringSpec 4 | import io.kotest.matchers.shouldBe 5 | 6 | class HouseholdServiceTest : StringSpec({ 7 | "fail to create household of empty surname" { 8 | val service = HouseholdService() 9 | service.createHousehold(Household(surname = "")) shouldBe Failure( 10 | "Surname must be non-empty" 11 | ) 12 | } 13 | }) 14 | 15 | data class Failure(val reason: String) 16 | 17 | class Household(surname: String) { } 18 | 19 | class HouseholdService { 20 | fun createHousehold(household: Household): Failure = Failure("Surname must be non-empty") 21 | } 22 | -------------------------------------------------------------------------------- /chapter-13/src/test/kotlin/example/tdd/round2_step2/HouseholdServiceTest.kt: -------------------------------------------------------------------------------- 1 | package example.tdd.round2_step2 2 | 3 | import io.kotest.core.spec.style.StringSpec 4 | import io.kotest.matchers.shouldBe 5 | import io.kotest.property.Arb 6 | import io.kotest.property.arbitrary.next 7 | import io.kotest.property.arbitrary.string 8 | 9 | class HouseholdServiceTest : StringSpec({ 10 | "fail to create household of empty surname" { 11 | val service = HouseholdService() 12 | service.createHousehold(Household(surname = "")) shouldBe Failure( 13 | "Surname must be non-empty" 14 | ) 15 | } 16 | "successfully create a household" { 17 | val service = HouseholdService() 18 | val household = Household(surname = Arb.string(minSize = 3).next()) 19 | service.createHousehold(household) shouldBe Success(household) 20 | } 21 | }) 22 | 23 | data class Success(val household: Household) 24 | 25 | data class Failure(val reason: String) 26 | 27 | class Household(surname: String) { } 28 | 29 | class HouseholdService { 30 | fun createHousehold(household: Household): Failure = Failure("Surname must be non-empty") 31 | } 32 | -------------------------------------------------------------------------------- /chapter-14/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Kotlin ### 20 | .kotlin 21 | 22 | ### Eclipse ### 23 | .apt_generated 24 | .classpath 25 | .factorypath 26 | .project 27 | .settings 28 | .springBeans 29 | .sts4-cache 30 | bin/ 31 | !**/src/main/**/bin/ 32 | !**/src/test/**/bin/ 33 | 34 | ### NetBeans ### 35 | /nbproject/private/ 36 | /nbbuild/ 37 | /dist/ 38 | /nbdist/ 39 | /.nb-gradle/ 40 | 41 | ### VS Code ### 42 | .vscode/ 43 | 44 | ### Mac OS ### 45 | .DS_Store -------------------------------------------------------------------------------- /chapter-14/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") version "2.0.20" 3 | id("dev.zacsweers.redacted") version "1.10.0" 4 | application 5 | } 6 | group = "com" 7 | version = "1.0-SNAPSHOT" 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | 13 | dependencies { 14 | testImplementation(kotlin("test")) 15 | } 16 | 17 | tasks.test { 18 | useJUnitPlatform() 19 | } 20 | kotlin { 21 | jvmToolchain(21) 22 | } 23 | 24 | redacted { 25 | redactedAnnotation = "redacted/Redacted" 26 | replacementString = "*" 27 | } 28 | 29 | application { 30 | mainClass = "redacted.MainKt" 31 | } -------------------------------------------------------------------------------- /chapter-14/docs/demo-720.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-14/docs/demo-720.mp4 -------------------------------------------------------------------------------- /chapter-14/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | -------------------------------------------------------------------------------- /chapter-14/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-14/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-14/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Oct 09 15:48:44 BST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /chapter-14/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 3 | } 4 | rootProject.name = "chapter-14" 5 | 6 | -------------------------------------------------------------------------------- /chapter-14/src/main/kotlin/override/UserAccount.kt: -------------------------------------------------------------------------------- 1 | package override 2 | 3 | import java.time.Instant 4 | 5 | data class UserAccount( 6 | val username: String, 7 | val password: String, 8 | val createdAt: Instant 9 | ) { 10 | override fun toString(): String { 11 | return "UserAccount(createdAt=$createdAt)" 12 | } 13 | } -------------------------------------------------------------------------------- /chapter-14/src/main/kotlin/redacted/Main.kt: -------------------------------------------------------------------------------- 1 | package redacted 2 | 3 | import java.time.LocalDate 4 | 5 | fun main() { 6 | println("${BankAccount("Iban", "bic", "holderName")}") 7 | println("${UserAccount("username", "password", LocalDate.now())}") 8 | println("Secret wrapper: ${Secret("email@address.com")}") 9 | } -------------------------------------------------------------------------------- /chapter-14/src/main/kotlin/redacted/Redacted.kt: -------------------------------------------------------------------------------- 1 | package redacted 2 | 3 | @Retention(AnnotationRetention.SOURCE) 4 | @Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS) 5 | annotation class Redacted 6 | 7 | data class Secret (val value: T) { 8 | override fun toString(): String = "*" 9 | } -------------------------------------------------------------------------------- /chapter-14/src/main/kotlin/redacted/SensitiveData.kt: -------------------------------------------------------------------------------- 1 | package redacted 2 | 3 | import java.time.LocalDate 4 | 5 | @Redacted 6 | data class BankAccount( 7 | val iban: String, 8 | val bic: String, 9 | val holderName: String 10 | ) 11 | 12 | data class UserAccount( 13 | @Redacted val username: String, 14 | @Redacted val password: String, 15 | val createdAt: LocalDate 16 | ) -------------------------------------------------------------------------------- /chapter-15/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Kotlin ### 20 | .kotlin 21 | 22 | ### Eclipse ### 23 | .apt_generated 24 | .classpath 25 | .factorypath 26 | .project 27 | .settings 28 | .springBeans 29 | .sts4-cache 30 | bin/ 31 | !**/src/main/**/bin/ 32 | !**/src/test/**/bin/ 33 | 34 | ### NetBeans ### 35 | /nbproject/private/ 36 | /nbbuild/ 37 | /dist/ 38 | /nbdist/ 39 | /.nb-gradle/ 40 | 41 | ### VS Code ### 42 | .vscode/ 43 | 44 | ### Mac OS ### 45 | .DS_Store -------------------------------------------------------------------------------- /chapter-15/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") version "2.0.20" 3 | } 4 | 5 | group = "com" 6 | version = "1.0-SNAPSHOT" 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | testImplementation(kotlin("test")) 14 | } 15 | 16 | tasks.test { 17 | useJUnitPlatform() 18 | } 19 | kotlin { 20 | jvmToolchain(21) 21 | } -------------------------------------------------------------------------------- /chapter-15/docs/demo-720.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-15/docs/demo-720.mp4 -------------------------------------------------------------------------------- /chapter-15/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | -------------------------------------------------------------------------------- /chapter-15/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-15/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-15/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Oct 14 15:34:38 BST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /chapter-15/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 3 | } 4 | rootProject.name = "chapter-15" 5 | 6 | -------------------------------------------------------------------------------- /chapter-15/src/main/java/Account.kt: -------------------------------------------------------------------------------- 1 | @JvmRecord 2 | data class Account(val number: String, val holderName: String) -------------------------------------------------------------------------------- /chapter-15/src/main/java/Household.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.List; 3 | 4 | public class Household { 5 | private final String name; 6 | private final List members = new ArrayList<>(); 7 | 8 | public Household(String name, List members) { 9 | this.name = name; 10 | this.members.addAll(members); 11 | } 12 | 13 | public String getName() { 14 | return name; 15 | } 16 | 17 | public List getMembers() { 18 | return new ArrayList(members); 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /chapter-15/src/main/kotlin/extension/Name.kt: -------------------------------------------------------------------------------- 1 | package extension 2 | 3 | fun String.getFirstLetters(): String = 4 | split(" ").joinToString(".") { 5 | it.first().toString() 6 | } 7 | 8 | fun List?.concat(): String = this?.joinToString(",")?: "" 9 | 10 | fun main() { 11 | println(Name("Sam") + Name("Jones")) 12 | } 13 | 14 | data class Name(val value: String) 15 | 16 | operator fun Name.plus(other: Name): Name = Name("$value ${other.value}") 17 | 18 | -------------------------------------------------------------------------------- /chapter-15/src/main/kotlin/extension/dto/NameDto.kt: -------------------------------------------------------------------------------- 1 | package extension.dto 2 | 3 | import extension.Name 4 | 5 | internal fun Name.toJson(): String = 6 | "{\"name\":\"$value\"}" 7 | 8 | object NameJsonConverter { 9 | fun toJson(name: Name): String = "{\"name\":\"${name.value}\"}" 10 | } -------------------------------------------------------------------------------- /chapter-15/src/main/kotlin/extension/dto/subpack/Subpack.kt: -------------------------------------------------------------------------------- 1 | package extension.dto.subpack 2 | 3 | import extension.Name 4 | import extension.dto.toJson 5 | 6 | fun foo() { 7 | Name("abc").toJson() 8 | } -------------------------------------------------------------------------------- /chapter-15/src/main/kotlin/extension/member/Name.kt: -------------------------------------------------------------------------------- 1 | package extension.member 2 | 3 | data class Name(val value: String) { 4 | fun toJson(): String = "{\"name\":\"$value\"}" 5 | } -------------------------------------------------------------------------------- /chapter-15/src/main/kotlin/extension/nonlocal/toJson.kt: -------------------------------------------------------------------------------- 1 | package extension.nonlocal 2 | 3 | import extension.Name 4 | 5 | fun Name.toJson(): String = 6 | "{\"name\":\"$value\"}" 7 | -------------------------------------------------------------------------------- /chapter-15/src/main/kotlin/extension/repository/NameSql.kt: -------------------------------------------------------------------------------- 1 | package extension.repository 2 | 3 | import extension.Name 4 | import extension.dto.toJson 5 | 6 | internal fun Name.toInsertSql(): String = 7 | "insert into" 8 | 9 | fun foo2() { 10 | Name("asd").toJson() 11 | } 12 | 13 | -------------------------------------------------------------------------------- /chapter-15/src/main/kotlin/extension/vanilla/ToJson.kt: -------------------------------------------------------------------------------- 1 | package extension.vanilla 2 | 3 | import extension.Name 4 | 5 | internal fun toJson(name: Name): String = "{\"name\":\"${name.value}\"}" 6 | -------------------------------------------------------------------------------- /chapter-15/src/main/kotlin/infix/Bdd.kt: -------------------------------------------------------------------------------- 1 | package infix 2 | 3 | object When 4 | 5 | typealias PreCondition = () -> Int 6 | 7 | typealias Action = (Int) -> Int 8 | 9 | infix fun When.number(n: Int): PreCondition = { n } 10 | 11 | infix fun PreCondition.then(action: Action): Int = action(this()) 12 | 13 | object Square: Action { 14 | override fun invoke(p1: Int): Int = p1 * p1 15 | } 16 | 17 | infix fun Int.shouldBe(expected: Int) { 18 | require(this == expected) { 19 | "Expected: $expected but was $this" 20 | } 21 | } 22 | 23 | fun main() { 24 | ((When.number(2)).then(Square)).shouldBe(5) 25 | When number 2 then Square shouldBe 5 26 | 27 | } -------------------------------------------------------------------------------- /chapter-15/src/main/kotlin/scoping/Main.kt: -------------------------------------------------------------------------------- 1 | package scoping 2 | 3 | fun main() { 4 | println("3".let { it + "5" }) 5 | println("3".run { this + "5" }) 6 | println(with("3") { this + "5" }) 7 | 8 | "3".also { println(it) } 9 | "3".apply { println(this) } 10 | } -------------------------------------------------------------------------------- /chapter-15/src/main/kotlin/scoping/custom/Validation.kt: -------------------------------------------------------------------------------- 1 | package scoping.custom 2 | 3 | fun T.validate( 4 | build: ValidationBuilder.(T) -> Unit 5 | ): List = 6 | ValidationBuilder() 7 | .also { builder -> builder.build(this) } 8 | .getErrors() 9 | 10 | class ValidationBuilder { 11 | private val failures = mutableListOf() 12 | 13 | fun evaluate( 14 | result: Boolean, 15 | failureMessage: () -> String 16 | ) { 17 | if (!result) failures.add(failureMessage()) 18 | } 19 | fun getErrors() = failures.toList() 20 | } 21 | 22 | fun main() { 23 | val failures = "Some very%long nickname".validate { 24 | evaluate(it.length < 20) { "Must be under 20 characters: \"$it\"" } 25 | evaluate(it.contains("%").not()) { "Must not contains % character"} 26 | } 27 | println("failures: $failures") 28 | } -------------------------------------------------------------------------------- /chapter-2/.gradle/8.4/checksums/checksums.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-2/.gradle/8.4/checksums/checksums.lock -------------------------------------------------------------------------------- /chapter-2/.gradle/8.4/checksums/md5-checksums.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-2/.gradle/8.4/checksums/md5-checksums.bin -------------------------------------------------------------------------------- /chapter-2/.gradle/8.4/checksums/sha1-checksums.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-2/.gradle/8.4/checksums/sha1-checksums.bin -------------------------------------------------------------------------------- /chapter-2/.gradle/8.4/dependencies-accessors/dependencies-accessors.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-2/.gradle/8.4/dependencies-accessors/dependencies-accessors.lock -------------------------------------------------------------------------------- /chapter-2/.gradle/8.4/dependencies-accessors/gc.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-2/.gradle/8.4/dependencies-accessors/gc.properties -------------------------------------------------------------------------------- /chapter-2/.gradle/8.4/executionHistory/executionHistory.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-2/.gradle/8.4/executionHistory/executionHistory.bin -------------------------------------------------------------------------------- /chapter-2/.gradle/8.4/executionHistory/executionHistory.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-2/.gradle/8.4/executionHistory/executionHistory.lock -------------------------------------------------------------------------------- /chapter-2/.gradle/8.4/fileChanges/last-build.bin: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /chapter-2/.gradle/8.4/fileHashes/fileHashes.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-2/.gradle/8.4/fileHashes/fileHashes.bin -------------------------------------------------------------------------------- /chapter-2/.gradle/8.4/fileHashes/fileHashes.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-2/.gradle/8.4/fileHashes/fileHashes.lock -------------------------------------------------------------------------------- /chapter-2/.gradle/8.4/fileHashes/resourceHashesCache.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-2/.gradle/8.4/fileHashes/resourceHashesCache.bin -------------------------------------------------------------------------------- /chapter-2/.gradle/8.4/gc.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-2/.gradle/8.4/gc.properties -------------------------------------------------------------------------------- /chapter-2/.gradle/buildOutputCleanup/buildOutputCleanup.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-2/.gradle/buildOutputCleanup/buildOutputCleanup.lock -------------------------------------------------------------------------------- /chapter-2/.gradle/buildOutputCleanup/cache.properties: -------------------------------------------------------------------------------- 1 | #Mon Oct 30 20:08:39 GMT 2023 2 | gradle.version=8.4 3 | -------------------------------------------------------------------------------- /chapter-2/.gradle/buildOutputCleanup/outputFiles.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-2/.gradle/buildOutputCleanup/outputFiles.bin -------------------------------------------------------------------------------- /chapter-2/.gradle/file-system.probe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-2/.gradle/file-system.probe -------------------------------------------------------------------------------- /chapter-2/.gradle/vcs-1/gc.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-2/.gradle/vcs-1/gc.properties -------------------------------------------------------------------------------- /chapter-2/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /chapter-2/.idea/.name: -------------------------------------------------------------------------------- 1 | HelloWorld -------------------------------------------------------------------------------- /chapter-2/.idea/aws.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /chapter-2/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 11 | 19 | 23 | 24 | 29 | 30 | -------------------------------------------------------------------------------- /chapter-2/.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /chapter-2/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /chapter-2/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 17 | 18 | -------------------------------------------------------------------------------- /chapter-2/.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /chapter-2/.idea/ktlint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | 6 | -------------------------------------------------------------------------------- /chapter-2/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /chapter-2/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /chapter-2/README.md: -------------------------------------------------------------------------------- 1 | # HelloWorld 2 | 3 | ## Package 4 | ``` 5 | ./gradlew distZip 6 | ``` 7 | 8 | -------------------------------------------------------------------------------- /chapter-2/gradle.properties: -------------------------------------------------------------------------------- 1 | junitVersion=5.10.0 2 | http4kVersion=5.9.0.0 3 | kotlinVersion=1.9.10 4 | -------------------------------------------------------------------------------- /chapter-2/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-2/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-2/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /chapter-2/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "HelloWorld" 2 | -------------------------------------------------------------------------------- /chapter-2/src/main/kotlin/packt/architecturekotlin/chapter2/example1/ExchangeService.kt: -------------------------------------------------------------------------------- 1 | package packt.architecturekotlin.chapter2.example1 2 | 3 | data class Household( 4 | val name: String, 5 | val members: List, 6 | ) 7 | 8 | data class Person( 9 | val firstName: String, 10 | val lastName: String, 11 | val age: Int, 12 | val skills: List, 13 | ) 14 | 15 | fun Household.validate(): List = 16 | listOfNotNull( 17 | if (name.isBlank()) "name must not be empty" else null, 18 | ) + members.flatMap { it.validate() } 19 | 20 | fun Person.validate(): List = listOfNotNull( 21 | if (firstName.isBlank()) "first name must be non-empty" else null, 22 | if (lastName.isBlank()) "last name must be non-empty" else null, 23 | if (age < 0) "age must be non-negative" else null, 24 | ) 25 | -------------------------------------------------------------------------------- /chapter-2/src/main/kotlin/packt/architecturekotlin/chapter2/example2/Enums.kt: -------------------------------------------------------------------------------- 1 | package packt.architecturekotlin.chapter2.example2 2 | 3 | enum class InternalError { 4 | WRONG_PASSWORD, 5 | USERNAME_NOT_FOUND, 6 | FAILED_CAPTCHA, 7 | TIMED_OUT, 8 | INVALID_REQUEST 9 | } 10 | 11 | enum class ExternalError { 12 | FAILED_AUTHENTICATION, 13 | TIMED_OUT, 14 | INVALID_REQUEST 15 | } -------------------------------------------------------------------------------- /chapter-2/src/main/kotlin/packt/architecturekotlin/chapter2/example3/dip/ContractWorkflowService.kt: -------------------------------------------------------------------------------- 1 | package packt.architecturekotlin.chapter2.example3.dip 2 | 3 | import java.time.Instant 4 | 5 | class ContractWorkflowService( 6 | val emailNotificationService: EmailNotificationService, 7 | ) { 8 | fun agree(contract: Contract): Contract { 9 | return contract.copy(agreedAt = Instant.now()).also { 10 | emailNotificationService.notifyHouseholds(contract) 11 | } 12 | } 13 | } 14 | interface NotificationService { 15 | fun notifyHouseholds(contract: Contract) 16 | } 17 | class EmailNotificationService : NotificationService { 18 | override fun notifyHouseholds(contract: Contract) { 19 | // send messages to household's email addresses 20 | } 21 | } 22 | 23 | data class Contract( 24 | val draftedAt: Instant, 25 | val agreedAt: Instant, 26 | ) 27 | -------------------------------------------------------------------------------- /chapter-2/src/main/kotlin/packt/architecturekotlin/chapter2/example3/isp/Human.kt: -------------------------------------------------------------------------------- 1 | package packt.architecturekotlin.chapter2.example3.isp 2 | 3 | interface Human { 4 | fun logOn() 5 | 6 | fun exerciseContract() 7 | } 8 | 9 | data class User(val username: String) : Human { 10 | override fun logOn() { 11 | // user log on 12 | } 13 | 14 | override fun exerciseContract() { 15 | throw UnsupportedOperationException("user cannot exercise contract") 16 | } 17 | } 18 | 19 | data class HouseholdMember(val name: String) : Human { 20 | override fun logOn() { 21 | throw UnsupportedOperationException("household members do not log on") 22 | } 23 | 24 | override fun exerciseContract() { 25 | // exercise contract 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter-2/src/main/kotlin/packt/architecturekotlin/chapter2/example3/lsp/PhoneNotificationService.kt: -------------------------------------------------------------------------------- 1 | package packt.architecturekotlin.chapter2.example3.lsp 2 | 3 | import packt.architecturekotlin.chapter2.example3.srp.Contract 4 | import packt.architecturekotlin.chapter2.example3.srp.NotificationService 5 | 6 | class PhoneNotificationService : NotificationService { 7 | 8 | override fun notifyHouseholds(contract: Contract) { 9 | // ring an automated message to household's phone 10 | 11 | // also update contract status to UNDER_REVIEW 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /chapter-2/src/main/kotlin/packt/architecturekotlin/chapter2/example3/ocp/NotificationService.kt: -------------------------------------------------------------------------------- 1 | package packt.architecturekotlin.chapter2.example3.ocp 2 | 3 | import packt.architecturekotlin.chapter2.example3.srp.Contract 4 | 5 | interface NotificationService { 6 | fun notifyIHouseholds(contract: Contract) 7 | } 8 | class SmsNotificationService : NotificationService { 9 | override fun notifyIHouseholds(contract: Contract) { 10 | // send sms messages to household's phone numbers 11 | } 12 | } 13 | class EmailNotificationService : NotificationService { 14 | override fun notifyIHouseholds(contract: Contract) { 15 | // send messages to household's email addresses 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /chapter-2/src/main/kotlin/packt/architecturekotlin/chapter2/example3/srp/WithoutSrp.kt: -------------------------------------------------------------------------------- 1 | package packt.architecturekotlin.chapter2.example3.srp 2 | 3 | import packt.architecturekotlin.chapter2.example1.Household 4 | import java.time.Instant 5 | 6 | interface HouseholdService { 7 | fun create(household: Household): Household 8 | } 9 | interface ContractService { 10 | fun draftContract(contract: Contract) 11 | } 12 | interface NotificationService { 13 | fun notifyHouseholds(contract: Contract) 14 | } 15 | 16 | class SmsNotificationService : NotificationService { 17 | 18 | override fun notifyHouseholds(contract: Contract) { 19 | // send sms messages to household's phone numbers 20 | } 21 | } 22 | 23 | class EmailNotificationService : NotificationService { 24 | override fun notifyHouseholds(contract: Contract) { 25 | // send messages to household's email addresses 26 | } 27 | } 28 | 29 | data class Contract( 30 | val draftedBy: Instant, 31 | ) 32 | -------------------------------------------------------------------------------- /chapter-2/src/main/kotlin/packt/architecturekotlin/chapter2/example4/demeter.kt: -------------------------------------------------------------------------------- 1 | package packt.architecturekotlin.chapter2.example4 2 | 3 | class Person(val name: String, val address: Address) { 4 | fun getAddressCity(): String { 5 | return address.city 6 | } 7 | } 8 | 9 | class Address(val city: String) 10 | -------------------------------------------------------------------------------- /chapter-2/src/main/kotlin/packt/architecturekotlin/chapter2/formats/JacksonMessage.kt: -------------------------------------------------------------------------------- 1 | package packt.architecturekotlin.chapter2.formats 2 | 3 | import org.http4k.core.Body 4 | import org.http4k.format.Jackson.auto 5 | 6 | data class JacksonMessage(val subject: String, val message: String) 7 | 8 | val jacksonMessageLens = Body.auto().toLens() 9 | -------------------------------------------------------------------------------- /chapter-2/src/test/kotlin/packt/architecturekotlin/chapter2/HelloWorldClient.kt: -------------------------------------------------------------------------------- 1 | package packt.architecturekotlin.chapter2 2 | 3 | import org.http4k.client.JavaHttpClient 4 | import org.http4k.core.HttpHandler 5 | import org.http4k.core.Method.GET 6 | import org.http4k.core.Request 7 | import org.http4k.core.Response 8 | import org.http4k.core.then 9 | import org.http4k.filter.DebuggingFilters.PrintResponse 10 | 11 | fun main() { 12 | val client: HttpHandler = JavaHttpClient() 13 | 14 | val printingClient: HttpHandler = PrintResponse().then(client) 15 | 16 | val response: Response = printingClient(Request(GET, "http://localhost:9000/ping")) 17 | 18 | println(response.bodyString()) 19 | } 20 | -------------------------------------------------------------------------------- /chapter-2/src/test/kotlin/packt/architecturekotlin/chapter2/HelloWorldTest.kt: -------------------------------------------------------------------------------- 1 | package packt.architecturekotlin.chapter2 2 | 3 | import org.http4k.core.Method.GET 4 | import org.http4k.core.Request 5 | import org.http4k.core.Response 6 | import org.http4k.core.Status.Companion.OK 7 | import org.http4k.kotest.shouldHaveBody 8 | import org.http4k.kotest.shouldHaveStatus 9 | import org.junit.jupiter.api.Assertions.assertEquals 10 | import org.junit.jupiter.api.Test 11 | 12 | class HelloWorldTest { 13 | 14 | @Test 15 | fun `Ping test`() { 16 | assertEquals(Response(OK).body("pong"), app(Request(GET, "/ping"))) 17 | } 18 | @Test 19 | fun `Check Kotest matcher for http4k work as expected`() { 20 | val request = Request(GET, "/testing/kotest?a=b").body("http4k is cool").header("my header", "a value") 21 | 22 | val response = app(request) 23 | 24 | // response assertions 25 | response shouldHaveStatus OK 26 | response shouldHaveBody "Echo 'http4k is cool'" 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /chapter-3/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /chapter-3/.idea/aws.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /chapter-3/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /chapter-3/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 17 | 18 | -------------------------------------------------------------------------------- /chapter-3/.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /chapter-3/.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /chapter-3/.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /chapter-3/.idea/ktlint-plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DISTRACT_FREE 5 | 6 | -------------------------------------------------------------------------------- /chapter-3/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /chapter-3/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /chapter-3/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") version "1.8.0" 3 | } 4 | 5 | group = "org.example" 6 | version = "1.0-SNAPSHOT" 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | testImplementation(kotlin("test")) 14 | } 15 | 16 | tasks.test { 17 | useJUnitPlatform() 18 | } 19 | 20 | kotlin { 21 | jvmToolchain(8) 22 | } -------------------------------------------------------------------------------- /chapter-3/docs/demo-720.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-3/docs/demo-720.mp4 -------------------------------------------------------------------------------- /chapter-3/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | -------------------------------------------------------------------------------- /chapter-3/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-3/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-3/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /chapter-3/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | 2 | rootProject.name = "chapter-3" 3 | 4 | -------------------------------------------------------------------------------- /chapter-4/ClientServer/README.md: -------------------------------------------------------------------------------- 1 | # ClientServer 2 | 3 | ## Package 4 | ``` 5 | ./gradlew distZip 6 | ``` 7 | 8 | -------------------------------------------------------------------------------- /chapter-4/ClientServer/docs/demo-720.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-4/ClientServer/docs/demo-720.mp4 -------------------------------------------------------------------------------- /chapter-4/ClientServer/gradle.properties: -------------------------------------------------------------------------------- 1 | junitVersion=5.10.0 2 | http4kVersion=5.13.0.0 3 | kotlinVersion=1.9.22 4 | -------------------------------------------------------------------------------- /chapter-4/ClientServer/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-4/ClientServer/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-4/ClientServer/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /chapter-4/ClientServer/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "ClientServer" 2 | -------------------------------------------------------------------------------- /chapter-4/ClientServer/src/main/kotlin/clientserver/formats/JacksonMessage.kt: -------------------------------------------------------------------------------- 1 | package clientserver.formats 2 | 3 | import java.time.Instant 4 | 5 | data class ServiceContract( 6 | val id: Int, 7 | val partyA: Party, 8 | val partyB: Party, 9 | ) 10 | 11 | data class Party( 12 | val householdName: String, 13 | val service: String, 14 | val agreedAt: Instant? = null, 15 | ) 16 | -------------------------------------------------------------------------------- /chapter-4/P2P/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") version "1.9.22" 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | -------------------------------------------------------------------------------- /chapter-4/P2P/docs/demo-720.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-4/P2P/docs/demo-720.mp4 -------------------------------------------------------------------------------- /chapter-4/P2P/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-4/P2P/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-4/P2P/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /chapter-4/P2P/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "P2P" 2 | -------------------------------------------------------------------------------- /chapter-4/P2P/src/main/kotlin/p2p/HouseholdB.kt: -------------------------------------------------------------------------------- 1 | 2 | package p2p 3 | 4 | import ServiceContract 5 | import agree 6 | import bothPartiesHaveDifferentNames 7 | import isHouseholdInvolved 8 | import partyAgreed 9 | import serviceReceivedBy 10 | import withReceivedService 11 | import java.net.InetSocketAddress 12 | import java.time.Instant 13 | 14 | fun main() { 15 | val node = 16 | UdpNode( 17 | InetSocketAddress(HOST_B, PORT_B), 18 | ServiceContractConvertor, 19 | ) { it.receivedByHouseholdB() } 20 | node.start() 21 | loopForever(1000) { node.consume() } 22 | } 23 | 24 | fun ServiceContract.receivedByHouseholdB() = 25 | if (bothPartiesHaveDifferentNames().not() || 26 | partyAgreed(HOUSEHOLD_B) || 27 | isHouseholdInvolved(HOUSEHOLD_B).not() 28 | ) { 29 | println("No response to service contract: $this") 30 | null 31 | } else if (serviceReceivedBy(HOUSEHOLD_B) == CLEANING) { 32 | println("Submitted revised service contract: $id") 33 | withReceivedService(HOUSEHOLD_B, BABYSITTING) 34 | } else if (serviceReceivedBy(HOUSEHOLD_B) == BABYSITTING) { 35 | println("Agreed to service contract: $id") 36 | agree(HOUSEHOLD_B) { Instant.now() } 37 | } else { 38 | println("No response to service contract: $id") 39 | null 40 | } 41 | -------------------------------------------------------------------------------- /chapter-4/P2P/src/main/kotlin/p2p/Loop.kt: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | fun loopForever( 4 | intervalInMs: Long, 5 | delegate: () -> Unit, 6 | ) { 7 | assert(intervalInMs >= 0L) 8 | 9 | while (true) { 10 | try { 11 | delegate() 12 | if (intervalInMs > 0) { 13 | Thread.sleep(intervalInMs) 14 | } 15 | } catch (ignored: Exception) { 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /chapter-4/P2P/src/main/kotlin/p2p/UdpNode.kt: -------------------------------------------------------------------------------- 1 | package p2p 2 | 3 | import java.net.SocketAddress 4 | import java.nio.ByteBuffer 5 | import java.nio.channels.DatagramChannel 6 | 7 | class UdpNode( 8 | val address: SocketAddress, 9 | val convertor: DtoConvertor, 10 | val transformer: (T) -> T?, 11 | ) { 12 | private val inbound: ByteBuffer = convertor.allocate() 13 | private val outbound: ByteBuffer = convertor.allocate() 14 | private var channel: DatagramChannel? = null 15 | 16 | fun start() { 17 | channel = 18 | DatagramChannel.open() 19 | .bind(address) 20 | 21 | println("Started on $$address") 22 | } 23 | 24 | fun produce( 25 | payload: T, 26 | target: SocketAddress, 27 | ): Int { 28 | outbound.clear() 29 | convertor.toBuffer(payload, outbound) 30 | outbound.flip() 31 | 32 | return channel!!.send(outbound, target) 33 | } 34 | 35 | fun consume(): Int { 36 | return channel?.let { c -> 37 | inbound.clear() 38 | val address: SocketAddress = c.receive(inbound) 39 | inbound.rewind() 40 | val received = convertor.fromBuffer(inbound) 41 | 42 | transformer(received)?.let { transformed -> 43 | produce(transformed, address) 44 | } 45 | } ?: 0 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /chapter-5/base/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /chapter-5/base/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /chapter-5/base/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | namespace 'com.example.base' 8 | compileSdk 33 9 | 10 | defaultConfig { 11 | applicationId "com.example.base" 12 | minSdk 29 13 | targetSdk 33 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | compileOptions { 27 | sourceCompatibility JavaVersion.VERSION_1_8 28 | targetCompatibility JavaVersion.VERSION_1_8 29 | } 30 | kotlinOptions { 31 | jvmTarget = '1.8' 32 | } 33 | } 34 | 35 | dependencies { 36 | 37 | implementation 'androidx.core:core-ktx:1.7.0' 38 | implementation 'androidx.appcompat:appcompat:1.4.1' 39 | implementation 'com.google.android.material:material:1.5.0' 40 | implementation 'androidx.constraintlayout:constraintlayout:2.1.3' 41 | testImplementation 'junit:junit:4.13.2' 42 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 43 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 44 | } -------------------------------------------------------------------------------- /chapter-5/base/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /chapter-5/base/app/src/androidTest/java/com/example/base/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.base 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.example.base", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/java/com/example/base/model/ContractRepository.kt: -------------------------------------------------------------------------------- 1 | package com.example.base.model 2 | 3 | class ContractRepository { 4 | fun submit(contract: DraftContractInput): Boolean { 5 | // Submit the draft contract for validation and persistence 6 | return true.also { 7 | println("Persisted contract: $contract") 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/java/com/example/base/model/DraftContractInput.kt: -------------------------------------------------------------------------------- 1 | package com.example.base.model 2 | 3 | data class DraftContractInput( 4 | val initiator: HouseholdInput, 5 | val neighbor: HouseholdInput 6 | ) 7 | data class HouseholdInput( 8 | val householdName: String, 9 | val serviceProvided: String 10 | ) -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/java/com/example/base/view/ConfirmationFragment.kt: -------------------------------------------------------------------------------- 1 | package com.example.base.view 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.Fragment 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import com.example.base.R 9 | 10 | class ConfirmationFragment : Fragment() { 11 | override fun onCreateView( 12 | inflater: LayoutInflater, container: ViewGroup?, 13 | savedInstanceState: Bundle? 14 | ): View? { 15 | val view = inflater.inflate(R.layout.fragment_confirmation, container, false) 16 | return view 17 | } 18 | } -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/layout/fragment_confirmation.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 22 | 23 | -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/mipmap-anydpi-v33/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/base/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/base/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/base/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/base/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/base/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/base/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/base/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/base/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/base/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/base/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Exchange of contracts 3 | -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /chapter-5/base/app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /chapter-5/base/app/src/test/java/com/example/base/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.base 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /chapter-5/base/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | id 'com.android.application' version '7.4.0' apply false 4 | id 'com.android.library' version '7.4.0' apply false 5 | id 'org.jetbrains.kotlin.android' version '1.7.21' apply false 6 | } -------------------------------------------------------------------------------- /chapter-5/base/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/base/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-5/base/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Mar 10 19:13:30 GMT 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /chapter-5/base/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | } 15 | rootProject.name = "base" 16 | include ':app' 17 | -------------------------------------------------------------------------------- /chapter-5/mvc/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /chapter-5/mvc/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /chapter-5/mvc/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | namespace 'com.example.base' 8 | compileSdk 33 9 | 10 | defaultConfig { 11 | applicationId "com.example.base" 12 | minSdk 29 13 | targetSdk 33 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | compileOptions { 27 | sourceCompatibility JavaVersion.VERSION_1_8 28 | targetCompatibility JavaVersion.VERSION_1_8 29 | } 30 | kotlinOptions { 31 | jvmTarget = '1.8' 32 | } 33 | } 34 | 35 | dependencies { 36 | 37 | implementation 'androidx.core:core-ktx:1.7.0' 38 | implementation 'androidx.appcompat:appcompat:1.4.1' 39 | implementation 'com.google.android.material:material:1.5.0' 40 | implementation 'androidx.constraintlayout:constraintlayout:2.1.3' 41 | testImplementation 'junit:junit:4.13.2' 42 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 43 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 44 | } -------------------------------------------------------------------------------- /chapter-5/mvc/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/androidTest/java/com/example/base/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.base 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.example.base", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/java/com/example/base/model/ContractRepository.kt: -------------------------------------------------------------------------------- 1 | package com.example.base.model 2 | 3 | class ContractRepository { 4 | fun submit(contract: DraftContractInput): Boolean { 5 | // Submit the draft contract for validation and persistence 6 | return true.also { 7 | println("Persisted contract: $contract") 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/java/com/example/base/model/DraftContractInput.kt: -------------------------------------------------------------------------------- 1 | package com.example.base.model 2 | 3 | data class DraftContractInput( 4 | val initiator: HouseholdInput, 5 | val neighbor: HouseholdInput 6 | ) 7 | data class HouseholdInput( 8 | val householdName: String, 9 | val serviceProvided: String 10 | ) -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/java/com/example/base/view/ConfirmationFragment.kt: -------------------------------------------------------------------------------- 1 | package com.example.base.view 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.Fragment 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.TextView 9 | import com.example.base.R 10 | 11 | 12 | class ConfirmationFragment : Fragment() { 13 | override fun onCreateView( 14 | inflater: LayoutInflater, container: ViewGroup?, 15 | savedInstanceState: Bundle? 16 | ): View? { 17 | val inflated = inflater.inflate(R.layout.fragment_confirmation, container, false) 18 | 19 | val yourHouseholdName = arguments?.getString("yourHouseholdName") 20 | val yourHouseholdService = arguments?.getString("yourHouseholdService") 21 | val yourNeighborName = arguments?.getString("yourNeighborName") 22 | val yourNeighborService = arguments?.getString("yourNeighborService") 23 | inflated.findViewById(R.id.your_household_summary).text = 24 | "Your household \"$yourHouseholdName\" providing ${yourHouseholdService}" 25 | inflated.findViewById(R.id.your_neighbor_summary).text = 26 | "your neighbor \"$yourNeighborName\" providing ${yourNeighborService}" 27 | 28 | return inflated 29 | } 30 | } -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/mipmap-anydpi-v33/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvc/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvc/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvc/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvc/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvc/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvc/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvc/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvc/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvc/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvc/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Exchange of contracts 3 | -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /chapter-5/mvc/app/src/test/java/com/example/base/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.base 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /chapter-5/mvc/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | id 'com.android.application' version '7.4.0' apply false 4 | id 'com.android.library' version '7.4.0' apply false 5 | id 'org.jetbrains.kotlin.android' version '1.7.21' apply false 6 | } -------------------------------------------------------------------------------- /chapter-5/mvc/docs/demo-720.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvc/docs/demo-720.mp4 -------------------------------------------------------------------------------- /chapter-5/mvc/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvc/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-5/mvc/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Mar 10 19:13:30 GMT 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /chapter-5/mvc/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | } 15 | rootProject.name = "base" 16 | include ':app' 17 | -------------------------------------------------------------------------------- /chapter-5/mvp/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /chapter-5/mvp/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /chapter-5/mvp/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | namespace 'com.example.base' 8 | compileSdk 33 9 | 10 | defaultConfig { 11 | applicationId "com.example.base" 12 | minSdk 29 13 | targetSdk 33 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | compileOptions { 27 | sourceCompatibility JavaVersion.VERSION_1_8 28 | targetCompatibility JavaVersion.VERSION_1_8 29 | } 30 | kotlinOptions { 31 | jvmTarget = '1.8' 32 | } 33 | } 34 | 35 | dependencies { 36 | 37 | implementation 'androidx.core:core-ktx:1.7.0' 38 | implementation 'androidx.appcompat:appcompat:1.4.1' 39 | implementation 'com.google.android.material:material:1.5.0' 40 | implementation 'androidx.constraintlayout:constraintlayout:2.1.3' 41 | testImplementation 'junit:junit:4.13.2' 42 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 43 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 44 | } -------------------------------------------------------------------------------- /chapter-5/mvp/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/androidTest/java/com/example/base/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.base 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.example.base", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/java/com/example/base/model/ContractRepository.kt: -------------------------------------------------------------------------------- 1 | package com.example.base.model 2 | 3 | class ContractRepository { 4 | var onSubmitListener: DraftContractSubmittedListener? = null 5 | 6 | fun submit(contract: DraftContractInput): Boolean { 7 | // Submit the draft contract for validation and persistence 8 | return true.also { 9 | onSubmitListener?.invoke(contract) 10 | }.also { 11 | println("Persisted contract: $contract") 12 | } 13 | } 14 | } 15 | 16 | typealias DraftContractSubmittedListener = (DraftContractInput) -> Unit -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/java/com/example/base/model/DraftContractInput.kt: -------------------------------------------------------------------------------- 1 | package com.example.base.model 2 | 3 | data class DraftContractInput( 4 | val initiator: HouseholdInput, 5 | val neighbor: HouseholdInput 6 | ) 7 | data class HouseholdInput( 8 | val householdName: String, 9 | val serviceProvided: String 10 | ) -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/java/com/example/base/presenter/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.base.presenter 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import com.example.base.R 6 | import com.example.base.model.ContractRepository 7 | import com.example.base.model.DraftContractInput 8 | import com.example.base.view.ConfirmationFragment 9 | import com.example.base.view.ContractDraftFragment 10 | 11 | class MainActivity : AppCompatActivity(), Presenter { 12 | private val contractRepository: ContractRepository = ContractRepository() 13 | 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | super.onCreate(savedInstanceState) 16 | setContentView(R.layout.activity_main) 17 | 18 | val contractDraftFragment = ContractDraftFragment() 19 | supportFragmentManager.beginTransaction().replace(R.id.fragment_container, contractDraftFragment).commit() 20 | 21 | } 22 | 23 | override fun submitContract(contract: DraftContractInput) { 24 | contractRepository.onSubmitListener = { 25 | val confirmationFragment = ConfirmationFragment() 26 | confirmationFragment.lastSubmittedContract = it 27 | supportFragmentManager.beginTransaction().replace(R.id.fragment_container, confirmationFragment).commit() 28 | } 29 | contractRepository.submit(contract) 30 | } 31 | } -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/java/com/example/base/presenter/Presenter.kt: -------------------------------------------------------------------------------- 1 | package com.example.base.presenter 2 | 3 | import com.example.base.model.DraftContractInput 4 | 5 | interface Presenter { 6 | fun submitContract(contract: DraftContractInput) 7 | } -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/java/com/example/base/view/ConfirmationFragment.kt: -------------------------------------------------------------------------------- 1 | package com.example.base.view 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.Fragment 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.Button 9 | import android.widget.TextView 10 | import com.example.base.R 11 | import com.example.base.model.DraftContractInput 12 | import com.example.base.presenter.Presenter 13 | 14 | class ConfirmationFragment : Fragment() { 15 | lateinit var lastSubmittedContract: DraftContractInput 16 | 17 | override fun onCreateView( 18 | inflater: LayoutInflater, container: ViewGroup?, 19 | savedInstanceState: Bundle? 20 | ): View? { 21 | val inflated = inflater.inflate(R.layout.fragment_confirmation, container, false) 22 | 23 | lastSubmittedContract?.also { 24 | inflated.findViewById(R.id.your_household_summary).text = 25 | "Your household \"${it.initiator.householdName}\" providing ${it.initiator.serviceProvided}" 26 | inflated.findViewById(R.id.your_neighbor_summary).text = 27 | "your neighbor \"${it.neighbor.householdName}\" providing ${it.neighbor.serviceProvided}" 28 | } 29 | return inflated 30 | } 31 | } -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/mipmap-anydpi-v33/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvp/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvp/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvp/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvp/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvp/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvp/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Exchange of contracts 3 | -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/test/java/com/example/base/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.base 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /chapter-5/mvp/app/src/test/java/com/example/base/model/ContractRepositoryTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.base.model 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | class ContractRepositoryTest { 8 | private val repository = ContractRepository() 9 | private val contract = DraftContractInput( 10 | HouseholdInput("Smith", "Cleaning"), 11 | HouseholdInput("Lee", "Cooking") 12 | ) 13 | @Test 14 | fun `Returns true after a draft contract is submitted`() { 15 | assertTrue(repository.submit(contract)) 16 | } 17 | @Test 18 | fun `Invokes callback after a draft contract is submitted`() { 19 | var received: DraftContractInput? = null 20 | repository.onSubmitListener = { 21 | received = it 22 | } 23 | assertNull(received) 24 | repository.submit(contract) 25 | assertEquals(contract, received) 26 | } 27 | } -------------------------------------------------------------------------------- /chapter-5/mvp/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | id 'com.android.application' version '7.4.0' apply false 4 | id 'com.android.library' version '7.4.0' apply false 5 | id 'org.jetbrains.kotlin.android' version '1.7.21' apply false 6 | } -------------------------------------------------------------------------------- /chapter-5/mvp/docs/demo-720.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvp/docs/demo-720.mp4 -------------------------------------------------------------------------------- /chapter-5/mvp/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvp/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-5/mvp/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /chapter-5/mvp/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | } 15 | rootProject.name = "base" 16 | include ':app' 17 | -------------------------------------------------------------------------------- /chapter-5/mvvm/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /chapter-5/mvvm/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /chapter-5/mvvm/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | namespace 'com.example.base' 8 | compileSdk 33 9 | 10 | defaultConfig { 11 | applicationId "com.example.base" 12 | minSdk 29 13 | targetSdk 33 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | compileOptions { 27 | sourceCompatibility JavaVersion.VERSION_1_8 28 | targetCompatibility JavaVersion.VERSION_1_8 29 | } 30 | kotlinOptions { 31 | jvmTarget = '1.8' 32 | } 33 | } 34 | 35 | dependencies { 36 | 37 | implementation 'androidx.core:core-ktx:1.7.0' 38 | implementation 'androidx.appcompat:appcompat:1.4.1' 39 | implementation 'com.google.android.material:material:1.5.0' 40 | implementation 'androidx.constraintlayout:constraintlayout:2.1.3' 41 | testImplementation 'junit:junit:4.13.2' 42 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 43 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 44 | } -------------------------------------------------------------------------------- /chapter-5/mvvm/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/androidTest/java/com/example/base/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.base 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.example.base", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/java/com/example/base/model/ContractRepository.kt: -------------------------------------------------------------------------------- 1 | package com.example.base.model 2 | 3 | class ContractRepository { 4 | var onSubmitListener: DraftContractSubmittedListener? = null 5 | 6 | fun submit(contract: DraftContractInput): Boolean { 7 | // Submit the draft contract for validation and persistence 8 | return true.also { 9 | onSubmitListener?.invoke(contract) 10 | }.also { 11 | println("Persisted contract: $contract") 12 | } 13 | } 14 | } 15 | 16 | typealias DraftContractSubmittedListener = (DraftContractInput) -> Unit -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/java/com/example/base/model/DraftContractInput.kt: -------------------------------------------------------------------------------- 1 | package com.example.base.model 2 | 3 | data class DraftContractInput( 4 | val initiator: HouseholdInput, 5 | val neighbor: HouseholdInput 6 | ) 7 | data class HouseholdInput( 8 | val householdName: String, 9 | val serviceProvided: String 10 | ) -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/java/com/example/base/view/ConfirmationFragment.kt: -------------------------------------------------------------------------------- 1 | package com.example.base.view 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.Fragment 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.TextView 9 | import androidx.appcompat.app.AppCompatActivity 10 | import androidx.lifecycle.ViewModelProvider 11 | import com.example.base.R 12 | import com.example.base.viewmodel.DraftContractViewModel 13 | 14 | class ConfirmationFragment : Fragment() { 15 | override fun onCreateView( 16 | inflater: LayoutInflater, container: ViewGroup?, 17 | savedInstanceState: Bundle? 18 | ): View? { 19 | val inflated = inflater.inflate(R.layout.fragment_confirmation, container, false) 20 | val viewModel = ViewModelProvider(activity as AppCompatActivity).get(DraftContractViewModel::class.java) 21 | inflated.findViewById(R.id.your_household_summary).text = 22 | "Your household \"${viewModel.yourHouseholdName}\" providing ${viewModel.yourHouseholdService}" 23 | inflated.findViewById(R.id.your_neighbor_summary).text = 24 | "your neighbor \"${viewModel.yourNeighborName}\" providing ${viewModel.yourNeighborService}" 25 | return inflated 26 | } 27 | } -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/java/com/example/base/viewmodel/Command.kt: -------------------------------------------------------------------------------- 1 | package com.example.base.viewmodel 2 | 3 | import com.example.base.model.DraftContractInput 4 | 5 | interface Command { 6 | fun submitContract(contract: DraftContractInput) 7 | } -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/java/com/example/base/viewmodel/DraftContractViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.example.base.viewmodel 2 | 3 | import android.widget.EditText 4 | import androidx.lifecycle.ViewModel 5 | import com.example.base.model.DraftContractInput 6 | import com.example.base.model.HouseholdInput 7 | 8 | class DraftContractViewModel : ViewModel() { 9 | var yourHouseholdName: String? = null 10 | var yourHouseholdService: String? = null 11 | var yourNeighborName: String? = null 12 | var yourNeighborService: String? = null 13 | } 14 | 15 | fun DraftContractViewModel.toModel(): DraftContractInput? = 16 | if (yourHouseholdName != null 17 | && yourHouseholdService != null 18 | && yourNeighborName != null 19 | && yourNeighborService != null 20 | ) { 21 | DraftContractInput( 22 | initiator = HouseholdInput( 23 | householdName = yourHouseholdName!!, 24 | serviceProvided = yourHouseholdService!! 25 | ), 26 | neighbor = HouseholdInput( 27 | householdName = yourNeighborName!!, 28 | serviceProvided = yourNeighborService!! 29 | ) 30 | ) 31 | } else { 32 | null 33 | } 34 | -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/java/com/example/base/viewmodel/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.base.viewmodel 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import com.example.base.R 6 | import com.example.base.model.ContractRepository 7 | import com.example.base.model.DraftContractInput 8 | import com.example.base.view.ConfirmationFragment 9 | import com.example.base.view.ContractDraftFragment 10 | 11 | class MainActivity : AppCompatActivity(), Command { 12 | private val contractRepository: ContractRepository = ContractRepository() 13 | 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | super.onCreate(savedInstanceState) 16 | setContentView(R.layout.activity_main) 17 | 18 | val contractDraftFragment = ContractDraftFragment() 19 | supportFragmentManager.beginTransaction().replace(R.id.fragment_container, contractDraftFragment).commit() 20 | } 21 | 22 | override fun submitContract(contract: DraftContractInput) { 23 | contractRepository.onSubmitListener = { 24 | val confirmationFragment = ConfirmationFragment() 25 | supportFragmentManager.beginTransaction().replace(R.id.fragment_container, confirmationFragment).commit() 26 | } 27 | contractRepository.submit(contract) 28 | } 29 | } -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/mipmap-anydpi-v33/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvvm/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvvm/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvvm/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvvm/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvvm/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvvm/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvvm/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvvm/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvvm/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvvm/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Exchange of contracts 3 | -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /chapter-5/mvvm/app/src/test/java/com/example/base/model/ContractRepositoryTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.base.model 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | class ContractRepositoryTest { 8 | private val repository = ContractRepository() 9 | private val contract = DraftContractInput( 10 | HouseholdInput("Smith", "Cleaning"), 11 | HouseholdInput("Lee", "Cooking") 12 | ) 13 | @Test 14 | fun `Returns true after a draft contract is submitted`() { 15 | assertTrue(repository.submit(contract)) 16 | } 17 | @Test 18 | fun `Invokes callback after a draft contract is submitted`() { 19 | var received: DraftContractInput? = null 20 | repository.onSubmitListener = { 21 | received = it 22 | } 23 | assertNull(received) 24 | repository.submit(contract) 25 | assertEquals(contract, received) 26 | } 27 | } -------------------------------------------------------------------------------- /chapter-5/mvvm/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | id 'com.android.application' version '7.4.0' apply false 4 | id 'com.android.library' version '7.4.0' apply false 5 | id 'org.jetbrains.kotlin.android' version '1.7.21' apply false 6 | } -------------------------------------------------------------------------------- /chapter-5/mvvm/docs/demo-720.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvvm/docs/demo-720.mp4 -------------------------------------------------------------------------------- /chapter-5/mvvm/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-5/mvvm/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-5/mvvm/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Mar 10 19:13:30 GMT 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /chapter-5/mvvm/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | } 15 | rootProject.name = "base" 16 | include ':app' 17 | -------------------------------------------------------------------------------- /chapter-6/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | bin/ 16 | !**/src/main/**/bin/ 17 | !**/src/test/**/bin/ 18 | 19 | ### IntelliJ IDEA ### 20 | .idea 21 | *.iws 22 | *.iml 23 | *.ipr 24 | out/ 25 | !**/src/main/**/out/ 26 | !**/src/test/**/out/ 27 | 28 | ### NetBeans ### 29 | /nbproject/private/ 30 | /nbbuild/ 31 | /dist/ 32 | /nbdist/ 33 | /.nb-gradle/ 34 | 35 | ### VS Code ### 36 | .vscode/ -------------------------------------------------------------------------------- /chapter-6/build.gradle.kts: -------------------------------------------------------------------------------- 1 | 2 | val kotlin_version: String by project 3 | val logback_version: String by project 4 | 5 | plugins { 6 | kotlin("jvm") version "2.0.20" 7 | kotlin("plugin.serialization") version "2.0.20" 8 | id("io.ktor.plugin") version "2.3.12" 9 | } 10 | 11 | group = "com.example" 12 | version = "0.0.1" 13 | 14 | application { 15 | mainClass.set("io.ktor.server.netty.EngineMain") 16 | 17 | val isDevelopment: Boolean = project.ext.has("development") 18 | applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment") 19 | } 20 | 21 | repositories { 22 | mavenCentral() 23 | } 24 | 25 | dependencies { 26 | implementation("io.ktor:ktor-server-core-jvm") 27 | implementation("io.ktor:ktor-server-netty-jvm") 28 | implementation("ch.qos.logback:logback-classic:$logback_version") 29 | implementation("io.ktor:ktor-server-config-yaml") 30 | implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.12") 31 | implementation("io.ktor:ktor-server-content-negotiation:2.3.12") 32 | implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") 33 | testImplementation("io.ktor:ktor-server-test-host-jvm") 34 | testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version") 35 | } 36 | -------------------------------------------------------------------------------- /chapter-6/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | ktor_version=2.3.12 3 | kotlin_version=2.0.20 4 | logback_version=1.4.14 5 | -------------------------------------------------------------------------------- /chapter-6/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-6/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-6/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /chapter-6/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "com.example.chapter-6" 2 | -------------------------------------------------------------------------------- /chapter-6/src/main/kotlin/com/example/plugins/Entities.kt: -------------------------------------------------------------------------------- 1 | package com.example.plugins 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class Household( 7 | val name: String, 8 | val email: String, 9 | val members: List, 10 | val deleted: Boolean = false 11 | ) 12 | 13 | val householdByNames = mutableMapOf( 14 | "Whittington" to Household( 15 | "Whittington", 16 | "info@whittington.com", 17 | listOf( 18 | "Mary Whittington", 19 | "Daniel Whittington" 20 | ) 21 | ) 22 | ) -------------------------------------------------------------------------------- /chapter-6/src/main/kotlin/com/example/plugins/Functions.kt: -------------------------------------------------------------------------------- 1 | package com.example.plugins 2 | 3 | object ReadHouseholdByName { 4 | operator fun invoke(name: String): Household? = householdByNames[name] 5 | } 6 | 7 | object UpsertHousehold { 8 | operator fun invoke(household: Household): Household? = householdByNames.put(household.name, household) 9 | } 10 | 11 | object DeleteHousehold { 12 | operator fun invoke(name: String): Household? = householdByNames.remove(name) 13 | } 14 | -------------------------------------------------------------------------------- /chapter-6/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | ktor: 2 | application: 3 | modules: 4 | - com.example.ApplicationKt.module 5 | deployment: 6 | port: 8080 7 | -------------------------------------------------------------------------------- /chapter-6/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /chapter-7/clean/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /chapter-7/clean/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") version "1.9.23" 3 | } 4 | 5 | group = "clean" 6 | version = "1.0-SNAPSHOT" 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | testImplementation(kotlin("test")) 14 | } 15 | 16 | tasks.test { 17 | useJUnitPlatform() 18 | } 19 | kotlin { 20 | jvmToolchain(21) 21 | } 22 | -------------------------------------------------------------------------------- /chapter-7/clean/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | -------------------------------------------------------------------------------- /chapter-7/clean/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-7/clean/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-7/clean/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Sep 14 06:16:55 BST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /chapter-7/clean/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0" 3 | } 4 | rootProject.name = "clean" 5 | 6 | -------------------------------------------------------------------------------- /chapter-7/clean/src/main/kotlin/entity/Contract.kt: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | data class Contract( 4 | val partyA: Party, 5 | val partyB: Party, 6 | val contractState: ContractState, 7 | ) 8 | -------------------------------------------------------------------------------- /chapter-7/clean/src/main/kotlin/entity/ContractState.kt: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | enum class ContractState { 4 | DRAFTED, 5 | UNDER_REVIEW, 6 | AGREED, 7 | REJECTED, 8 | PARTIALLY_EXERCISED, 9 | FULLY_EXERCISED, 10 | WITHDRAWN, 11 | } 12 | -------------------------------------------------------------------------------- /chapter-7/clean/src/main/kotlin/entity/Household.kt: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | data class Household( 4 | val name: String, 5 | val members: List, 6 | ) { 7 | init { 8 | 9 | require(members.isNotEmpty()) { "Household must have at least one member" } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /chapter-7/clean/src/main/kotlin/entity/Party.kt: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import java.time.Instant 4 | 5 | data class Party( 6 | val household: Household, 7 | val serviceProvided: String, 8 | val agreedAt: Instant? = null, 9 | val completedAt: Instant? = null, 10 | ) 11 | -------------------------------------------------------------------------------- /chapter-7/clean/src/main/kotlin/usecase/DraftContract.kt: -------------------------------------------------------------------------------- 1 | package usecase 2 | 3 | import entity.Contract 4 | import entity.ContractState 5 | import entity.Household 6 | import entity.Party 7 | 8 | fun draftContract( 9 | householdA: Household, 10 | householdB: Household, 11 | serviceProvidedByHouseholdA: String, 12 | serviceProvidedByHouseholdB: String, 13 | ): Contract { 14 | require(householdA != householdB) { "Parties must be from different households" } 15 | 16 | return Contract( 17 | Party( 18 | householdA, 19 | serviceProvidedByHouseholdA, 20 | ), 21 | Party( 22 | householdB, 23 | serviceProvidedByHouseholdB, 24 | ), 25 | ContractState.DRAFTED, 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /chapter-7/connect/README.md: -------------------------------------------------------------------------------- 1 | # Connect 2 | 3 | ## Package 4 | ``` 5 | ./gradlew distZip 6 | ``` 7 | 8 | -------------------------------------------------------------------------------- /chapter-7/connect/gradle.properties: -------------------------------------------------------------------------------- 1 | junitVersion=5.10.2 2 | http4kVersion=5.23.0.0 3 | http4kConnectVersion=5.17.0.2 4 | kotlinVersion=2.0.0 5 | -------------------------------------------------------------------------------- /chapter-7/connect/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-7/connect/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-7/connect/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /chapter-7/connect/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "Connect" 2 | -------------------------------------------------------------------------------- /chapter-7/connect/src/main/kotlin/connect/Connect.kt: -------------------------------------------------------------------------------- 1 | package connect 2 | 3 | import org.http4k.core.Body 4 | import org.http4k.core.HttpHandler 5 | import org.http4k.core.Method.GET 6 | import org.http4k.core.Response 7 | import org.http4k.core.Status.Companion.OK 8 | import org.http4k.core.then 9 | import org.http4k.core.with 10 | import org.http4k.filter.DebuggingFilters.PrintRequest 11 | import org.http4k.format.Jackson.auto 12 | import org.http4k.routing.bind 13 | import org.http4k.routing.path 14 | import org.http4k.routing.routes 15 | import org.http4k.server.SunHttp 16 | import org.http4k.server.asServer 17 | 18 | val householdLens = Body.auto().toLens() 19 | 20 | val app: HttpHandler = 21 | routes( 22 | "/households/{name}" bind GET to { request -> 23 | val householdName = request.path("name")!!.toString() 24 | Response(OK).with(householdLens of Household(name = householdName, emailAddress = "same.address@domain.com")) 25 | }, 26 | ) 27 | 28 | fun main() { 29 | val printingApp: HttpHandler = PrintRequest().then(app) 30 | 31 | val server = printingApp.asServer(SunHttp(9000)).start() 32 | 33 | println("Server started on " + server.port()) 34 | } 35 | -------------------------------------------------------------------------------- /chapter-7/connect/src/main/kotlin/connect/Entities.kt: -------------------------------------------------------------------------------- 1 | package connect 2 | 3 | data class Household( 4 | val name: String, 5 | val emailAddress: String, 6 | ) 7 | -------------------------------------------------------------------------------- /chapter-7/connect/src/test/kotlin/connect/ConnectTest.kt: -------------------------------------------------------------------------------- 1 | package connect 2 | 3 | import org.http4k.core.ContentType 4 | import org.http4k.core.Method.GET 5 | import org.http4k.core.Request 6 | import org.http4k.core.Response 7 | import org.http4k.core.Status.Companion.OK 8 | import org.http4k.lens.contentType 9 | import org.junit.jupiter.api.Assertions.assertEquals 10 | import org.junit.jupiter.api.Test 11 | 12 | class ConnectTest { 13 | @Test 14 | fun `Get the household from Household API`() { 15 | assertEquals( 16 | Response(OK) 17 | .contentType(ContentType.APPLICATION_JSON) 18 | .body("{\"name\":\"Whittington\",\"emailAddress\":\"same.address@domain.com\"}"), 19 | app(Request(GET, "/households/Whittington")), 20 | ) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /chapter-7/connect/src/test/kotlin/connect/HouseholdApiAction.kt: -------------------------------------------------------------------------------- 1 | package connect 2 | 3 | import org.http4k.core.Request 4 | import org.http4k.core.Response 5 | 6 | interface HouseholdApiAction { 7 | fun toRequest(): Request 8 | 9 | fun fromResponse(response: Response): R 10 | } 11 | -------------------------------------------------------------------------------- /chapter-7/connect/src/test/kotlin/connect/http/Actions.kt: -------------------------------------------------------------------------------- 1 | package connect.http 2 | 3 | import connect.Household 4 | import connect.HouseholdApiAction 5 | import org.http4k.core.Body 6 | import org.http4k.core.Method 7 | import org.http4k.core.Request 8 | import org.http4k.core.Response 9 | import org.http4k.format.Jackson.auto 10 | 11 | val householdLens = Body.auto().toLens() 12 | 13 | data class GetHousehold( 14 | val householdName: String, 15 | ) : HouseholdApiAction { 16 | override fun toRequest(): Request = Request(Method.GET, "/households/$householdName") 17 | 18 | override fun fromResponse(response: Response): Household = householdLens(response) 19 | } 20 | -------------------------------------------------------------------------------- /chapter-7/connect/src/test/kotlin/connect/http/Adapters.kt: -------------------------------------------------------------------------------- 1 | package connect.http 2 | 3 | import connect.HouseholdApiAction 4 | import org.http4k.core.Filter 5 | import org.http4k.core.HttpHandler 6 | import org.http4k.core.Uri 7 | import org.http4k.core.then 8 | import org.http4k.filter.ClientFilters.SetBaseUriFrom 9 | 10 | interface HouseholdApi { 11 | operator fun invoke(action: HouseholdApiAction): R 12 | 13 | companion object 14 | } 15 | 16 | val token = "fakeToken" 17 | 18 | fun HouseholdApi.Companion.Http(client: HttpHandler) = 19 | object : HouseholdApi { 20 | private val http = 21 | SetBaseUriFrom(Uri.of("http://localhost:9000")) 22 | .then( 23 | Filter { next -> { next(it.header("Authorization", "Bearer $token")) } }, 24 | ).then(client) 25 | 26 | override fun invoke(action: HouseholdApiAction) = action.fromResponse(http(action.toRequest())) 27 | } 28 | 29 | fun HouseholdApi.getHousehold(householdName: String) = invoke(GetHousehold(householdName)) 30 | -------------------------------------------------------------------------------- /chapter-7/connect/src/test/kotlin/connect/http/Usage.kt: -------------------------------------------------------------------------------- 1 | package connect.http 2 | 3 | import connect.Household 4 | import org.http4k.client.JavaHttpClient 5 | 6 | fun main() { 7 | val householdApi = HouseholdApi.Http(JavaHttpClient()) 8 | val household: Household = householdApi.getHousehold("Whittington") 9 | println(household) 10 | } 11 | -------------------------------------------------------------------------------- /chapter-7/fcis/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | 39 | ### Kotlin ### 40 | .kotlin 41 | -------------------------------------------------------------------------------- /chapter-7/fcis/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") version "1.9.25" 3 | kotlin("plugin.spring") version "1.9.25" 4 | id("org.springframework.boot") version "3.3.3" 5 | id("io.spring.dependency-management") version "1.1.6" 6 | } 7 | 8 | group = "fcis" 9 | version = "0.0.1-SNAPSHOT" 10 | 11 | java { 12 | toolchain { 13 | languageVersion = JavaLanguageVersion.of(21) 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | } 20 | 21 | dependencies { 22 | implementation("org.springframework.boot:spring-boot-starter") 23 | implementation("org.springframework.boot:spring-boot-starter-web") 24 | implementation("org.jetbrains.kotlin:kotlin-reflect") 25 | implementation("io.arrow-kt:arrow-core:1.2.4") 26 | testImplementation("org.springframework.boot:spring-boot-starter-test") 27 | testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") 28 | testImplementation("com.tngtech.archunit:archunit:1.3.0") 29 | testRuntimeOnly("org.junit.platform:junit-platform-launcher") 30 | } 31 | 32 | kotlin { 33 | compilerOptions { 34 | freeCompilerArgs.addAll("-Xjsr305=strict") 35 | } 36 | } 37 | 38 | tasks.withType { 39 | useJUnitPlatform() 40 | } 41 | -------------------------------------------------------------------------------- /chapter-7/fcis/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-7/fcis/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-7/fcis/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /chapter-7/fcis/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "fcis" 2 | -------------------------------------------------------------------------------- /chapter-7/fcis/src/main/kotlin/fcis/core/Common.kt: -------------------------------------------------------------------------------- 1 | package fcis.core 2 | 3 | enum class ErrorType { 4 | HOUSEHOLD_NOT_FOUND, 5 | SAME_HOUSEHOLD_IN_CONTRACT, 6 | } 7 | 8 | data class Error( 9 | val reason: String, 10 | val type: ErrorType, 11 | ) 12 | -------------------------------------------------------------------------------- /chapter-7/fcis/src/main/kotlin/fcis/core/Data.kt: -------------------------------------------------------------------------------- 1 | package fcis.core 2 | 3 | import java.time.Instant 4 | 5 | data class Contract( 6 | val partyA: Party, 7 | val partyB: Party, 8 | val contractState: ContractState, 9 | ) 10 | 11 | data class Party( 12 | val household: Household, 13 | val serviceProvided: String, 14 | val agreedAt: Instant? = null, 15 | val completedAt: Instant? = null, 16 | ) 17 | 18 | enum class ContractState { 19 | DRAFTED, 20 | UNDER_REVIEW, 21 | AGREED, 22 | REJECTED, 23 | PARTIALLY_EXERCISED, 24 | FULLY_EXERCISED, 25 | WITHDRAWN, 26 | } 27 | 28 | data class Household( 29 | val name: String, 30 | val members: List, 31 | ) { 32 | init { 33 | 34 | require(members.isNotEmpty()) { "Household must have at least one member" } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /chapter-7/fcis/src/main/kotlin/fcis/core/Functions.kt: -------------------------------------------------------------------------------- 1 | package fcis.core 2 | 3 | import arrow.core.Either 4 | 5 | fun draftContract( 6 | householdA: Household, 7 | householdB: Household, 8 | serviceProvidedByHouseholdA: String, 9 | serviceProvidedByHouseholdB: String, 10 | ): Either = 11 | if (householdA.name == householdB.name) { 12 | Either.Left( 13 | Error("Parties must be from different households", ErrorType.SAME_HOUSEHOLD_IN_CONTRACT), 14 | ) 15 | } else { 16 | Either.Right( 17 | Contract( 18 | partyA = Party(householdA, serviceProvidedByHouseholdA), 19 | partyB = Party(householdB, serviceProvidedByHouseholdB), 20 | contractState = ContractState.DRAFTED, 21 | ), 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /chapter-7/fcis/src/main/kotlin/fcis/fcis/FcisApplication.kt: -------------------------------------------------------------------------------- 1 | package fcis.fcis 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication 4 | import org.springframework.boot.runApplication 5 | 6 | @SpringBootApplication 7 | class FcisApplication 8 | 9 | fun main(args: Array) { 10 | runApplication(*args) 11 | } 12 | -------------------------------------------------------------------------------- /chapter-7/fcis/src/main/kotlin/fcis/shell/rest/Dtos.kt: -------------------------------------------------------------------------------- 1 | package fcis.shell.rest 2 | 3 | data class DraftContractRequest( 4 | val householdA: String, 5 | val householdB: String, 6 | val serviceProvidedByHouseholdA: String, 7 | val serviceProvidedByHouseholdB: String, 8 | ) 9 | 10 | data class ContractDto( 11 | val householdA: String, 12 | val householdB: String, 13 | val serviceProvidedByHouseholdA: String, 14 | val serviceProvidedByHouseholdB: String, 15 | val contractState: String, 16 | ) 17 | -------------------------------------------------------------------------------- /chapter-7/fcis/src/main/kotlin/fcis/shell/rest/Functions.kt: -------------------------------------------------------------------------------- 1 | package fcis.shell.rest 2 | 3 | import arrow.core.Either 4 | import fcis.core.Contract 5 | import fcis.core.ContractState 6 | import fcis.core.Error 7 | import fcis.core.Household 8 | import fcis.core.Party 9 | 10 | typealias HouseholdLookup = (String) -> Household? 11 | 12 | typealias ContractPersist = (Contract) -> Either 13 | 14 | class DummyContractPersist : ContractPersist { 15 | override fun invoke(p1: Contract): Either { 16 | TODO("Not yet implemented") 17 | } 18 | } 19 | 20 | fun main() { 21 | val contract = 22 | Contract( 23 | Party(Household("A", listOf("a")), "ironing"), 24 | Party(Household("B", listOf("b")), "guttering"), 25 | ContractState.DRAFTED, 26 | ) 27 | val persist = DummyContractPersist() 28 | val result = persist(contract) 29 | } 30 | -------------------------------------------------------------------------------- /chapter-7/fcis/src/main/kotlin/fcis/shell/rest/Mappings.kt: -------------------------------------------------------------------------------- 1 | package fcis.shell.rest 2 | 3 | import fcis.core.Contract 4 | import fcis.core.ErrorType 5 | import org.springframework.http.HttpStatus 6 | 7 | fun Contract.toDto(): ContractDto = 8 | ContractDto( 9 | partyA.household.name, 10 | partyB.household.name, 11 | this.partyA.serviceProvided, 12 | this.partyB.serviceProvided, 13 | this.contractState.name, 14 | ) 15 | 16 | fun ErrorType.toHttpStatus(): HttpStatus = 17 | when (this) { 18 | ErrorType.HOUSEHOLD_NOT_FOUND -> HttpStatus.NOT_FOUND 19 | ErrorType.SAME_HOUSEHOLD_IN_CONTRACT -> HttpStatus.BAD_REQUEST 20 | } 21 | -------------------------------------------------------------------------------- /chapter-7/fcis/src/main/kotlin/fcis/shell/rest/Validations.kt: -------------------------------------------------------------------------------- 1 | package fcis.shell.rest 2 | 3 | import arrow.core.Either 4 | import fcis.core.Error 5 | import fcis.core.ErrorType 6 | import fcis.core.Household 7 | 8 | fun DraftContractRequest.ensureHouseholdExist(householdLookup: HouseholdLookup): Either> { 9 | val householdARecord = householdLookup(householdA) 10 | val householdBRecord = householdLookup(householdB) 11 | 12 | return if (householdARecord == null) { 13 | Either.Left( 14 | Error("Households not found: $householdA", ErrorType.HOUSEHOLD_NOT_FOUND), 15 | ) 16 | } else if (householdBRecord == null) { 17 | Either.Left( 18 | Error("Households not found: $householdB", ErrorType.HOUSEHOLD_NOT_FOUND), 19 | ) 20 | } else { 21 | Either.Right( 22 | householdARecord to householdBRecord, 23 | ) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /chapter-7/fcis/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=fcis 2 | -------------------------------------------------------------------------------- /chapter-7/fcis/src/test/kotlin/fcis/fcis/FcisApplicationTests.kt: -------------------------------------------------------------------------------- 1 | package fcis.fcis 2 | 3 | import org.junit.jupiter.api.Test 4 | import org.springframework.boot.test.context.SpringBootTest 5 | 6 | @SpringBootTest 7 | class FcisApplicationTests { 8 | 9 | @Test 10 | fun contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter-7/fcis/src/test/kotlin/fcis/fcis/LayeredArchitectureTest.kt: -------------------------------------------------------------------------------- 1 | package fcis.fcis 2 | 3 | import com.tngtech.archunit.core.importer.ClassFileImporter 4 | import com.tngtech.archunit.library.Architectures.layeredArchitecture 5 | import kotlin.test.Test 6 | 7 | class LayeredArchitectureTest { 8 | val classes = ClassFileImporter().importPackages("fcis") 9 | 10 | @Test 11 | fun `layer dependencies are_respected`() { 12 | layeredArchitecture() 13 | .consideringAllDependencies() 14 | .layer("Imperative Shell") 15 | .definedBy("fcis.shell..") 16 | .layer("Functional Core") 17 | .definedBy("fcis.core..") 18 | .whereLayer("Imperative Shell") 19 | .mayNotBeAccessedByAnyLayer() 20 | .whereLayer("Functional Core") 21 | .mayOnlyBeAccessedByLayers("Imperative Shell") 22 | .check(classes) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter-7/hexagonal/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /chapter-7/hexagonal/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") version "1.9.25" 3 | kotlin("plugin.spring") version "1.9.25" 4 | id("org.springframework.boot") version "3.3.3" 5 | id("io.spring.dependency-management") version "1.1.6" 6 | } 7 | 8 | group = "hexagonal" 9 | version = "1.0-SNAPSHOT" 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | dependencies { 16 | implementation("org.springframework.boot:spring-boot-starter") 17 | implementation("org.springframework.boot:spring-boot-starter-web") 18 | implementation("org.jetbrains.kotlin:kotlin-reflect") 19 | testImplementation(kotlin("test")) 20 | testImplementation("org.springframework.boot:spring-boot-starter-test") 21 | testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") 22 | testRuntimeOnly("org.junit.platform:junit-platform-launcher") 23 | } 24 | 25 | tasks.test { 26 | useJUnitPlatform() 27 | } 28 | kotlin { 29 | jvmToolchain(21) 30 | } 31 | -------------------------------------------------------------------------------- /chapter-7/hexagonal/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | -------------------------------------------------------------------------------- /chapter-7/hexagonal/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-7/hexagonal/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-7/hexagonal/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Sep 14 08:46:16 BST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /chapter-7/hexagonal/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0" 3 | } 4 | rootProject.name = "hexagonal" 5 | 6 | -------------------------------------------------------------------------------- /chapter-7/hexagonal/src/main/kotlin/adapter/ContractServiceImpl.kt: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | import core.Contract 4 | import core.Household 5 | import core.port.ContractRepository 6 | import core.port.ContractService 7 | 8 | class ContractServiceImpl( 9 | private val contractRepository: ContractRepository, 10 | ) : ContractService { 11 | override fun draftContract( 12 | householdA: Household, 13 | householdB: Household, 14 | serviceProvidedByHouseholdA: String, 15 | serviceProvidedByHouseholdB: String, 16 | ): Contract = 17 | draftContract( 18 | householdA, 19 | householdB, 20 | serviceProvidedByHouseholdA, 21 | serviceProvidedByHouseholdB, 22 | ).also { contractRepository.save(it) } 23 | } 24 | -------------------------------------------------------------------------------- /chapter-7/hexagonal/src/main/kotlin/adapter/rest/DraftContractRequest.kt: -------------------------------------------------------------------------------- 1 | package adapter.rest 2 | 3 | data class DraftContractRequest( 4 | val householdA: String, 5 | val householdB: String, 6 | val serviceProvidedByHouseholdA: String, 7 | val serviceProvidedByHouseholdB: String, 8 | ) 9 | 10 | data class ContractDto( 11 | val householdA: String, 12 | val householdB: String, 13 | val serviceProvidedByHouseholdA: String, 14 | val serviceProvidedByHouseholdB: String, 15 | val contractState: String, 16 | ) 17 | -------------------------------------------------------------------------------- /chapter-7/hexagonal/src/main/kotlin/core/Contract.kt: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | data class Contract( 4 | val partyA: Party, 5 | val partyB: Party, 6 | val contractState: ContractState, 7 | ) 8 | -------------------------------------------------------------------------------- /chapter-7/hexagonal/src/main/kotlin/core/ContractState.kt: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | enum class ContractState { 4 | DRAFTED, 5 | UNDER_REVIEW, 6 | AGREED, 7 | REJECTED, 8 | PARTIALLY_EXERCISED, 9 | FULLY_EXERCISED, 10 | WITHDRAWN, 11 | } 12 | -------------------------------------------------------------------------------- /chapter-7/hexagonal/src/main/kotlin/core/Household.kt: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | data class Household( 4 | val name: String, 5 | val members: List, 6 | ) { 7 | init { 8 | 9 | require(members.isNotEmpty()) { "Household must have at least one member" } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /chapter-7/hexagonal/src/main/kotlin/core/Party.kt: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import java.time.Instant 4 | 5 | data class Party( 6 | val household: Household, 7 | val serviceProvided: String, 8 | val agreedAt: Instant? = null, 9 | val completedAt: Instant? = null, 10 | ) 11 | -------------------------------------------------------------------------------- /chapter-7/hexagonal/src/main/kotlin/core/port/ContractRepository.kt: -------------------------------------------------------------------------------- 1 | package core.port 2 | 3 | import core.Contract 4 | 5 | interface ContractRepository { 6 | fun save(contract: Contract) 7 | } 8 | -------------------------------------------------------------------------------- /chapter-7/hexagonal/src/main/kotlin/core/port/ContractService.kt: -------------------------------------------------------------------------------- 1 | package core.port 2 | 3 | import core.Contract 4 | import core.Household 5 | 6 | interface ContractService { 7 | fun draftContract( 8 | householdA: Household, 9 | householdB: Household, 10 | serviceProvidedByHouseholdA: String, 11 | serviceProvidedByHouseholdB: String, 12 | ): Contract 13 | } 14 | -------------------------------------------------------------------------------- /chapter-7/hexagonal/src/main/kotlin/core/port/HouseholdRepository.kt: -------------------------------------------------------------------------------- 1 | package core.port 2 | 3 | import core.Household 4 | 5 | interface HouseholdRepository { 6 | fun findByName(householdName: String): Household? 7 | } 8 | -------------------------------------------------------------------------------- /chapter-8/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /chapter-8/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") version "1.9.23" 3 | } 4 | 5 | group = "ddd" 6 | version = "1.0-SNAPSHOT" 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | testImplementation(kotlin("test")) 14 | } 15 | 16 | tasks.test { 17 | useJUnitPlatform() 18 | } 19 | kotlin { 20 | jvmToolchain(21) 21 | } -------------------------------------------------------------------------------- /chapter-8/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | -------------------------------------------------------------------------------- /chapter-8/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-8/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-8/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Sep 16 11:22:25 BST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /chapter-8/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0" 3 | } 4 | rootProject.name = "chapter-8" 5 | 6 | -------------------------------------------------------------------------------- /chapter-8/src/main/kotlin/Elements.kt: -------------------------------------------------------------------------------- 1 | 2 | data class Address( 3 | val line1: String, 4 | val line2: String? = null, 5 | val line3: String? = null, 6 | val postalCode: String, 7 | val city: String, 8 | val country: String 9 | ) -------------------------------------------------------------------------------- /chapter-8/src/main/kotlin/Household.kt: -------------------------------------------------------------------------------- 1 | import java.time.Instant 2 | 3 | data class Contract( 4 | val partyA: Party, 5 | val partyB: Party, 6 | val contractState: ContractState, 7 | ) 8 | 9 | data class Party( 10 | val household: Household, 11 | val serviceProvided: String, 12 | val agreedAt: Instant? = null, 13 | val completedAt: Instant? = null, 14 | ) 15 | 16 | enum class ContractState { 17 | DRAFTED, 18 | UNDER_REVIEW, 19 | AGREED, 20 | REJECTED, 21 | PARTIALLY_EXERCISED, 22 | FULLY_EXERCISED, 23 | WITHDRAWN, 24 | } 25 | 26 | data class Household( 27 | val name: String, 28 | val emailAddress: String 29 | ) 30 | -------------------------------------------------------------------------------- /chapter-9/.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # Linux start script should use lf 5 | /gradlew text eol=lf 6 | 7 | # These are Windows script files and should use crlf 8 | *.bat text eol=crlf 9 | 10 | -------------------------------------------------------------------------------- /chapter-9/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Gradle project-specific cache directory 2 | .gradle 3 | 4 | # Ignore Gradle build output directory 5 | build 6 | -------------------------------------------------------------------------------- /chapter-9/app/src/main/kotlin/Aggregate.kt: -------------------------------------------------------------------------------- 1 | import java.time.Instant 2 | import java.util.UUID 3 | 4 | data class Contract( 5 | val id: UUID, 6 | val draftedAt: Instant, 7 | val updatedAt: Instant? = null, 8 | val version: Int, 9 | val partyA: Party, 10 | val partyB: Party, 11 | ) 12 | 13 | data class Party( 14 | val householdName: String, 15 | val serviceProvided: String, 16 | val agreedAt: Instant? = null 17 | ) -------------------------------------------------------------------------------- /chapter-9/app/src/main/kotlin/EventSourcing.kt: -------------------------------------------------------------------------------- 1 | class Executor( 2 | transact: (A, E) -> Transaction 3 | ) { 4 | private val events = mutableListOf() 5 | private var aggregate: A? = null 6 | 7 | fun execute(event: E) { 8 | 9 | } 10 | 11 | fun toTransaction(): Transaction? { 12 | return aggregate?.let { Transaction(events, it) } 13 | } 14 | } 15 | 16 | data class Transaction( 17 | val events: List, 18 | val aggregate: A 19 | ) 20 | -------------------------------------------------------------------------------- /chapter-9/app/src/main/kotlin/EventStore.kt: -------------------------------------------------------------------------------- 1 | 2 | class EventStore { 3 | private val eventsByKey = mutableMapOf>() 4 | fun append(id: K, payload: E) { 5 | eventsByKey.merge(id, listOf(payload)) { t1, t2 -> t1 + t2 } 6 | } 7 | fun get(id: K): List? = eventsByKey[id] 8 | fun get(predicate: (E) -> Boolean): List = eventsByKey.flatMap { 9 | it.value.filter(predicate) 10 | } 11 | } -------------------------------------------------------------------------------- /chapter-9/app/src/main/kotlin/Queries.kt: -------------------------------------------------------------------------------- 1 | import java.util.UUID 2 | 3 | data class CurrentContractQuery( 4 | val contractId: UUID 5 | ) 6 | 7 | fun CurrentContractQuery.handle( 8 | eventStore: EventStore 9 | ): Contract? = eventStore.get(contractId)?.play() -------------------------------------------------------------------------------- /chapter-9/docs/demo-720.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-9/docs/demo-720.mp4 -------------------------------------------------------------------------------- /chapter-9/gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | # This file was generated by the Gradle 'init' task. 2 | # https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format 3 | 4 | [versions] 5 | guava = "33.0.0-jre" 6 | junit-jupiter-engine = "5.10.2" 7 | 8 | [libraries] 9 | guava = { module = "com.google.guava:guava", version.ref = "guava" } 10 | junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit-jupiter-engine" } 11 | 12 | [plugins] 13 | jvm = { id = "org.jetbrains.kotlin.jvm", version = "2.0.0" } 14 | -------------------------------------------------------------------------------- /chapter-9/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chapter-9/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-9/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /chapter-9/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.8/userguide/multi_project_builds.html in the Gradle documentation. 6 | */ 7 | 8 | plugins { 9 | // Apply the foojay-resolver plugin to allow automatic download of JDKs 10 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 11 | } 12 | 13 | rootProject.name = "chapter-9" 14 | include("app") 15 | -------------------------------------------------------------------------------- /chatper-10/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /chatper-10/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | alias(libs.plugins.jvm) 4 | 5 | } 6 | 7 | group = "org.example" 8 | version = "1.0-SNAPSHOT" 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | testImplementation(platform("org.junit:junit-bom:5.10.0")) 16 | testImplementation("org.junit.jupiter:junit-jupiter") 17 | } 18 | 19 | tasks.test { 20 | useJUnitPlatform() 21 | } -------------------------------------------------------------------------------- /chatper-10/docs/demo-720.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chatper-10/docs/demo-720.mp4 -------------------------------------------------------------------------------- /chatper-10/gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | # This file was generated by the Gradle 'init' task. 2 | # https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format 3 | 4 | [versions] 5 | guava = "33.0.0-jre" 6 | junit-jupiter-engine = "5.10.2" 7 | 8 | [libraries] 9 | guava = { module = "com.google.guava:guava", version.ref = "guava" } 10 | junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit-jupiter-engine" } 11 | 12 | [plugins] 13 | jvm = { id = "org.jetbrains.kotlin.jvm", version = "2.0.0" } 14 | -------------------------------------------------------------------------------- /chatper-10/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Software-Architecture-with-Kotlin/c83cfa6d964bd6f4f09e3fd7839314f99678f16c/chatper-10/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chatper-10/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Jul 29 10:26:02 BST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /chatper-10/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "chatper-10" 2 | 3 | -------------------------------------------------------------------------------- /chatper-10/src/main/kotlin/Household.kt: -------------------------------------------------------------------------------- 1 | data class Household( 2 | val version: Int, 3 | val name: String, 4 | val email: String, 5 | ) -------------------------------------------------------------------------------- /chatper-10/src/main/kotlin/HouseholdRepository.kt: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.ConcurrentHashMap 2 | import java.util.concurrent.ConcurrentMap 3 | 4 | class HouseholdRepository { 5 | private val values: ConcurrentMap = ConcurrentHashMap() 6 | 7 | fun create( 8 | key: String, 9 | callback: () -> Household 10 | ): Household { 11 | val household = callback() 12 | val result = values.putIfAbsent(key, household) 13 | return result ?: household 14 | } 15 | 16 | fun update( 17 | key: String, 18 | callback: (Household) -> Household 19 | ): Household? = values.computeIfPresent(key) { _, existing -> 20 | callback(existing).let { updated -> 21 | if (updated.version == existing.version + 1) { 22 | updated 23 | } else { 24 | existing 25 | } 26 | } 27 | } 28 | 29 | fun get(key: String): Household? = values[key] 30 | } -------------------------------------------------------------------------------- /chatper-10/src/main/kotlin/Usage.kt: -------------------------------------------------------------------------------- 1 | fun main() { 2 | val repo = HouseholdRepository() 3 | 4 | val name = "Whittington" 5 | val email1 = "info@whittington.com" 6 | val email2a = "query@whittington.com" 7 | val email2b = "contact@whittington.com" 8 | val household1 = Household(0, name, email1) 9 | 10 | repo.create(name) { household1 } 11 | 12 | repo.update(name) { household1.copy(version = 1, email = email2a)} 13 | repo.update(name) { household1.copy(version = 1, email = email2b)} 14 | 15 | repo.get(name)?.also { 16 | println("${it.version}, ${it.email}") 17 | } 18 | } 19 | --------------------------------------------------------------------------------