├── corehr ├── schema │ ├── gradle.properties │ └── src │ │ └── main │ │ └── resources │ │ └── db │ │ └── changelog │ │ └── corehr │ │ ├── db.changelog-initial-schema.yaml │ │ ├── db.changelog-initial-schema-and-data.yaml │ │ ├── initial_schema.sql │ │ └── data.sql ├── docs │ ├── hexagonal_codes.png │ ├── hexagonal_modules.png │ └── architecture_guide.puml ├── exception │ ├── build.gradle.kts │ ├── gradle.properties │ └── src │ │ └── main │ │ ├── resources │ │ └── team │ │ │ └── flex │ │ │ └── training │ │ │ └── corehr │ │ │ └── exception │ │ │ ├── message_en.properties │ │ │ └── message_ko.properties │ │ └── kotlin │ │ └── team │ │ └── flex │ │ └── module │ │ └── sample │ │ └── corehr │ │ └── exception │ │ ├── CompanyNotFoundException.kt │ │ ├── EmployeeNotFoundException.kt │ │ ├── JobRoleNotFoundException.kt │ │ └── DepartmentNotFoundException.kt ├── gradle.properties ├── model │ ├── build.gradle.kts │ ├── gradle.properties │ └── src │ │ └── main │ │ └── kotlin │ │ └── team │ │ └── flex │ │ └── module │ │ └── sample │ │ └── corehr │ │ ├── AuditProps.kt │ │ ├── company │ │ ├── CompanyModel.kt │ │ ├── Company.kt │ │ ├── jobrole │ │ │ ├── JobRole.kt │ │ │ ├── JobRoleModel.kt │ │ │ └── JobRoleIdentity.kt │ │ ├── CompanyIdentity.kt │ │ └── department │ │ │ ├── Department.kt │ │ │ ├── DepartmentIdentity.kt │ │ │ └── DepartmentModel.kt │ │ └── employee │ │ ├── Employee.kt │ │ ├── EmployeeIdentity.kt │ │ └── EmployeeModel.kt ├── api │ ├── gradle.properties │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── spring │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── kotlin │ │ │ └── team │ │ │ └── flex │ │ │ └── module │ │ │ └── sample │ │ │ └── corehr │ │ │ └── employee │ │ │ ├── dto │ │ │ └── EmployeeResponse.kt │ │ │ ├── EmployeeApiAutoConfiguration.kt │ │ │ └── EmployeeApiController.kt │ └── build.gradle.kts ├── service │ ├── gradle.properties │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── spring │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── kotlin │ │ └── team │ │ └── flex │ │ └── module │ │ └── sample │ │ └── corehr │ │ ├── company │ │ ├── CompanyAutoConfiguration.kt │ │ ├── jobrole │ │ │ ├── JobRoleAutoConfiguration.kt │ │ │ └── JobRoleLookUpService.kt │ │ ├── department │ │ │ ├── DepartmentAutoConfiguration.kt │ │ │ └── DepartmentLookUpService.kt │ │ └── CompanyLookUpService.kt │ │ └── employee │ │ ├── EmployeeAutoConfiguration.kt │ │ └── EmployeeLookUpService.kt ├── infrastructure │ ├── gradle.properties │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ └── team │ │ └── flex │ │ └── module │ │ └── sample │ │ └── corehr │ │ ├── company │ │ ├── repository │ │ │ └── CompanyRepository.kt │ │ ├── jobrole │ │ │ └── repository │ │ │ │ └── JobRoleRepository.kt │ │ └── department │ │ │ └── repository │ │ │ └── DepartmentRepository.kt │ │ └── employee │ │ └── repository │ │ └── EmployeeRepository.kt ├── repository-jdbc │ ├── gradle.properties │ ├── src │ │ ├── main │ │ │ ├── resources │ │ │ │ └── META-INF │ │ │ │ │ └── spring │ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── kotlin │ │ │ │ └── team │ │ │ │ └── flex │ │ │ │ └── module │ │ │ │ └── sample │ │ │ │ └── corehr │ │ │ │ ├── company │ │ │ │ ├── repository │ │ │ │ │ ├── CompanyRepositoryAutoConfiguration.kt │ │ │ │ │ ├── CompanyEntity.kt │ │ │ │ │ └── CompanyJdbcRepository.kt │ │ │ │ ├── jobrole │ │ │ │ │ └── repository │ │ │ │ │ │ ├── JobRoleRepositoryAutoConfiguration.kt │ │ │ │ │ │ ├── JobRoleEntity.kt │ │ │ │ │ │ └── JobRoleJdbcRepository.kt │ │ │ │ └── department │ │ │ │ │ └── repository │ │ │ │ │ ├── DepartmentRepositoryAutoConfiguration.kt │ │ │ │ │ ├── DepartmentEntity.kt │ │ │ │ │ └── DepartmentJdbcRepository.kt │ │ │ │ └── employee │ │ │ │ └── repository │ │ │ │ ├── EmployeeRepositoryAutoConfiguration.kt │ │ │ │ ├── EmployeeEntity.kt │ │ │ │ └── EmployeeJdbcRepository.kt │ │ └── integrationTest │ │ │ ├── resources │ │ │ └── application.yml │ │ │ └── kotlin │ │ │ └── team │ │ │ └── flex │ │ │ └── module │ │ │ └── sample │ │ │ └── corehr │ │ │ ├── TestApplication.kt │ │ │ ├── company │ │ │ ├── CompanyRepositoryIntegrationTest.kt │ │ │ ├── department │ │ │ │ └── DepartmentRepositoryIntegrationTest.kt │ │ │ └── jobrole │ │ │ │ └── JobRoleRepositoryIntegrationTest.kt │ │ │ └── employee │ │ │ └── EmployeeRepositoryIntegrationTest.kt │ └── build.gradle.kts ├── application-api │ ├── gradle.properties │ ├── build.gradle.kts │ └── src │ │ ├── main │ │ ├── kotlin │ │ │ └── team │ │ │ │ └── flex │ │ │ │ └── module │ │ │ │ └── sample │ │ │ │ └── corehr │ │ │ │ └── application │ │ │ │ ├── CoreHrApplication.kt │ │ │ │ └── config │ │ │ │ └── SpringDocConfiguration.kt │ │ └── resources │ │ │ └── application.yml │ │ └── integrationTest │ │ └── kotlin │ │ └── team │ │ └── flex │ │ └── module │ │ └── sample │ │ └── corehr │ │ └── application │ │ └── IntegrationTest.kt └── README.md ├── payroll ├── schema │ ├── gradle.properties │ └── src │ │ └── main │ │ └── resources │ │ └── db │ │ └── changelog │ │ └── payroll │ │ ├── data.sql │ │ ├── db.changelog-initial-schema.yaml │ │ ├── db.changelog-initial-schema-and-data.yaml │ │ └── initial_schema.sql ├── gradle.properties ├── exception │ ├── build.gradle.kts │ └── gradle.properties ├── api │ ├── gradle.properties │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── spring │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── kotlin │ │ │ └── team │ │ │ └── flex │ │ │ └── module │ │ │ └── sample │ │ │ └── payroll │ │ │ ├── dto │ │ │ ├── PayRequest.kt │ │ │ ├── PayrollInfoRequest.kt │ │ │ ├── PayrollInfoResponse.kt │ │ │ └── PayrollHistoryResponse.kt │ │ │ ├── PayrollApiAutoConfiguration.kt │ │ │ └── PayrollApiController.kt │ └── build.gradle.kts ├── model │ ├── gradle.properties │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ └── team │ │ └── flex │ │ └── module │ │ └── sample │ │ └── payroll │ │ ├── PayrollHistory.kt │ │ ├── PayrollIdentity.kt │ │ ├── Payroll.kt │ │ └── PayrollModel.kt ├── service │ ├── gradle.properties │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── spring │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── kotlin │ │ │ └── team │ │ │ └── flex │ │ │ └── module │ │ │ └── sample │ │ │ └── payroll │ │ │ ├── PayrollAutoConfiguration.kt │ │ │ └── PayrollService.kt │ └── build.gradle.kts ├── infrastructure │ ├── gradle.properties │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ └── team │ │ └── flex │ │ └── module │ │ └── sample │ │ └── payroll │ │ └── repository │ │ ├── PayrollRepository.kt │ │ └── PayrollHistoryRepository.kt ├── repository-jdbc │ ├── gradle.properties │ ├── src │ │ ├── main │ │ │ ├── resources │ │ │ │ └── META-INF │ │ │ │ │ └── spring │ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── kotlin │ │ │ │ └── team │ │ │ │ └── flex │ │ │ │ └── module │ │ │ │ └── sample │ │ │ │ └── payroll │ │ │ │ ├── history │ │ │ │ ├── PayrollHistoryRepositoryAutoConfiguration.kt │ │ │ │ ├── PayrollHistoryEntity.kt │ │ │ │ └── PayrollHistoryJdbcRepository.kt │ │ │ │ ├── PayrollRepositoryAutoConfiguration.kt │ │ │ │ ├── PayrollEntity.kt │ │ │ │ └── PayrollJdbcRepository.kt │ │ └── integrationTest │ │ │ └── resources │ │ │ └── application.yml │ └── build.gradle.kts └── application-api │ ├── gradle.properties │ ├── build.gradle.kts │ └── src │ └── main │ ├── kotlin │ └── team │ │ └── flex │ │ └── module │ │ └── sample │ │ └── corehr │ │ └── application │ │ ├── PayrollApplication.kt │ │ └── config │ │ └── SpringDocConfiguration.kt │ └── resources │ └── application.yml ├── config └── detekt │ └── detekt.yml ├── .sdkmanrc ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── libs.versions.toml ├── application-api ├── gradle.properties ├── src │ ├── main │ │ ├── resources │ │ │ ├── db │ │ │ │ └── changelog │ │ │ │ │ └── db.changelog-master.yaml │ │ │ └── application.yml │ │ └── kotlin │ │ │ └── team │ │ │ └── flex │ │ │ └── module │ │ │ └── sample │ │ │ └── application │ │ │ └── SampleApplication.kt │ └── integrationTest │ │ └── kotlin │ │ └── team │ │ └── flex │ │ └── module │ │ └── sample │ │ └── application │ │ └── IntegrationTest.kt └── build.gradle.kts ├── .editorconfig ├── gradle.properties ├── .gitignore ├── settings.gradle.kts ├── README.md ├── .github └── workflows │ └── ci.yml ├── gradlew.bat └── gradlew /corehr/schema/gradle.properties: -------------------------------------------------------------------------------- 1 | type=kotlin 2 | group=team.flex.module.sample.corehr 3 | -------------------------------------------------------------------------------- /payroll/schema/gradle.properties: -------------------------------------------------------------------------------- 1 | type=kotlin 2 | group=team.flex.module.sample.payroll 3 | -------------------------------------------------------------------------------- /config/detekt/detekt.yml: -------------------------------------------------------------------------------- 1 | 2 | # We should know what we do 3 | performance: 4 | SpreadOperator: 5 | active: false 6 | -------------------------------------------------------------------------------- /corehr/docs/hexagonal_codes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flex-public/hexagonal-module-sample/HEAD/corehr/docs/hexagonal_codes.png -------------------------------------------------------------------------------- /corehr/exception/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | dependencies { 6 | } 7 | -------------------------------------------------------------------------------- /corehr/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 flex Inc. - All Rights Reserved. 3 | # 4 | group=team.flex.module.sample.corehr 5 | -------------------------------------------------------------------------------- /corehr/model/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | dependencies { 6 | } 7 | -------------------------------------------------------------------------------- /payroll/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 flex Inc. - All Rights Reserved. 3 | # 4 | group=team.flex.module.sample.payroll 5 | -------------------------------------------------------------------------------- /.sdkmanrc: -------------------------------------------------------------------------------- 1 | # Enable auto-env through the sdkman_auto_env config 2 | # Add key=value pairs of SDKs to use below 3 | java=21.0.7-amzn 4 | -------------------------------------------------------------------------------- /corehr/docs/hexagonal_modules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flex-public/hexagonal-module-sample/HEAD/corehr/docs/hexagonal_modules.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flex-public/hexagonal-module-sample/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /payroll/exception/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | dependencies { 6 | } 7 | -------------------------------------------------------------------------------- /corehr/model/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 flex Inc. - All Rights Reserved. 3 | # 4 | type=kotlin-lib 5 | group=team.flex.module.sample.corehr 6 | -------------------------------------------------------------------------------- /corehr/api/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 flex Inc. - All Rights Reserved. 3 | # 4 | type=kotlin-boot-mvc 5 | group=team.flex.module.sample.corehr 6 | -------------------------------------------------------------------------------- /corehr/service/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 flex Inc. - All Rights Reserved. 3 | # 4 | type=kotlin-boot 5 | group=team.flex.module.sample.corehr 6 | -------------------------------------------------------------------------------- /payroll/api/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 flex Inc. - All Rights Reserved. 3 | # 4 | type=kotlin-boot-mvc 5 | group=team.flex.module.sample.payroll 6 | -------------------------------------------------------------------------------- /payroll/model/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 flex Inc. - All Rights Reserved. 3 | # 4 | type=kotlin-lib 5 | group=team.flex.module.sample.payroll 6 | -------------------------------------------------------------------------------- /payroll/service/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 flex Inc. - All Rights Reserved. 3 | # 4 | type=kotlin-boot 5 | group=team.flex.module.sample.payroll 6 | -------------------------------------------------------------------------------- /corehr/exception/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 flex Inc. - All Rights Reserved. 3 | # 4 | 5 | type=kotlin-lib 6 | group=team.flex.module.sample.corehr 7 | -------------------------------------------------------------------------------- /corehr/infrastructure/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 flex Inc. - All Rights Reserved. 3 | # 4 | 5 | type=kotlin 6 | group=team.flex.module.sample.corehr 7 | -------------------------------------------------------------------------------- /payroll/exception/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 flex Inc. - All Rights Reserved. 3 | # 4 | 5 | type=kotlin-lib 6 | group=team.flex.module.sample.payroll 7 | -------------------------------------------------------------------------------- /payroll/infrastructure/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 flex Inc. - All Rights Reserved. 3 | # 4 | 5 | type=kotlin 6 | group=team.flex.module.sample.payroll 7 | -------------------------------------------------------------------------------- /corehr/infrastructure/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | dependencies { 6 | api(project(":corehr:model")) 7 | } 8 | -------------------------------------------------------------------------------- /payroll/api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | team.flex.module.sample.payroll.PayrollApiAutoConfiguration 2 | -------------------------------------------------------------------------------- /payroll/infrastructure/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | dependencies { 6 | api(project(":payroll:model")) 7 | } 8 | -------------------------------------------------------------------------------- /payroll/service/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | team.flex.module.sample.payroll.PayrollAutoConfiguration 2 | -------------------------------------------------------------------------------- /corehr/api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | team.flex.module.sample.corehr.employee.EmployeeApiAutoConfiguration 2 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 flex Inc. - All Rights Reserved. 3 | # 4 | type=kotlin-boot-jdbc-repository 5 | group=team.flex.module.sample.corehr 6 | -------------------------------------------------------------------------------- /payroll/repository-jdbc/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 flex Inc. - All Rights Reserved. 3 | # 4 | type=kotlin-boot-jdbc-repository 5 | group=team.flex.module.sample.payroll 6 | -------------------------------------------------------------------------------- /payroll/model/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | dependencies { 6 | api(project(":corehr:model")) 7 | api(project(":corehr:exception")) 8 | } 9 | -------------------------------------------------------------------------------- /application-api/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 flex Inc. - All Rights Reserved. 3 | # 4 | type=kotlin-boot-mvc-application 5 | label=docker,liquibase-script-test 6 | group=team.flex.module.sample.corehr 7 | -------------------------------------------------------------------------------- /corehr/exception/src/main/resources/team/flex/training/corehr/exception/message_en.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 flex Inc. - All Rights Reserved. 3 | # 4 | 5 | team.flex.module.sample.corehr.COREHR_404_000= 6 | -------------------------------------------------------------------------------- /corehr/exception/src/main/resources/team/flex/training/corehr/exception/message_ko.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 flex Inc. - All Rights Reserved. 3 | # 4 | 5 | team.flex.module.sample.corehr.COREHR_404_000= 6 | -------------------------------------------------------------------------------- /payroll/api/src/main/kotlin/team/flex/module/sample/payroll/dto/PayRequest.kt: -------------------------------------------------------------------------------- 1 | package team.flex.module.sample.payroll.dto 2 | 3 | class PayRequest( 4 | val employeeId: Long, 5 | val companyId: Long, 6 | ) 7 | -------------------------------------------------------------------------------- /corehr/application-api/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 flex Inc. - All Rights Reserved. 3 | # 4 | type=kotlin-boot-mvc-application 5 | label=docker,liquibase-script-test 6 | group=team.flex.module.sample.corehr 7 | -------------------------------------------------------------------------------- /payroll/application-api/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024 flex Inc. - All Rights Reserved. 3 | # 4 | type=kotlin-boot-mvc-application 5 | label=docker,liquibase-script-test 6 | group=team.flex.module.sample.payroll 7 | -------------------------------------------------------------------------------- /payroll/repository-jdbc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | team.flex.module.sample.payroll.PayrollRepositoryAutoConfiguration 2 | team.flex.module.sample.payroll.history.PayrollHistoryRepositoryAutoConfiguration 3 | -------------------------------------------------------------------------------- /corehr/exception/src/main/kotlin/team/flex/module/sample/corehr/exception/CompanyNotFoundException.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.exception 6 | 7 | class CompanyNotFoundException : RuntimeException() 8 | -------------------------------------------------------------------------------- /corehr/exception/src/main/kotlin/team/flex/module/sample/corehr/exception/EmployeeNotFoundException.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.exception 6 | 7 | class EmployeeNotFoundException : RuntimeException() 8 | -------------------------------------------------------------------------------- /corehr/exception/src/main/kotlin/team/flex/module/sample/corehr/exception/JobRoleNotFoundException.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.exception 6 | 7 | class JobRoleNotFoundException : RuntimeException() 8 | -------------------------------------------------------------------------------- /corehr/service/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | dependencies { 6 | api(project(":corehr:model")) 7 | implementation(project(":corehr:exception")) 8 | implementation(project(":corehr:infrastructure")) 9 | } 10 | -------------------------------------------------------------------------------- /payroll/api/src/main/kotlin/team/flex/module/sample/payroll/dto/PayrollInfoRequest.kt: -------------------------------------------------------------------------------- 1 | package team.flex.module.sample.payroll.dto 2 | 3 | class PayrollInfoRequest( 4 | val employeeId: Long, 5 | val companyId: Long, 6 | val payday: Int, 7 | val payrollAmount: Long, 8 | ) 9 | -------------------------------------------------------------------------------- /payroll/schema/src/main/resources/db/changelog/payroll/data.sql: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | insert into `payroll` (id, employee_id, company_id, payday, payroll_amount, created_at, updated_at) values (1, 1, 1, 25, 100000000, now(), now()); 6 | -------------------------------------------------------------------------------- /corehr/exception/src/main/kotlin/team/flex/module/sample/corehr/exception/DepartmentNotFoundException.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.exception 6 | 7 | class DepartmentNotFoundException : RuntimeException() 8 | -------------------------------------------------------------------------------- /payroll/service/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | dependencies { 6 | api(project(":payroll:model")) 7 | implementation(project(":payroll:exception")) 8 | implementation(project(":payroll:infrastructure")) 9 | } 10 | -------------------------------------------------------------------------------- /application-api/src/main/resources/db/changelog/db.changelog-master.yaml: -------------------------------------------------------------------------------- 1 | databaseChangeLog: 2 | - include: 3 | file: classpath:/db/changelog/corehr/db.changelog-initial-schema-and-data.yaml 4 | - include: 5 | file: classpath:/db/changelog/payroll/db.changelog-initial-schema-and-data.yaml 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{kt,kts}] 2 | ij_kotlin_allow_trailing_comma = true 3 | ij_kotlin_allow_trailing_comma_on_call_site = true 4 | ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL 5 | ij_kotlin_name_count_to_use_star_import = 999 6 | ij_kotlin_name_count_to_use_star_import_for_members = 999 7 | ij_continuation_indent_size = 4 8 | -------------------------------------------------------------------------------- /corehr/api/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | dependencies { 6 | implementation(project(":corehr:exception")) 7 | implementation(project(":corehr:service")) 8 | 9 | integrationTestImplementation("org.springframework.security:spring-security-test") 10 | } 11 | -------------------------------------------------------------------------------- /corehr/model/src/main/kotlin/team/flex/module/sample/corehr/AuditProps.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr 6 | 7 | import java.time.Instant 8 | 9 | interface AuditProps { 10 | val createdAt: Instant 11 | val updatedAt: Instant 12 | } 13 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | #=============================================================================== 2 | # Copyright 2021 flex Inc. - All Rights Reserved. 3 | #=============================================================================== 4 | 5 | group=team.flex.module.sample 6 | version=3.0.0-SNAPSHOT 7 | 8 | kotlin.code.style=official 9 | -------------------------------------------------------------------------------- /payroll/api/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | dependencies { 6 | implementation(project(":payroll:exception")) 7 | implementation(project(":payroll:service")) 8 | 9 | integrationTestImplementation("org.springframework.security:spring-security-test") 10 | } 11 | -------------------------------------------------------------------------------- /payroll/model/src/main/kotlin/team/flex/module/sample/payroll/PayrollHistory.kt: -------------------------------------------------------------------------------- 1 | package team.flex.module.sample.payroll 2 | 3 | import java.time.Instant 4 | 5 | data class PayrollHistory( 6 | val employeeId: Long, 7 | val companyId: Long, 8 | val payrollId: Long, 9 | val payDatetime: Instant, 10 | val payrollAmount: Long, 11 | ) 12 | -------------------------------------------------------------------------------- /corehr/api/src/main/kotlin/team/flex/module/sample/corehr/employee/dto/EmployeeResponse.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.employee.dto 6 | 7 | class EmployeeResponse( 8 | val employeeId: Long, 9 | val employeeNumber: String, 10 | val employeeName: String, 11 | ) 12 | -------------------------------------------------------------------------------- /payroll/api/src/main/kotlin/team/flex/module/sample/payroll/dto/PayrollInfoResponse.kt: -------------------------------------------------------------------------------- 1 | package team.flex.module.sample.payroll.dto 2 | 3 | import java.time.Instant 4 | 5 | class PayrollInfoResponse( 6 | val employeeId: Long, 7 | val companyId: Long, 8 | val payday: Int, 9 | val payrollAmount: Long, 10 | val createdAt: Instant, 11 | val updatedAt: Instant, 12 | ) 13 | -------------------------------------------------------------------------------- /corehr/service/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | team.flex.module.sample.corehr.company.department.DepartmentAutoConfiguration 2 | team.flex.module.sample.corehr.company.jobrole.JobRoleAutoConfiguration 3 | team.flex.module.sample.corehr.company.CompanyAutoConfiguration 4 | team.flex.module.sample.corehr.employee.EmployeeAutoConfiguration 5 | -------------------------------------------------------------------------------- /corehr/model/src/main/kotlin/team/flex/module/sample/corehr/company/CompanyModel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company 6 | 7 | import team.flex.module.sample.corehr.AuditProps 8 | 9 | interface CompanyProps { 10 | val name: String 11 | } 12 | 13 | interface CompanyModel : 14 | CompanyIdentity, 15 | CompanyProps, 16 | AuditProps 17 | -------------------------------------------------------------------------------- /corehr/model/src/main/kotlin/team/flex/module/sample/corehr/company/Company.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company 6 | 7 | import java.time.Instant 8 | 9 | data class Company( 10 | override val companyId: Long, 11 | override val name: String, 12 | override val createdAt: Instant, 13 | override val updatedAt: Instant, 14 | ) : CompanyModel 15 | -------------------------------------------------------------------------------- /payroll/service/src/main/kotlin/team/flex/module/sample/payroll/PayrollAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.payroll 6 | 7 | import org.springframework.boot.autoconfigure.AutoConfiguration 8 | import org.springframework.context.annotation.Import 9 | 10 | @Import(PayrollServiceImpl::class) 11 | @AutoConfiguration 12 | class PayrollAutoConfiguration 13 | -------------------------------------------------------------------------------- /payroll/model/src/main/kotlin/team/flex/module/sample/payroll/PayrollIdentity.kt: -------------------------------------------------------------------------------- 1 | package team.flex.module.sample.payroll 2 | 3 | interface PayrollIdentity { 4 | companion object 5 | 6 | val payrollId: Long 7 | } 8 | 9 | internal data class SimplePayrollIdentity( 10 | override val payrollId: Long, 11 | ) : PayrollIdentity 12 | 13 | fun PayrollIdentity.Companion.of(payrollId: Long): PayrollIdentity = SimplePayrollIdentity(payrollId) 14 | -------------------------------------------------------------------------------- /corehr/schema/src/main/resources/db/changelog/corehr/db.changelog-initial-schema.yaml: -------------------------------------------------------------------------------- 1 | databaseChangeLog: 2 | - changeSet: 3 | id: 1734418328776-1 4 | author: flex-team 5 | changes: 6 | - sqlFile: 7 | dbms: mysql 8 | encoding: UTF-8 9 | endDelimiter: ';' 10 | path: db/changelog/corehr/initial_schema.sql 11 | splitStatements: true 12 | stripComments: false 13 | -------------------------------------------------------------------------------- /payroll/schema/src/main/resources/db/changelog/payroll/db.changelog-initial-schema.yaml: -------------------------------------------------------------------------------- 1 | databaseChangeLog: 2 | - changeSet: 3 | id: 1734418328776-1 4 | author: flex-team 5 | changes: 6 | - sqlFile: 7 | dbms: mysql 8 | encoding: UTF-8 9 | endDelimiter: ';' 10 | path: db/changelog/payroll/initial_schema.sql 11 | splitStatements: true 12 | stripComments: false 13 | -------------------------------------------------------------------------------- /payroll/api/src/main/kotlin/team/flex/module/sample/payroll/PayrollApiAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.payroll 6 | 7 | import org.springframework.boot.autoconfigure.AutoConfiguration 8 | import org.springframework.context.annotation.Import 9 | 10 | @Import( 11 | PayrollApiController::class, 12 | ) 13 | @AutoConfiguration 14 | class PayrollApiAutoConfiguration 15 | -------------------------------------------------------------------------------- /payroll/model/src/main/kotlin/team/flex/module/sample/payroll/Payroll.kt: -------------------------------------------------------------------------------- 1 | package team.flex.module.sample.payroll 2 | 3 | import java.time.Instant 4 | 5 | data class Payroll( 6 | override val id: Long, 7 | override val employeeId: Long, 8 | override val companyId: Long, 9 | override val payday: Int, 10 | override val payrollAmount: Long, 11 | override val createdAt: Instant, 12 | override val updatedAt: Instant, 13 | ) : PayrollModel 14 | -------------------------------------------------------------------------------- /corehr/api/src/main/kotlin/team/flex/module/sample/corehr/employee/EmployeeApiAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.employee 6 | 7 | import org.springframework.boot.autoconfigure.AutoConfiguration 8 | import org.springframework.context.annotation.Import 9 | 10 | @Import( 11 | EmployeeApiController::class, 12 | ) 13 | @AutoConfiguration 14 | class EmployeeApiAutoConfiguration 15 | -------------------------------------------------------------------------------- /corehr/application-api/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | dependencies { 6 | implementation(project(":corehr:schema")) 7 | implementation(project(":corehr:api")) 8 | implementation(project(":corehr:repository-jdbc")) 9 | 10 | implementation("org.testcontainers:mysql") 11 | runtimeOnly("com.mysql:mysql-connector-j") { 12 | exclude(group = "com.google.protobuf", module = "protobuf-java") 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | team.flex.module.sample.corehr.company.department.repository.DepartmentRepositoryAutoConfiguration 2 | team.flex.module.sample.corehr.company.jobrole.repository.JobRoleRepositoryAutoConfiguration 3 | team.flex.module.sample.corehr.company.repository.CompanyRepositoryAutoConfiguration 4 | team.flex.module.sample.corehr.employee.repository.EmployeeRepositoryAutoConfiguration 5 | 6 | -------------------------------------------------------------------------------- /corehr/model/src/main/kotlin/team/flex/module/sample/corehr/company/jobrole/JobRole.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.jobrole 6 | 7 | import java.time.Instant 8 | 9 | data class JobRole( 10 | override val jobRoleId: Long, 11 | override val companyId: Long, 12 | override val name: String, 13 | override val createdAt: Instant, 14 | override val updatedAt: Instant, 15 | ) : JobRoleModel 16 | -------------------------------------------------------------------------------- /payroll/application-api/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | dependencies { 6 | implementation(project(":payroll:schema")) 7 | implementation(project(":payroll:api")) 8 | implementation(project(":payroll:repository-jdbc")) 9 | 10 | implementation("org.testcontainers:mysql") 11 | runtimeOnly("com.mysql:mysql-connector-j") { 12 | exclude(group = "com.google.protobuf", module = "protobuf-java") 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /corehr/infrastructure/src/main/kotlin/team/flex/module/sample/corehr/company/repository/CompanyRepository.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.repository 6 | 7 | import team.flex.module.sample.corehr.company.CompanyIdentity 8 | import team.flex.module.sample.corehr.company.CompanyModel 9 | 10 | interface CompanyRepository { 11 | fun findByCompanyIdentity(companyIdentity: CompanyIdentity): CompanyModel? 12 | } 13 | -------------------------------------------------------------------------------- /payroll/repository-jdbc/src/main/kotlin/team/flex/module/sample/payroll/history/PayrollHistoryRepositoryAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.payroll.history 6 | 7 | import org.springframework.boot.autoconfigure.AutoConfiguration 8 | import org.springframework.context.annotation.Import 9 | 10 | @Import(PayrollHistoryRepositoryImpl::class) 11 | @AutoConfiguration 12 | class PayrollHistoryRepositoryAutoConfiguration 13 | -------------------------------------------------------------------------------- /corehr/model/src/main/kotlin/team/flex/module/sample/corehr/employee/Employee.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.employee 6 | 7 | import java.time.Instant 8 | 9 | data class Employee( 10 | override val employeeId: Long, 11 | override val companyId: Long, 12 | override val employeeNumber: String, 13 | override val name: String, 14 | override val createdAt: Instant, 15 | override val updatedAt: Instant, 16 | ) : EmployeeModel 17 | -------------------------------------------------------------------------------- /corehr/model/src/main/kotlin/team/flex/module/sample/corehr/company/CompanyIdentity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company 6 | 7 | interface CompanyIdentity { 8 | companion object 9 | 10 | val companyId: Long 11 | } 12 | 13 | internal data class SimpleCompanyIdentity( 14 | override val companyId: Long, 15 | ) : CompanyIdentity 16 | 17 | fun CompanyIdentity.Companion.of(companyId: Long): CompanyIdentity = SimpleCompanyIdentity(companyId) 18 | -------------------------------------------------------------------------------- /corehr/model/src/main/kotlin/team/flex/module/sample/corehr/company/jobrole/JobRoleModel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.jobrole 6 | 7 | import team.flex.module.sample.corehr.AuditProps 8 | import team.flex.module.sample.corehr.company.CompanyIdentity 9 | 10 | interface JobRoleProps { 11 | val name: String 12 | } 13 | 14 | interface JobRoleModel : 15 | JobRoleIdentity, 16 | CompanyIdentity, 17 | JobRoleProps, 18 | AuditProps 19 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | dependencies { 6 | implementation(project(":corehr:infrastructure")) 7 | implementation("org.liquibase:liquibase-core") 8 | 9 | integrationTestImplementation("org.testcontainers:mysql") 10 | integrationTestImplementation(project(":corehr:schema")) 11 | integrationTestRuntimeOnly("com.mysql:mysql-connector-j") { 12 | exclude(group = "com.google.protobuf", module = "protobuf-java") 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /corehr/schema/src/main/resources/db/changelog/corehr/db.changelog-initial-schema-and-data.yaml: -------------------------------------------------------------------------------- 1 | databaseChangeLog: 2 | - include: 3 | file: /db/changelog/corehr/db.changelog-initial-schema.yaml 4 | - changeSet: 5 | id: 1734418328776-2 6 | author: flex-team 7 | changes: 8 | - sqlFile: 9 | dbms: mysql 10 | encoding: UTF-8 11 | endDelimiter: ';' 12 | path: db/changelog/corehr/data.sql 13 | splitStatements: true 14 | stripComments: false 15 | -------------------------------------------------------------------------------- /payroll/schema/src/main/resources/db/changelog/payroll/db.changelog-initial-schema-and-data.yaml: -------------------------------------------------------------------------------- 1 | databaseChangeLog: 2 | - include: 3 | file: /db/changelog/payroll/db.changelog-initial-schema.yaml 4 | - changeSet: 5 | id: 1734418328776-2 6 | author: flex-team 7 | changes: 8 | - sqlFile: 9 | dbms: mysql 10 | encoding: UTF-8 11 | endDelimiter: ';' 12 | path: db/changelog/payroll/data.sql 13 | splitStatements: true 14 | stripComments: false 15 | -------------------------------------------------------------------------------- /payroll/repository-jdbc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | dependencies { 6 | implementation(project(":payroll:infrastructure")) 7 | 8 | implementation("org.liquibase:liquibase-core") 9 | 10 | integrationTestImplementation("org.testcontainers:mysql") 11 | integrationTestImplementation(project(":payroll:schema")) 12 | integrationTestRuntimeOnly("com.mysql:mysql-connector-j") { 13 | exclude(group = "com.google.protobuf", module = "protobuf-java") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /corehr/model/src/main/kotlin/team/flex/module/sample/corehr/company/jobrole/JobRoleIdentity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.jobrole 6 | 7 | interface JobRoleIdentity { 8 | companion object 9 | 10 | val jobRoleId: Long 11 | } 12 | 13 | internal data class SimpleJobRoleIdentity( 14 | override val jobRoleId: Long, 15 | ) : JobRoleIdentity 16 | 17 | fun JobRoleIdentity.Companion.of(jobRoleId: Long): JobRoleIdentity = SimpleJobRoleIdentity(jobRoleId) 18 | -------------------------------------------------------------------------------- /corehr/model/src/main/kotlin/team/flex/module/sample/corehr/employee/EmployeeIdentity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.employee 6 | 7 | interface EmployeeIdentity { 8 | companion object 9 | 10 | val employeeId: Long 11 | } 12 | 13 | internal data class SimpleEmployeeIdentity( 14 | override val employeeId: Long, 15 | ) : EmployeeIdentity 16 | 17 | fun EmployeeIdentity.Companion.of(employeeId: Long): EmployeeIdentity = SimpleEmployeeIdentity(employeeId) 18 | -------------------------------------------------------------------------------- /payroll/model/src/main/kotlin/team/flex/module/sample/payroll/PayrollModel.kt: -------------------------------------------------------------------------------- 1 | package team.flex.module.sample.payroll 2 | 3 | import team.flex.module.sample.corehr.AuditProps 4 | import team.flex.module.sample.corehr.company.CompanyIdentity 5 | import team.flex.module.sample.corehr.employee.EmployeeIdentity 6 | 7 | interface PayrollProps { 8 | val id: Long 9 | val payday: Int 10 | val payrollAmount: Long 11 | } 12 | 13 | interface PayrollModel : 14 | EmployeeIdentity, 15 | CompanyIdentity, 16 | PayrollProps, 17 | AuditProps 18 | -------------------------------------------------------------------------------- /corehr/model/src/main/kotlin/team/flex/module/sample/corehr/company/department/Department.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.department 6 | 7 | import java.time.Instant 8 | 9 | data class Department( 10 | override val departmentId: Long, 11 | override val companyId: Long, 12 | override val parentDepartmentId: Long?, 13 | override val name: String, 14 | override val createdAt: Instant, 15 | override val updatedAt: Instant, 16 | ) : DepartmentModel 17 | -------------------------------------------------------------------------------- /corehr/model/src/main/kotlin/team/flex/module/sample/corehr/employee/EmployeeModel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.employee 6 | 7 | import team.flex.module.sample.corehr.AuditProps 8 | import team.flex.module.sample.corehr.company.CompanyIdentity 9 | 10 | interface EmployeeProps { 11 | val employeeNumber: String 12 | val name: String 13 | } 14 | 15 | interface EmployeeModel : 16 | EmployeeIdentity, 17 | CompanyIdentity, 18 | EmployeeProps, 19 | AuditProps 20 | -------------------------------------------------------------------------------- /payroll/repository-jdbc/src/main/kotlin/team/flex/module/sample/payroll/PayrollRepositoryAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.payroll 6 | 7 | import org.springframework.boot.autoconfigure.AutoConfiguration 8 | import org.springframework.context.annotation.Import 9 | import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories 10 | 11 | @Import(PayrollRepositoryImpl::class) 12 | @AutoConfiguration 13 | @EnableJdbcRepositories 14 | class PayrollRepositoryAutoConfiguration 15 | -------------------------------------------------------------------------------- /corehr/model/src/main/kotlin/team/flex/module/sample/corehr/company/department/DepartmentIdentity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.department 6 | 7 | interface DepartmentIdentity { 8 | companion object 9 | 10 | val departmentId: Long 11 | } 12 | 13 | internal data class SimpleDepartmentIdentity( 14 | override val departmentId: Long, 15 | ) : DepartmentIdentity 16 | 17 | fun DepartmentIdentity.Companion.of(departmentId: Long): DepartmentIdentity = SimpleDepartmentIdentity(departmentId) 18 | -------------------------------------------------------------------------------- /application-api/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | dependencies { 6 | implementation(project(":corehr:schema")) 7 | implementation(project(":corehr:api")) 8 | implementation(project(":corehr:repository-jdbc")) 9 | 10 | implementation(project(":payroll:schema")) 11 | implementation(project(":payroll:api")) 12 | implementation(project(":payroll:repository-jdbc")) 13 | 14 | implementation("org.testcontainers:mysql") 15 | runtimeOnly("com.mysql:mysql-connector-j") { 16 | exclude(group = "com.google.protobuf", module = "protobuf-java") 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /payroll/infrastructure/src/main/kotlin/team/flex/module/sample/payroll/repository/PayrollRepository.kt: -------------------------------------------------------------------------------- 1 | package team.flex.module.sample.payroll.repository 2 | 3 | import team.flex.module.sample.corehr.company.CompanyIdentity 4 | import team.flex.module.sample.corehr.employee.EmployeeIdentity 5 | import team.flex.module.sample.payroll.Payroll 6 | import team.flex.module.sample.payroll.PayrollModel 7 | 8 | interface PayrollRepository { 9 | fun findByEmployeeIdentity( 10 | companyIdentity: CompanyIdentity, 11 | employeeIdentity: EmployeeIdentity, 12 | ): PayrollModel? 13 | 14 | fun save(payroll: Payroll): Payroll 15 | } 16 | -------------------------------------------------------------------------------- /application-api/src/main/kotlin/team/flex/module/sample/application/SampleApplication.kt: -------------------------------------------------------------------------------- 1 | package team.flex.module.sample.application 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication 4 | import org.springframework.boot.runApplication 5 | import org.springframework.context.annotation.Bean 6 | import java.time.Clock 7 | import java.util.TimeZone 8 | 9 | @SpringBootApplication 10 | class SampleApplication { 11 | @Bean 12 | fun clock() = Clock.systemUTC() 13 | } 14 | 15 | fun main(args: Array) { 16 | TimeZone.setDefault(TimeZone.getTimeZone("UTC")) 17 | runApplication(*args) 18 | } 19 | -------------------------------------------------------------------------------- /payroll/api/src/main/kotlin/team/flex/module/sample/payroll/dto/PayrollHistoryResponse.kt: -------------------------------------------------------------------------------- 1 | package team.flex.module.sample.payroll.dto 2 | 3 | import java.time.Instant 4 | 5 | sealed class PayrollHistoryResponse( 6 | val type: ResponseType, 7 | ) { 8 | enum class ResponseType { 9 | EXIST, 10 | NOT_EXIST, 11 | } 12 | 13 | data class Exist( 14 | val employeeId: Long, 15 | val companyId: Long, 16 | val payDatetime: Instant, 17 | val payrollAmount: Long, 18 | ) : PayrollHistoryResponse(ResponseType.EXIST) 19 | 20 | object NotExist : PayrollHistoryResponse(ResponseType.NOT_EXIST) 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | /target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | .sts4-cache 13 | 14 | ### IntelliJ IDEA ### 15 | .idea/ 16 | *.iws 17 | *.iml 18 | *.ipr 19 | /out/ 20 | 21 | ### NetBeans ### 22 | /nbproject/private/ 23 | /nbbuild/ 24 | /dist/ 25 | /nbdist/ 26 | /.nb-gradle/ 27 | /**/build/ 28 | /**/out/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | 33 | ### gradle ### 34 | .gradle/ 35 | 36 | /**/src/main/generated/ 37 | /**/src/main/resources-ignore-* 38 | java_pid* 39 | 40 | ### File Storage ### 41 | /storage/ 42 | 43 | argfile* 44 | -------------------------------------------------------------------------------- /corehr/model/src/main/kotlin/team/flex/module/sample/corehr/company/department/DepartmentModel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.department 6 | 7 | import team.flex.module.sample.corehr.AuditProps 8 | import team.flex.module.sample.corehr.company.CompanyIdentity 9 | 10 | interface DepartmentProps { 11 | val name: String 12 | } 13 | 14 | interface ParentDepartmentRelationIdentity { 15 | val parentDepartmentId: Long? 16 | } 17 | 18 | interface DepartmentModel : 19 | DepartmentIdentity, 20 | ParentDepartmentRelationIdentity, 21 | CompanyIdentity, 22 | DepartmentProps, 23 | AuditProps 24 | -------------------------------------------------------------------------------- /corehr/service/src/main/kotlin/team/flex/module/sample/corehr/company/CompanyAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company 6 | 7 | import org.springframework.boot.autoconfigure.AutoConfiguration 8 | import org.springframework.context.annotation.Bean 9 | import team.flex.module.sample.corehr.company.repository.CompanyRepository 10 | 11 | @AutoConfiguration 12 | class CompanyAutoConfiguration { 13 | @Bean 14 | fun companyLookUpServiceImpl(companyRepository: CompanyRepository): CompanyLookUpService = 15 | CompanyLookUpServiceImpl( 16 | companyRepository = companyRepository, 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /corehr/service/src/main/kotlin/team/flex/module/sample/corehr/company/jobrole/JobRoleAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.jobrole 6 | 7 | import org.springframework.boot.autoconfigure.AutoConfiguration 8 | import org.springframework.context.annotation.Bean 9 | import team.flex.module.sample.corehr.company.jobrole.repository.JobRoleRepository 10 | 11 | @AutoConfiguration 12 | class JobRoleAutoConfiguration { 13 | @Bean 14 | fun jobRoleLookUpServiceImpl(jobRoleRepository: JobRoleRepository): JobRoleLookUpService = 15 | JobRoleLookUpServiceImpl( 16 | jobRoleRepository, 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /corehr/service/src/main/kotlin/team/flex/module/sample/corehr/employee/EmployeeAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.employee 6 | 7 | import org.springframework.boot.autoconfigure.AutoConfiguration 8 | import org.springframework.context.annotation.Bean 9 | import team.flex.module.sample.corehr.employee.repository.EmployeeRepository 10 | 11 | @AutoConfiguration 12 | class EmployeeAutoConfiguration { 13 | @Bean 14 | fun employeeLookUpServiceImpl(employeeRepository: EmployeeRepository): EmployeeLookUpService = 15 | EmployeeLookUpServiceImpl( 16 | employeeRepository = employeeRepository, 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /payroll/application-api/src/main/kotlin/team/flex/module/sample/corehr/application/PayrollApplication.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.payroll 6 | 7 | import org.springframework.boot.autoconfigure.SpringBootApplication 8 | import org.springframework.boot.runApplication 9 | import org.springframework.context.annotation.Bean 10 | import java.time.Clock 11 | import java.util.TimeZone 12 | 13 | @SpringBootApplication 14 | class PayrollApplication { 15 | @Bean 16 | fun clock() = Clock.systemUTC() 17 | } 18 | 19 | fun main(args: Array) { 20 | TimeZone.setDefault(TimeZone.getTimeZone("UTC")) 21 | runApplication(*args) 22 | } 23 | -------------------------------------------------------------------------------- /corehr/application-api/src/main/kotlin/team/flex/module/sample/corehr/application/CoreHrApplication.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.application 6 | 7 | import org.springframework.boot.autoconfigure.SpringBootApplication 8 | import org.springframework.boot.runApplication 9 | import org.springframework.context.annotation.Bean 10 | import java.time.Clock 11 | import java.util.TimeZone 12 | 13 | @SpringBootApplication 14 | class CoreHrApplication { 15 | @Bean 16 | fun clock() = Clock.systemUTC() 17 | } 18 | 19 | fun main(args: Array) { 20 | TimeZone.setDefault(TimeZone.getTimeZone("UTC")) 21 | runApplication(*args) 22 | } 23 | -------------------------------------------------------------------------------- /corehr/infrastructure/src/main/kotlin/team/flex/module/sample/corehr/employee/repository/EmployeeRepository.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.employee.repository 6 | 7 | import team.flex.module.sample.corehr.company.CompanyIdentity 8 | import team.flex.module.sample.corehr.employee.EmployeeIdentity 9 | import team.flex.module.sample.corehr.employee.EmployeeModel 10 | 11 | interface EmployeeRepository { 12 | fun findByEmployeeIdentity( 13 | companyIdentity: CompanyIdentity, 14 | employeeIdentity: EmployeeIdentity, 15 | ): EmployeeModel? 16 | 17 | fun findAllByCompanyIdentity(companyIdentity: CompanyIdentity): List 18 | } 19 | -------------------------------------------------------------------------------- /corehr/infrastructure/src/main/kotlin/team/flex/module/sample/corehr/company/jobrole/repository/JobRoleRepository.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.jobrole.repository 6 | 7 | import team.flex.module.sample.corehr.company.CompanyIdentity 8 | import team.flex.module.sample.corehr.company.jobrole.JobRoleIdentity 9 | import team.flex.module.sample.corehr.company.jobrole.JobRoleModel 10 | 11 | interface JobRoleRepository { 12 | fun findByJobRoleIdentity( 13 | companyIdentity: CompanyIdentity, 14 | jobRoleIdentity: JobRoleIdentity, 15 | ): JobRoleModel? 16 | 17 | fun findAllByCompanyIdentity(companyIdentity: CompanyIdentity): List 18 | } 19 | -------------------------------------------------------------------------------- /corehr/service/src/main/kotlin/team/flex/module/sample/corehr/company/department/DepartmentAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.department 6 | 7 | import org.springframework.boot.autoconfigure.AutoConfiguration 8 | import org.springframework.context.annotation.Bean 9 | import team.flex.module.sample.corehr.company.department.repository.DepartmentRepository 10 | 11 | @AutoConfiguration 12 | class DepartmentAutoConfiguration { 13 | @Bean 14 | fun departmentLookUpServiceImpl(departmentRepository: DepartmentRepository): DepartmentLookUpService = 15 | DepartmentLookUpServiceImpl( 16 | departmentRepository = departmentRepository, 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/src/main/kotlin/team/flex/module/sample/corehr/company/repository/CompanyRepositoryAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.repository 6 | 7 | import org.springframework.boot.autoconfigure.AutoConfiguration 8 | import org.springframework.context.annotation.Bean 9 | import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories 10 | 11 | @AutoConfiguration 12 | @EnableJdbcRepositories 13 | class CompanyRepositoryAutoConfiguration { 14 | @Bean 15 | fun companyRepository(companyJdbcRepository: CompanyJdbcRepository): CompanyRepository { 16 | return CompanyRepositoryImpl( 17 | companyJdbcRepository, 18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /payroll/repository-jdbc/src/main/kotlin/team/flex/module/sample/payroll/history/PayrollHistoryEntity.kt: -------------------------------------------------------------------------------- 1 | package team.flex.module.sample.payroll.history 2 | 3 | import org.springframework.data.annotation.Id 4 | import org.springframework.data.relational.core.mapping.Column 5 | import org.springframework.data.relational.core.mapping.Table 6 | import java.time.Instant 7 | 8 | @Table("payroll_history") 9 | class PayrollHistoryEntity( 10 | @Column 11 | val employeeId: Long, 12 | @Column 13 | val companyId: Long, 14 | @Column 15 | val payrollId: Long, 16 | @Column 17 | val payDatetime: Instant, 18 | @Column 19 | val payrollAmount: Long, 20 | ) { 21 | @Id 22 | var id: Long = 0L 23 | 24 | @Column 25 | val createdAt: Instant = Instant.now() 26 | } 27 | -------------------------------------------------------------------------------- /corehr/infrastructure/src/main/kotlin/team/flex/module/sample/corehr/company/department/repository/DepartmentRepository.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.department.repository 6 | 7 | import team.flex.module.sample.corehr.company.CompanyIdentity 8 | import team.flex.module.sample.corehr.company.department.DepartmentIdentity 9 | import team.flex.module.sample.corehr.company.department.DepartmentModel 10 | 11 | interface DepartmentRepository { 12 | fun findByDepartmentIdentity( 13 | companyIdentity: CompanyIdentity, 14 | departmentIdentity: DepartmentIdentity, 15 | ): DepartmentModel? 16 | 17 | fun findAllByCompanyIdentity(companyIdentity: CompanyIdentity): List 18 | } 19 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/src/main/kotlin/team/flex/module/sample/corehr/employee/repository/EmployeeRepositoryAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.employee.repository 6 | 7 | import org.springframework.boot.autoconfigure.AutoConfiguration 8 | import org.springframework.context.annotation.Bean 9 | import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories 10 | 11 | @AutoConfiguration 12 | @EnableJdbcRepositories 13 | class EmployeeRepositoryAutoConfiguration { 14 | @Bean 15 | fun employeeRepository(employeeJdbcRepository: EmployeeJdbcRepository): EmployeeRepository { 16 | return EmployeeRepositoryImpl( 17 | employeeJdbcRepository, 18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/src/main/kotlin/team/flex/module/sample/corehr/company/jobrole/repository/JobRoleRepositoryAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.jobrole.repository 6 | 7 | import org.springframework.boot.autoconfigure.AutoConfiguration 8 | import org.springframework.context.annotation.Bean 9 | import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories 10 | 11 | @AutoConfiguration 12 | @EnableJdbcRepositories 13 | class JobRoleRepositoryAutoConfiguration { 14 | @Bean 15 | fun jobRoleRepository(jobRoleJdbcRepository: JobRoleJdbcRepository): JobRoleRepository { 16 | return JobRoleRepositoryImpl( 17 | jobRoleJdbcRepository, 18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /corehr/service/src/main/kotlin/team/flex/module/sample/corehr/company/CompanyLookUpService.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company 6 | 7 | import team.flex.module.sample.corehr.company.repository.CompanyRepository 8 | import team.flex.module.sample.corehr.exception.CompanyNotFoundException 9 | 10 | interface CompanyLookUpService { 11 | fun get(companyIdentity: CompanyIdentity): CompanyModel 12 | } 13 | 14 | internal class CompanyLookUpServiceImpl( 15 | private val companyRepository: CompanyRepository, 16 | ) : CompanyLookUpService { 17 | override fun get(companyIdentity: CompanyIdentity): CompanyModel = 18 | companyRepository.findByCompanyIdentity(companyIdentity = companyIdentity) ?: throw CompanyNotFoundException() 19 | } 20 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | kotlin = "2.1.21" 3 | kotlinx-coroutine = "1.10.2" 4 | 5 | [plugins] 6 | build-recipe = { id = "com.linecorp.build-recipe-plugin", version = "1.1.1" } 7 | spring-boot = { id = "org.springframework.boot", version = "3.5.0" } 8 | ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "12.3.0" } 9 | detekt = { id = "io.gitlab.arturbosch.detekt", version = "1.23.8" } 10 | 11 | kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } 12 | kotlin-spring = { id = "org.jetbrains.kotlin.plugin.spring", version.ref = "kotlin" } 13 | 14 | [libraries] 15 | kotlin-bom = { group = "org.jetbrains.kotlin", name = "kotlin-bom", version.ref = "kotlin" } 16 | kotlinx-coroutine-bom = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-bom", version.ref = "kotlinx-coroutine" } 17 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/src/main/kotlin/team/flex/module/sample/corehr/company/department/repository/DepartmentRepositoryAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.department.repository 6 | 7 | import org.springframework.boot.autoconfigure.AutoConfiguration 8 | import org.springframework.context.annotation.Bean 9 | import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories 10 | 11 | @AutoConfiguration 12 | @EnableJdbcRepositories 13 | class DepartmentRepositoryAutoConfiguration { 14 | @Bean 15 | fun departmentRepository(departmentJdbcRepository: DepartmentJdbcRepository): DepartmentRepository { 16 | return DepartmentRepositoryImpl( 17 | departmentJdbcRepository, 18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/src/main/kotlin/team/flex/module/sample/corehr/company/repository/CompanyEntity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.repository 6 | 7 | import org.springframework.data.annotation.Id 8 | import org.springframework.data.relational.core.mapping.Column 9 | import org.springframework.data.relational.core.mapping.Table 10 | import team.flex.module.sample.corehr.company.CompanyModel 11 | import java.time.Instant 12 | 13 | @Table("company") 14 | class CompanyEntity( 15 | @Column 16 | override var name: String, 17 | @Column 18 | override val createdAt: Instant, 19 | @Column 20 | override val updatedAt: Instant, 21 | ) : CompanyModel { 22 | @Id 23 | var id: Long = 0L 24 | 25 | override val companyId: Long 26 | get() = id 27 | } 28 | -------------------------------------------------------------------------------- /payroll/repository-jdbc/src/main/kotlin/team/flex/module/sample/payroll/PayrollEntity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.payroll 6 | 7 | import org.springframework.data.annotation.Id 8 | import org.springframework.data.relational.core.mapping.Column 9 | import org.springframework.data.relational.core.mapping.Table 10 | import java.time.Instant 11 | 12 | @Table("payroll") 13 | class PayrollEntity( 14 | @Id 15 | override var id: Long = 0L, 16 | @Column 17 | override val companyId: Long, 18 | @Column 19 | override val employeeId: Long, 20 | @Column 21 | override var payday: Int, 22 | @Column 23 | override var payrollAmount: Long, 24 | ) : PayrollModel { 25 | @Column 26 | override val createdAt: Instant = Instant.now() 27 | 28 | @Column 29 | override val updatedAt: Instant = Instant.now() 30 | } 31 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/src/main/kotlin/team/flex/module/sample/corehr/company/jobrole/repository/JobRoleEntity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.jobrole.repository 6 | 7 | import org.springframework.data.annotation.Id 8 | import org.springframework.data.relational.core.mapping.Column 9 | import org.springframework.data.relational.core.mapping.Table 10 | import team.flex.module.sample.corehr.company.jobrole.JobRoleModel 11 | import java.time.Instant 12 | 13 | @Table("job_role") 14 | class JobRoleEntity( 15 | @Column 16 | override val companyId: Long, 17 | @Column 18 | override var name: String, 19 | @Column 20 | override val createdAt: Instant, 21 | @Column 22 | override val updatedAt: Instant, 23 | ) : JobRoleModel { 24 | @Id 25 | var id: Long = 0L 26 | 27 | override val jobRoleId: Long 28 | get() = id 29 | } 30 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/src/integrationTest/resources/application.yml: -------------------------------------------------------------------------------- 1 | logging: 2 | level: 3 | org.springframework.jdbc.core: TRACE 4 | org.springframework.jdbc.core.StatementCreatorUtils: TRACE 5 | 6 | spring: 7 | datasource: 8 | url: jdbc:tc:mysql:8.0.36:///test?serverTimezone=UTC&TC_REUSABLE=true&useUnicode=true&character_set_server=utf8mb4&connectionTimeZone=UTC&forceConnectionTimeZoneToSession=false&preserveInstants=true&cacheDefaultTimezone=false&zeroDateTimeBehavior=convertToNull&jdbcCompliantTruncation=false&sessionVariables=sql_mode%3D'STRICT_ALL_TABLES'&rewriteBatchedStatements=true 9 | username: test 10 | password: test 11 | hikari: 12 | data-source-properties: 13 | keepSessionStateOnFailover: true 14 | jpa: 15 | properties: 16 | hibernate: 17 | jdbc: 18 | time_zone: UTC 19 | liquibase: 20 | change-log: classpath:/db/changelog/corehr/db.changelog-initial-schema.yaml 21 | 22 | -------------------------------------------------------------------------------- /payroll/repository-jdbc/src/integrationTest/resources/application.yml: -------------------------------------------------------------------------------- 1 | logging: 2 | level: 3 | org.springframework.jdbc.core: TRACE 4 | org.springframework.jdbc.core.StatementCreatorUtils: TRACE 5 | 6 | spring: 7 | datasource: 8 | url: jdbc:tc:mysql:8.0.36:///test?serverTimezone=UTC&TC_REUSABLE=true&useUnicode=true&character_set_server=utf8mb4&connectionTimeZone=UTC&forceConnectionTimeZoneToSession=false&preserveInstants=true&cacheDefaultTimezone=false&zeroDateTimeBehavior=convertToNull&jdbcCompliantTruncation=false&sessionVariables=sql_mode%3D'STRICT_ALL_TABLES'&rewriteBatchedStatements=true 9 | username: test 10 | password: test 11 | hikari: 12 | data-source-properties: 13 | keepSessionStateOnFailover: true 14 | jpa: 15 | properties: 16 | hibernate: 17 | jdbc: 18 | time_zone: UTC 19 | liquibase: 20 | change-log: classpath:/db/changelog/payroll/db.changelog-initial-schema.yaml 21 | 22 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/src/main/kotlin/team/flex/module/sample/corehr/employee/repository/EmployeeEntity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.employee.repository 6 | 7 | import org.springframework.data.annotation.Id 8 | import org.springframework.data.relational.core.mapping.Column 9 | import org.springframework.data.relational.core.mapping.Table 10 | import team.flex.module.sample.corehr.employee.EmployeeModel 11 | import java.time.Instant 12 | 13 | @Table("employee") 14 | class EmployeeEntity( 15 | @Column 16 | override val companyId: Long, 17 | @Column 18 | override val employeeNumber: String, 19 | @Column 20 | override var name: String, 21 | @Column 22 | override val createdAt: Instant, 23 | @Column 24 | override val updatedAt: Instant, 25 | ) : EmployeeModel { 26 | @Id 27 | var id: Long = 0L 28 | 29 | override val employeeId: Long 30 | get() = id 31 | } 32 | -------------------------------------------------------------------------------- /corehr/application-api/src/main/kotlin/team/flex/module/sample/corehr/application/config/SpringDocConfiguration.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.application.config 6 | 7 | import org.springdoc.core.customizers.OpenApiCustomizer 8 | import org.springdoc.core.models.GroupedOpenApi 9 | import org.springframework.context.annotation.Bean 10 | import org.springframework.context.annotation.Configuration 11 | 12 | @Configuration 13 | class SpringDocConfiguration { 14 | @Bean 15 | fun coreHrGroupedOpenApi(openApiCustomizers: List): GroupedOpenApi = 16 | GroupedOpenApi.builder() 17 | .group("corehr") 18 | .pathsToMatch( 19 | "/api/v2/corehr/**", 20 | ) 21 | .apply { 22 | openApiCustomizers.forEach { 23 | addOpenApiCustomizer(it) 24 | } 25 | } 26 | .build() 27 | } 28 | -------------------------------------------------------------------------------- /payroll/application-api/src/main/kotlin/team/flex/module/sample/corehr/application/config/SpringDocConfiguration.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.payroll.application.config 6 | 7 | import org.springdoc.core.customizers.OpenApiCustomizer 8 | import org.springdoc.core.models.GroupedOpenApi 9 | import org.springframework.context.annotation.Bean 10 | import org.springframework.context.annotation.Configuration 11 | 12 | @Configuration 13 | class SpringDocConfiguration { 14 | @Bean 15 | fun payrollGroupedOpenApi(openApiCustomizers: List): GroupedOpenApi = 16 | GroupedOpenApi.builder() 17 | .group("payroll") 18 | .pathsToMatch( 19 | "/api/v2/payroll/**", 20 | ) 21 | .apply { 22 | openApiCustomizers.forEach { 23 | addOpenApiCustomizer(it) 24 | } 25 | } 26 | .build() 27 | } 28 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/src/main/kotlin/team/flex/module/sample/corehr/company/department/repository/DepartmentEntity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.department.repository 6 | 7 | import org.springframework.data.annotation.Id 8 | import org.springframework.data.relational.core.mapping.Column 9 | import org.springframework.data.relational.core.mapping.Table 10 | import team.flex.module.sample.corehr.company.department.DepartmentModel 11 | import java.time.Instant 12 | 13 | @Table("department") 14 | class DepartmentEntity( 15 | @Column 16 | override val companyId: Long, 17 | @Column("parent_id") 18 | override val parentDepartmentId: Long?, 19 | @Column 20 | override var name: String, 21 | @Column 22 | override val createdAt: Instant, 23 | @Column 24 | override val updatedAt: Instant, 25 | ) : DepartmentModel { 26 | @Id 27 | var id: Long = 0L 28 | 29 | override val departmentId: Long 30 | get() = id 31 | } 32 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | rootProject.name = "flex-module-sample" 6 | 7 | include(":corehr:api") 8 | include(":corehr:application-api") 9 | include(":corehr:exception") 10 | include(":corehr:infrastructure") 11 | include(":corehr:model") 12 | include(":corehr:repository-jdbc") 13 | include(":corehr:schema") 14 | include(":corehr:service") 15 | 16 | include(":payroll:api") 17 | include(":payroll:application-api") 18 | include(":payroll:exception") 19 | include(":payroll:infrastructure") 20 | include(":payroll:model") 21 | include(":payroll:repository-jdbc") 22 | include(":payroll:schema") 23 | include(":payroll:service") 24 | 25 | include(":application-api") 26 | 27 | pluginManagement { 28 | buildscript { 29 | repositories { 30 | gradlePluginPortal() 31 | } 32 | } 33 | 34 | repositories { 35 | gradlePluginPortal() 36 | } 37 | } 38 | 39 | dependencyResolutionManagement { 40 | repositories { 41 | mavenCentral() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/src/integrationTest/kotlin/team/flex/module/sample/corehr/TestApplication.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr 6 | 7 | import org.springframework.boot.SpringBootConfiguration 8 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration 9 | import org.springframework.context.annotation.Import 10 | import team.flex.module.sample.corehr.company.department.repository.DepartmentRepositoryAutoConfiguration 11 | import team.flex.module.sample.corehr.company.repository.CompanyRepositoryAutoConfiguration 12 | import team.flex.module.sample.corehr.employee.repository.EmployeeRepositoryAutoConfiguration 13 | import java.util.TimeZone 14 | 15 | @Import( 16 | EmployeeRepositoryAutoConfiguration::class, 17 | CompanyRepositoryAutoConfiguration::class, 18 | DepartmentRepositoryAutoConfiguration::class, 19 | ) 20 | @EnableAutoConfiguration 21 | @SpringBootConfiguration 22 | class TestApplication { 23 | init { 24 | TimeZone.setDefault(TimeZone.getTimeZone("UTC")) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /corehr/service/src/main/kotlin/team/flex/module/sample/corehr/employee/EmployeeLookUpService.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.employee 6 | 7 | import team.flex.module.sample.corehr.company.CompanyIdentity 8 | import team.flex.module.sample.corehr.employee.repository.EmployeeRepository 9 | import team.flex.module.sample.corehr.exception.EmployeeNotFoundException 10 | 11 | interface EmployeeLookUpService { 12 | fun get( 13 | companyIdentity: CompanyIdentity, 14 | employeeIdentity: EmployeeIdentity, 15 | ): EmployeeModel 16 | } 17 | 18 | internal class EmployeeLookUpServiceImpl( 19 | private val employeeRepository: EmployeeRepository, 20 | ) : EmployeeLookUpService { 21 | override fun get( 22 | companyIdentity: CompanyIdentity, 23 | employeeIdentity: EmployeeIdentity, 24 | ): EmployeeModel = 25 | employeeRepository.findByEmployeeIdentity( 26 | companyIdentity = companyIdentity, 27 | employeeIdentity = employeeIdentity, 28 | ) 29 | ?: throw EmployeeNotFoundException() 30 | } 31 | -------------------------------------------------------------------------------- /payroll/infrastructure/src/main/kotlin/team/flex/module/sample/payroll/repository/PayrollHistoryRepository.kt: -------------------------------------------------------------------------------- 1 | package team.flex.module.sample.payroll.repository 2 | 3 | import team.flex.module.sample.corehr.company.CompanyIdentity 4 | import team.flex.module.sample.corehr.employee.EmployeeIdentity 5 | import team.flex.module.sample.payroll.PayrollHistory 6 | 7 | @Suppress("ktlint") 8 | interface PayrollHistoryRepository { 9 | // <<<<<<<<<<<<<< ✨ Windsurf Command ⭐ >>>>>>>>>>>>>>>> 10 | /** 11 | * Retrieves a [PayrollHistory] identified by the given [CompanyIdentity] and 12 | * [EmployeeIdentity]. 13 | * 14 | * @param companyIdentity the identity of the company 15 | * @param employeeIdentity the identity of the employee 16 | * @return the payroll history associated with the given company and employee, or `null` if none. 17 | */ 18 | // <<<<<<<<<< e52b93d1-6c93-4e5f-b037-6b031c6633d8 >>>>>>>>>>> 19 | 20 | fun findByEmployeeIdentity( 21 | companyIdentity: CompanyIdentity, 22 | employeeIdentity: EmployeeIdentity, 23 | ): PayrollHistory? 24 | 25 | fun save(payrollHistory: PayrollHistory): PayrollHistory 26 | } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flex-module-sample 2 | --- 3 | 4 | 이 프로젝트는 flex team 에서 componentScan 으로 결합하는 단일 모듈을 벗어난 프로젝트 구조를 제안하기 위해 만든 예제 입니다. 5 | 6 | 구조적인 표현을 위해 필요한 최소한의 코드만 포함하고 있습니다. 7 | 8 | 공감이 어려운 부분이나 표현이 부족하다고 생각이 되신다면, 저희 discord community를 통해 논의를 나눠보면 좋을 것 같습니다. 9 | 10 | ![Discord Banner 1](https://discord.com/api/guilds/1377214678945759252/widget.png?style=banner1) 11 | [디스코드 채널 들어가기](https://discord.gg/3gd36XYM) 12 | 13 | --- 14 | 15 | ## 실행 안내 16 | 17 | 1. [sdkman 설치](https://sdkman.io/install/)를 권장합니다. 18 | 2. 실행에는 docker 환경이 필요합니다. gradle check 태스크는 integrationTest를 포함하고 있으며, integrationTest는 testcontainers를 이용하고 있습니다. 19 | 1. [mac](https://docs.docker.com/desktop/setup/install/mac-install/) 20 | 2. [windows](https://docs.docker.com/desktop/setup/install/windows-install/) 21 | 3. [linux(ubuntu)](https://docs.docker.com/desktop/setup/install/linux/ubuntu/) 22 | 3. IDE는 JetBrains의 [IntelliJ Community 버전](https://www.jetbrains.com/help/idea/installation-guide.html)을 권장합니다. Ultimate도 무방합니다. 23 | 4. `./gradlew check` 를 통해 검증이 가능합니다. 24 | 5. `./gradlew bootRun` 을 실행하시면 8080 port를 이용해 서버가 기동됩니다. 25 | 6. [swagger-ui](http://localhost:8080/swagger-ui.html) 를 통해 api 호출을 확인해보실 수 있습니다. 26 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/src/main/kotlin/team/flex/module/sample/corehr/company/repository/CompanyJdbcRepository.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.repository 6 | 7 | import org.springframework.data.repository.CrudRepository 8 | import org.springframework.data.repository.findByIdOrNull 9 | import team.flex.module.sample.corehr.company.Company 10 | import team.flex.module.sample.corehr.company.CompanyIdentity 11 | import team.flex.module.sample.corehr.company.CompanyModel 12 | 13 | interface CompanyJdbcRepository : CrudRepository 14 | 15 | class CompanyRepositoryImpl( 16 | private val companyJdbcRepository: CompanyJdbcRepository, 17 | ) : CompanyRepository { 18 | override fun findByCompanyIdentity(companyIdentity: CompanyIdentity): CompanyModel? { 19 | return companyJdbcRepository.findByIdOrNull(id = companyIdentity.companyId)?.toModel() 20 | } 21 | 22 | private fun CompanyEntity.toModel() = 23 | Company( 24 | companyId = companyId, 25 | name = name, 26 | createdAt = createdAt, 27 | updatedAt = updatedAt, 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /application-api/src/integrationTest/kotlin/team/flex/module/sample/application/IntegrationTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.application 6 | 7 | import org.assertj.core.api.Assertions.assertThat 8 | import org.junit.jupiter.api.Test 9 | import org.springframework.boot.test.context.SpringBootTest 10 | import org.springframework.boot.test.web.client.TestRestTemplate 11 | import org.springframework.boot.test.web.client.getForEntity 12 | import org.springframework.http.HttpStatus 13 | import org.springframework.test.context.TestConstructor 14 | 15 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 16 | @TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL) 17 | class IntegrationTest(private val restTemplate: TestRestTemplate) { 18 | @Test 19 | fun test() { 20 | restTemplate.getForEntity("/actuator/health/liveness").also { 21 | assertThat(it.statusCode).isEqualTo(HttpStatus.OK) 22 | } 23 | 24 | restTemplate.getForEntity("/actuator/health/readiness").also { 25 | assertThat(it.statusCode).isEqualTo(HttpStatus.OK) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /corehr/application-api/src/integrationTest/kotlin/team/flex/module/sample/corehr/application/IntegrationTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.application 6 | 7 | import org.assertj.core.api.Assertions.assertThat 8 | import org.junit.jupiter.api.Test 9 | import org.springframework.boot.test.context.SpringBootTest 10 | import org.springframework.boot.test.web.client.TestRestTemplate 11 | import org.springframework.boot.test.web.client.getForEntity 12 | import org.springframework.http.HttpStatus 13 | import org.springframework.test.context.TestConstructor 14 | 15 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 16 | @TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL) 17 | class IntegrationTest(private val restTemplate: TestRestTemplate) { 18 | @Test 19 | fun test() { 20 | restTemplate.getForEntity("/actuator/health/liveness").also { 21 | assertThat(it.statusCode).isEqualTo(HttpStatus.OK) 22 | } 23 | 24 | restTemplate.getForEntity("/actuator/health/readiness").also { 25 | assertThat(it.statusCode).isEqualTo(HttpStatus.OK) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /application-api/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: flex-sample-backend 4 | config: 5 | import: 6 | datasource: 7 | url: jdbc:tc:mysql:8.0.36:///test?serverTimezone=UTC&TC_REUSABLE=true&useUnicode=true&character_set_server=utf8mb4&connectionTimeZone=UTC&forceConnectionTimeZoneToSession=false&preserveInstants=true&cacheDefaultTimezone=false&zeroDateTimeBehavior=convertToNull&jdbcCompliantTruncation=false&sessionVariables=sql_mode%3D'STRICT_ALL_TABLES'&rewriteBatchedStatements=true 8 | username: test 9 | password: test 10 | liquibase: 11 | change-log: classpath:/db/changelog/db.changelog-master.yaml 12 | 13 | server: 14 | tomcat: 15 | mbeanregistry: 16 | enabled: true 17 | 18 | #### Actuator 공통 설정 19 | management: 20 | endpoints: 21 | web: 22 | exposure: 23 | include: 24 | - health 25 | - prometheus 26 | - loggers 27 | endpoint: 28 | health: 29 | probes: 30 | enabled: true 31 | 32 | #### secrets 33 | --- 34 | 35 | spring: 36 | config: 37 | activate: 38 | on-profile: local-dev 39 | 40 | # local에서 server port 충돌을 피하기 위한 설정. gateway에 붙어야하면 이것을 기록해두는게 좋습니다 41 | server: 42 | port: 12346 43 | 44 | management: 45 | endpoints: 46 | web: 47 | exposure: 48 | include: '*' 49 | -------------------------------------------------------------------------------- /corehr/application-api/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: flex-sample-backend 4 | config: 5 | import: 6 | datasource: 7 | url: jdbc:tc:mysql:8.0.36:///test?serverTimezone=UTC&TC_REUSABLE=true&useUnicode=true&character_set_server=utf8mb4&connectionTimeZone=UTC&forceConnectionTimeZoneToSession=false&preserveInstants=true&cacheDefaultTimezone=false&zeroDateTimeBehavior=convertToNull&jdbcCompliantTruncation=false&sessionVariables=sql_mode%3D'STRICT_ALL_TABLES'&rewriteBatchedStatements=true 8 | username: test 9 | password: test 10 | liquibase: 11 | change-log: classpath:/db/changelog/corehr/db.changelog-initial-schema-and-data.yaml 12 | 13 | server: 14 | tomcat: 15 | mbeanregistry: 16 | enabled: true 17 | 18 | #### Actuator 공통 설정 19 | management: 20 | endpoints: 21 | web: 22 | exposure: 23 | include: 24 | - health 25 | - prometheus 26 | - loggers 27 | endpoint: 28 | health: 29 | probes: 30 | enabled: true 31 | 32 | #### secrets 33 | --- 34 | 35 | spring: 36 | config: 37 | activate: 38 | on-profile: local-dev 39 | 40 | # local에서 server port 충돌을 피하기 위한 설정. gateway에 붙어야하면 이것을 기록해두는게 좋습니다 41 | server: 42 | port: 12345 43 | 44 | management: 45 | endpoints: 46 | web: 47 | exposure: 48 | include: '*' 49 | -------------------------------------------------------------------------------- /payroll/application-api/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: flex-sample-backend 4 | config: 5 | import: 6 | datasource: 7 | url: jdbc:tc:mysql:8.0.36:///test?serverTimezone=UTC&TC_REUSABLE=true&useUnicode=true&character_set_server=utf8mb4&connectionTimeZone=UTC&forceConnectionTimeZoneToSession=false&preserveInstants=true&cacheDefaultTimezone=false&zeroDateTimeBehavior=convertToNull&jdbcCompliantTruncation=false&sessionVariables=sql_mode%3D'STRICT_ALL_TABLES'&rewriteBatchedStatements=true 8 | username: test 9 | password: test 10 | liquibase: 11 | change-log: classpath:/db/changelog/payroll/db.changelog-initial-schema-and-data.yaml 12 | 13 | server: 14 | tomcat: 15 | mbeanregistry: 16 | enabled: true 17 | 18 | #### Actuator 공통 설정 19 | management: 20 | endpoints: 21 | web: 22 | exposure: 23 | include: 24 | - health 25 | - prometheus 26 | - loggers 27 | endpoint: 28 | health: 29 | probes: 30 | enabled: true 31 | 32 | #### secrets 33 | --- 34 | 35 | spring: 36 | config: 37 | activate: 38 | on-profile: local-dev 39 | 40 | # local에서 server port 충돌을 피하기 위한 설정. gateway에 붙어야하면 이것을 기록해두는게 좋습니다 41 | server: 42 | port: 12345 43 | 44 | management: 45 | endpoints: 46 | web: 47 | exposure: 48 | include: '*' 49 | -------------------------------------------------------------------------------- /corehr/service/src/main/kotlin/team/flex/module/sample/corehr/company/jobrole/JobRoleLookUpService.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.jobrole 6 | 7 | import team.flex.module.sample.corehr.company.CompanyIdentity 8 | import team.flex.module.sample.corehr.company.jobrole.repository.JobRoleRepository 9 | import team.flex.module.sample.corehr.exception.JobRoleNotFoundException 10 | 11 | interface JobRoleLookUpService { 12 | fun get( 13 | companyIdentity: CompanyIdentity, 14 | jobRoleIdentity: JobRoleIdentity, 15 | ): JobRoleModel 16 | 17 | fun getAll(companyIdentity: CompanyIdentity): List 18 | } 19 | 20 | internal class JobRoleLookUpServiceImpl( 21 | private val jobRoleRepository: JobRoleRepository, 22 | ) : JobRoleLookUpService { 23 | override fun get( 24 | companyIdentity: CompanyIdentity, 25 | jobRoleIdentity: JobRoleIdentity, 26 | ): JobRoleModel = 27 | jobRoleRepository.findByJobRoleIdentity( 28 | companyIdentity = companyIdentity, 29 | jobRoleIdentity = jobRoleIdentity, 30 | ) ?: throw JobRoleNotFoundException() 31 | 32 | override fun getAll(companyIdentity: CompanyIdentity): List = 33 | jobRoleRepository.findAllByCompanyIdentity(companyIdentity = companyIdentity) 34 | } 35 | -------------------------------------------------------------------------------- /payroll/schema/src/main/resources/db/changelog/payroll/initial_schema.sql: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | CREATE TABLE `payroll` 6 | ( 7 | `id` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, 8 | `employee_id` BIGINT NOT NULL comment 'FK: employee.id', 9 | `company_id` BIGINT NOT NULL comment 'FK: company.id', 10 | `payday` INT(2) NOT NULL comment '지급일 1-31, 0 means last day of month', 11 | `payroll_amount` BIGINT NOT NULL comment '월급 금액', 12 | `created_at` DATETIME NOT NULL comment '생성일시', 13 | `updated_at` DATETIME NOT NULL comment '수정일시' 14 | ) ENGINE = InnoDB 15 | DEFAULT CHARSET = utf8mb4; 16 | 17 | CREATE TABLE `payroll_history` 18 | ( 19 | `id` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, 20 | `payroll_id` BIGINT NOT NULL comment 'FK: payroll.id', 21 | `employee_id` BIGINT NOT NULL comment 'FK: employ.id, for efficient query', 22 | `company_id` BIGINT NOT NULL comment 'FK: company.id, for efficient query', 23 | `payroll_amount` BIGINT NOT NULL comment '지급한 월급 금액', 24 | `pay_datetime` DATETIME NOT NULL comment '지급 일시', 25 | `created_at` DATETIME NOT NULL comment '생성일시', 26 | `updated_at` DATETIME NOT NULL comment '수정일시' 27 | ) ENGINE = InnoDB 28 | DEFAULT CHARSET = utf8mb4; 29 | -------------------------------------------------------------------------------- /corehr/service/src/main/kotlin/team/flex/module/sample/corehr/company/department/DepartmentLookUpService.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.department 6 | 7 | import team.flex.module.sample.corehr.company.CompanyIdentity 8 | import team.flex.module.sample.corehr.company.department.repository.DepartmentRepository 9 | import team.flex.module.sample.corehr.exception.DepartmentNotFoundException 10 | 11 | interface DepartmentLookUpService { 12 | fun get( 13 | companyIdentity: CompanyIdentity, 14 | departmentIdentity: DepartmentIdentity, 15 | ): DepartmentModel 16 | 17 | fun getAll(companyIdentity: CompanyIdentity): List 18 | } 19 | 20 | internal class DepartmentLookUpServiceImpl( 21 | private val departmentRepository: DepartmentRepository, 22 | ) : DepartmentLookUpService { 23 | override fun get( 24 | companyIdentity: CompanyIdentity, 25 | departmentIdentity: DepartmentIdentity, 26 | ): DepartmentModel = 27 | departmentRepository.findByDepartmentIdentity( 28 | companyIdentity = companyIdentity, 29 | departmentIdentity = departmentIdentity, 30 | ) 31 | ?: throw DepartmentNotFoundException() 32 | 33 | override fun getAll(companyIdentity: CompanyIdentity): List = 34 | departmentRepository.findAllByCompanyIdentity(companyIdentity = companyIdentity) 35 | } 36 | -------------------------------------------------------------------------------- /corehr/api/src/main/kotlin/team/flex/module/sample/corehr/employee/EmployeeApiController.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.employee 6 | 7 | import io.swagger.v3.oas.annotations.Operation 8 | import org.springframework.web.bind.annotation.GetMapping 9 | import org.springframework.web.bind.annotation.PathVariable 10 | import org.springframework.web.bind.annotation.RequestMapping 11 | import org.springframework.web.bind.annotation.RestController 12 | import team.flex.module.sample.corehr.company.CompanyIdentity 13 | import team.flex.module.sample.corehr.company.of 14 | import team.flex.module.sample.corehr.employee.dto.EmployeeResponse 15 | 16 | @RestController 17 | @RequestMapping("/api/v2/corehr") 18 | class EmployeeApiController( 19 | private val service: EmployeeLookUpService, 20 | ) { 21 | @GetMapping("/companies/{companyId}/employees/{employeeId}") 22 | @Operation( 23 | summary = "구성원 조회 API", 24 | operationId = "getEmployee", 25 | ) 26 | fun getEmployee( 27 | @PathVariable companyId: Long, 28 | @PathVariable employeeId: Long, 29 | ): EmployeeResponse { 30 | return service.get( 31 | CompanyIdentity.of(companyId), 32 | EmployeeIdentity.of(employeeId), 33 | ).let { 34 | EmployeeResponse( 35 | employeeId = it.employeeId, 36 | employeeNumber = it.employeeNumber, 37 | employeeName = it.name, 38 | ) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /corehr/schema/src/main/resources/db/changelog/corehr/initial_schema.sql: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | CREATE TABLE `department` 6 | ( 7 | `id` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, 8 | `company_id` BIGINT NOT NULL, 9 | `parent_id` BIGINT NULL, 10 | `name` VARCHAR(50) NOT NULL, 11 | `created_at` DATETIME NOT NULL, 12 | `updated_at` DATETIME NOT NULL 13 | ) ENGINE = InnoDB 14 | DEFAULT CHARSET = utf8mb4; 15 | 16 | CREATE TABLE `job_role` 17 | ( 18 | `id` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, 19 | `company_id` BIGINT NOT NULL, 20 | `name` VARCHAR(50) NOT NULL, 21 | `created_at` DATETIME NOT NULL, 22 | `updated_at` DATETIME NOT NULL 23 | ) ENGINE = InnoDB 24 | DEFAULT CHARSET = utf8mb4; 25 | 26 | CREATE TABLE `company` 27 | ( 28 | `id` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, 29 | `name` VARCHAR(50) NOT NULL, 30 | `created_at` DATETIME NOT NULL, 31 | `updated_at` DATETIME NOT NULL 32 | ) ENGINE = InnoDB 33 | DEFAULT CHARSET = utf8mb4; 34 | 35 | CREATE TABLE `employee` 36 | ( 37 | `id` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, 38 | `company_id` BIGINT NOT NULL, 39 | `employee_number` VARCHAR(50) NOT NULL, 40 | `name` VARCHAR(50) NOT NULL, 41 | `created_at` DATETIME NOT NULL, 42 | `updated_at` DATETIME NOT NULL 43 | ) ENGINE = InnoDB 44 | DEFAULT CHARSET = utf8mb4; 45 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/src/main/kotlin/team/flex/module/sample/corehr/company/jobrole/repository/JobRoleJdbcRepository.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.jobrole.repository 6 | 7 | import org.springframework.data.repository.CrudRepository 8 | import team.flex.module.sample.corehr.company.CompanyIdentity 9 | import team.flex.module.sample.corehr.company.jobrole.JobRole 10 | import team.flex.module.sample.corehr.company.jobrole.JobRoleIdentity 11 | import team.flex.module.sample.corehr.company.jobrole.JobRoleModel 12 | 13 | interface JobRoleJdbcRepository : CrudRepository { 14 | fun findByIdAndCompanyId( 15 | jobRoleId: Long, 16 | companyId: Long, 17 | ): JobRoleEntity? 18 | 19 | fun findAllByCompanyId(companyId: Long): List 20 | } 21 | 22 | class JobRoleRepositoryImpl( 23 | private val jobRoleJdbcRepository: JobRoleJdbcRepository, 24 | ) : JobRoleRepository { 25 | override fun findByJobRoleIdentity( 26 | companyIdentity: CompanyIdentity, 27 | jobRoleIdentity: JobRoleIdentity, 28 | ): JobRoleModel? { 29 | return jobRoleJdbcRepository.findByIdAndCompanyId( 30 | jobRoleId = jobRoleIdentity.jobRoleId, 31 | companyId = companyIdentity.companyId, 32 | )?.toModel() 33 | } 34 | 35 | override fun findAllByCompanyIdentity(companyIdentity: CompanyIdentity): List { 36 | return jobRoleJdbcRepository.findAllByCompanyId( 37 | companyId = companyIdentity.companyId, 38 | ).map { it.toModel() } 39 | } 40 | 41 | private fun JobRoleEntity.toModel() = 42 | JobRole( 43 | jobRoleId = jobRoleId, 44 | companyId = companyId, 45 | name = name, 46 | createdAt = createdAt, 47 | updatedAt = updatedAt, 48 | ) 49 | } 50 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/src/main/kotlin/team/flex/module/sample/corehr/employee/repository/EmployeeJdbcRepository.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.employee.repository 6 | 7 | import org.springframework.data.repository.CrudRepository 8 | import team.flex.module.sample.corehr.company.CompanyIdentity 9 | import team.flex.module.sample.corehr.employee.Employee 10 | import team.flex.module.sample.corehr.employee.EmployeeIdentity 11 | import team.flex.module.sample.corehr.employee.EmployeeModel 12 | 13 | interface EmployeeJdbcRepository : CrudRepository { 14 | fun findByIdAndCompanyId( 15 | employeeId: Long, 16 | companyId: Long, 17 | ): EmployeeEntity? 18 | 19 | fun findByCompanyId(companyId: Long): List 20 | } 21 | 22 | class EmployeeRepositoryImpl( 23 | private val employeeJdbcRepository: EmployeeJdbcRepository, 24 | ) : EmployeeRepository { 25 | override fun findByEmployeeIdentity( 26 | companyIdentity: CompanyIdentity, 27 | employeeIdentity: EmployeeIdentity, 28 | ): EmployeeModel? { 29 | return employeeJdbcRepository.findByIdAndCompanyId( 30 | employeeId = employeeIdentity.employeeId, 31 | companyId = companyIdentity.companyId, 32 | )?.toModel() 33 | } 34 | 35 | override fun findAllByCompanyIdentity(companyIdentity: CompanyIdentity): List { 36 | return employeeJdbcRepository.findByCompanyId( 37 | companyId = companyIdentity.companyId, 38 | ).map { it.toModel() } 39 | } 40 | 41 | private fun EmployeeEntity.toModel() = 42 | Employee( 43 | employeeId = employeeId, 44 | companyId = companyId, 45 | employeeNumber = employeeNumber, 46 | name = name, 47 | createdAt = createdAt, 48 | updatedAt = updatedAt, 49 | ) 50 | } 51 | -------------------------------------------------------------------------------- /payroll/repository-jdbc/src/main/kotlin/team/flex/module/sample/payroll/PayrollJdbcRepository.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.payroll 6 | 7 | import org.springframework.data.repository.CrudRepository 8 | import team.flex.module.sample.corehr.company.CompanyIdentity 9 | import team.flex.module.sample.corehr.employee.EmployeeIdentity 10 | import team.flex.module.sample.payroll.repository.PayrollRepository 11 | 12 | interface PayrollJdbcRepository : CrudRepository { 13 | fun findByEmployeeIdAndCompanyId( 14 | employeeId: Long, 15 | companyId: Long, 16 | ): PayrollEntity? 17 | } 18 | 19 | class PayrollRepositoryImpl( 20 | private val payrollJdbcRepository: PayrollJdbcRepository, 21 | ) : PayrollRepository { 22 | private fun PayrollEntity.toModel() = 23 | Payroll( 24 | id = id, 25 | employeeId = employeeId, 26 | companyId = companyId, 27 | payday = payday, 28 | payrollAmount = payrollAmount, 29 | createdAt = createdAt, 30 | updatedAt = updatedAt, 31 | ) 32 | 33 | override fun findByEmployeeIdentity( 34 | companyIdentity: CompanyIdentity, 35 | employeeIdentity: EmployeeIdentity, 36 | ): PayrollModel? { 37 | return payrollJdbcRepository.findByEmployeeIdAndCompanyId( 38 | employeeId = employeeIdentity.employeeId, 39 | companyId = companyIdentity.companyId, 40 | )?.toModel() 41 | } 42 | 43 | override fun save(payroll: Payroll): Payroll { 44 | return payrollJdbcRepository.save( 45 | PayrollEntity( 46 | companyId = payroll.companyId, 47 | employeeId = payroll.employeeId, 48 | payday = payroll.payday, 49 | payrollAmount = payroll.payrollAmount, 50 | ), 51 | ).toModel() 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/src/main/kotlin/team/flex/module/sample/corehr/company/department/repository/DepartmentJdbcRepository.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.department.repository 6 | 7 | import org.springframework.data.repository.CrudRepository 8 | import team.flex.module.sample.corehr.company.CompanyIdentity 9 | import team.flex.module.sample.corehr.company.department.Department 10 | import team.flex.module.sample.corehr.company.department.DepartmentIdentity 11 | import team.flex.module.sample.corehr.company.department.DepartmentModel 12 | 13 | interface DepartmentJdbcRepository : CrudRepository { 14 | fun findByIdAndCompanyId( 15 | departmentId: Long, 16 | companyId: Long, 17 | ): DepartmentEntity? 18 | 19 | fun findAllByCompanyId(companyId: Long): List 20 | } 21 | 22 | class DepartmentRepositoryImpl( 23 | private val departmentJdbcRepository: DepartmentJdbcRepository, 24 | ) : DepartmentRepository { 25 | override fun findByDepartmentIdentity( 26 | companyIdentity: CompanyIdentity, 27 | departmentIdentity: DepartmentIdentity, 28 | ): DepartmentModel? { 29 | return departmentJdbcRepository.findByIdAndCompanyId( 30 | departmentId = departmentIdentity.departmentId, 31 | companyId = companyIdentity.companyId, 32 | )?.toModel() 33 | } 34 | 35 | override fun findAllByCompanyIdentity(companyIdentity: CompanyIdentity): List { 36 | return departmentJdbcRepository.findAllByCompanyId( 37 | companyId = companyIdentity.companyId, 38 | ).map { it.toModel() } 39 | } 40 | 41 | private fun DepartmentEntity.toModel() = 42 | Department( 43 | departmentId = departmentId, 44 | companyId = companyId, 45 | parentDepartmentId = parentDepartmentId, 46 | name = name, 47 | createdAt = createdAt, 48 | updatedAt = updatedAt, 49 | ) 50 | } 51 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: [ "main" ] 5 | pull_request: 6 | branches: [ "main" ] 7 | workflow_dispatch: 8 | 9 | jobs: 10 | ci: 11 | name: ci 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: checkout 15 | uses: actions/checkout@v4 16 | - name: install sdkman 17 | run: curl -s "https://get.sdkman.io" | bash 18 | - name: jdk cache 19 | uses: actions/cache@v4 20 | with: 21 | path: | 22 | ~/.sdkman 23 | key: gha-${{ runner.os }}-sdkman-${{ hashFiles('**/.sdkmanrc') }} 24 | - name: install jdk 25 | run: | 26 | source "$HOME/.sdkman/bin/sdkman-init.sh" 27 | sdk env install 28 | - name: gradle cache 29 | uses: actions/cache@v4 30 | with: 31 | path: | 32 | ~/.gradle/caches 33 | ~/.gradle/wrapper 34 | ~/.gradle/jdks 35 | key: gha-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} 36 | restore-keys: | 37 | gha-${{ runner.os }}-gradle 38 | - name: check 39 | run: | 40 | source "$HOME/.sdkman/bin/sdkman-init.sh" 41 | ./gradlew check --parallel --no-daemon 42 | - name: upload ktlint report 43 | uses: jwgmeligmeyling/checkstyle-github-action@v1.2 44 | if: always() 45 | continue-on-error: true 46 | with: 47 | name: ktlint report 48 | path: '**/build/reports/ktlint/**/*.xml' 49 | - name: upload detekt report 50 | uses: jwgmeligmeyling/checkstyle-github-action@v1.2 51 | if: always() 52 | continue-on-error: true 53 | with: 54 | name: detekt report 55 | path: '**/build/reports/detekt/detekt.xml' 56 | - name: upload test report 57 | uses: mikepenz/action-junit-report@v3 58 | if: always() 59 | continue-on-error: true 60 | with: 61 | check_name: 'JUnit Test Report' 62 | report_paths: '**/build/test-results/*[tT]est/TEST-*.xml' 63 | -------------------------------------------------------------------------------- /payroll/repository-jdbc/src/main/kotlin/team/flex/module/sample/payroll/history/PayrollHistoryJdbcRepository.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.payroll.history 6 | 7 | import org.springframework.data.repository.CrudRepository 8 | import team.flex.module.sample.corehr.company.CompanyIdentity 9 | import team.flex.module.sample.corehr.employee.EmployeeIdentity 10 | import team.flex.module.sample.payroll.PayrollHistory 11 | import team.flex.module.sample.payroll.repository.PayrollHistoryRepository 12 | 13 | interface PayrollHistoryJdbcRepository : CrudRepository { 14 | fun findByEmployeeIdAndCompanyId( 15 | employeeId: Long, 16 | companyId: Long, 17 | ): PayrollHistoryEntity? 18 | } 19 | 20 | class PayrollHistoryRepositoryImpl( 21 | private val payrollHistoryJdbcRepository: PayrollHistoryJdbcRepository, 22 | ) : PayrollHistoryRepository { 23 | private fun PayrollHistoryEntity.toModel() = 24 | PayrollHistory( 25 | employeeId = employeeId, 26 | companyId = companyId, 27 | payrollId = payrollId, 28 | payDatetime = payDatetime, 29 | payrollAmount = payrollAmount, 30 | ) 31 | 32 | override fun findByEmployeeIdentity( 33 | companyIdentity: CompanyIdentity, 34 | employeeIdentity: EmployeeIdentity, 35 | ): PayrollHistory? { 36 | return payrollHistoryJdbcRepository.findByEmployeeIdAndCompanyId( 37 | employeeId = employeeIdentity.employeeId, 38 | companyId = companyIdentity.companyId, 39 | )?.toModel() 40 | } 41 | 42 | override fun save(payrollHistory: PayrollHistory): PayrollHistory { 43 | val entity = 44 | PayrollHistoryEntity( 45 | employeeId = payrollHistory.employeeId, 46 | companyId = payrollHistory.companyId, 47 | payrollId = payrollHistory.payrollId, 48 | payDatetime = payrollHistory.payDatetime, 49 | payrollAmount = payrollHistory.payrollAmount, 50 | ) 51 | return payrollHistoryJdbcRepository.save(entity).toModel() 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/src/integrationTest/kotlin/team/flex/module/sample/corehr/company/CompanyRepositoryIntegrationTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company 6 | 7 | import org.assertj.core.api.Assertions.assertThat 8 | import org.junit.jupiter.api.Test 9 | import org.springframework.boot.test.autoconfigure.data.jdbc.DataJdbcTest 10 | import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase 11 | import org.springframework.test.context.TestConstructor 12 | import team.flex.module.sample.corehr.company.repository.CompanyEntity 13 | import team.flex.module.sample.corehr.company.repository.CompanyJdbcRepository 14 | import team.flex.module.sample.corehr.company.repository.CompanyRepository 15 | import java.time.Instant 16 | import java.time.temporal.ChronoUnit 17 | 18 | @DataJdbcTest 19 | @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) 20 | @TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL) 21 | class CompanyRepositoryIntegrationTest( 22 | private val cut: CompanyRepository, 23 | private val jdbcRepository: CompanyJdbcRepository, 24 | ) { 25 | @Test 26 | fun crudTest() { 27 | jdbcRepository.findAll().also { 28 | assertThat(it).isEmpty() 29 | } 30 | 31 | val expected = 32 | CompanyEntity( 33 | name = "name", 34 | createdAt = Instant.now().truncatedTo(ChronoUnit.SECONDS), 35 | updatedAt = Instant.now().truncatedTo(ChronoUnit.SECONDS), 36 | ) 37 | 38 | val saved = 39 | jdbcRepository.save(expected).also { 40 | assertThat(it.id).isNotZero() 41 | } 42 | 43 | cut.findByCompanyIdentity( 44 | companyIdentity = CompanyIdentity.of(expected.companyId), 45 | ) 46 | .also { 47 | assertThat(it!!.name).isEqualTo(expected.name) 48 | assertThat(it.createdAt).isEqualTo(expected.createdAt) 49 | assertThat(it.updatedAt).isEqualTo(expected.updatedAt) 50 | } 51 | 52 | jdbcRepository.delete(saved) 53 | 54 | jdbcRepository.findAll().also { 55 | assertThat(it).isEmpty() 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /corehr/README.md: -------------------------------------------------------------------------------- 1 | # 모듈 2 | 3 | ## 의존성 그래프 4 | 5 | ```mermaid 6 | 7 | flowchart TD 8 | 9 | %% Domain Layer 10 | subgraph "Domain Layer" 11 | model["model"] 12 | exception["exception"] 13 | end 14 | 15 | %% Service Layer 16 | subgraph "Usecase Layer" 17 | service["service
(usecase & impl)"] 18 | end 19 | 20 | %% Driving Side 21 | subgraph "Driving Side" 22 | api["api
(input adapter)"] 23 | end 24 | 25 | %% Driven Side 26 | subgraph "Driven Side" 27 | infrastructure["infrastructure
(output port)"] 28 | repository["repository-jdbc
(output adapter)"] 29 | end 30 | 31 | %% Application Wrapper 32 | subgraph "Bootstrap" 33 | application["application-api"] 34 | end 35 | 36 | %% Relationships 37 | service --> model 38 | service --> infrastructure 39 | service --> exception 40 | infrastructure --> model 41 | repository -.-> infrastructure 42 | api --> service 43 | api --> exception 44 | application --> api 45 | application --> repository 46 | 47 | ``` 48 | 49 | ## 설명 50 | 51 | hexagonal architecture (Ports and Adapters Architecture)를 기반으로 한 그래들 멀티 모듈 구조로 구성함. 52 | 53 | ### Model Module (`model`) 54 | 55 | - 도메인의 핵심이 되는 모델들을 포함 56 | - 도메인 모델들이 가져야 하는 핵심 비즈니스 로직을 포함할 수 있음 57 | - 비즈니스 로직이 포함될 경우 단위 테스트 수준의 테스트 코드가 권장됨 58 | 59 | ### Service Module (`service`) 60 | 61 | - 비즈니스 로직 구현을 포함 62 | - persistence / network가 필요한 모듈과 직접 참조를 하지 않음 63 | - 비즈니스 로직을 검증하기 위한 테스트 코드를 반드시 포함하도록 함 64 | - 테스트 방법에 따라 테스트 코드들은 persistence 테스트 도구를 활용 가능 65 | - 외부 모듈에 노출할 Use Case와 모듈 내부에서만 사용 가능한 코드를 엄밀히 구분할 수 있도록 하여야 함 66 | 67 | ### Exception Module (`exception`) 68 | 69 | - `service`(혹은 `model`)의 비즈니스 로직에서 발생할 수 있는 예외들을 정의함 70 | 71 | ## Driving(Primary) Side 72 | 73 | ### API Module (`api`) 74 | 75 | - web / mobile 환경에서 사용자의 REST API 요청을 처리하기 위한 컨트롤러들을 포함 76 | - 컨트롤러 수준의 request / response 검증 테스트를 추천함 77 | 78 | ## Driven(Secondary) Side 79 | 80 | ### Infrastructure Module (`infrastructure`) 81 | 82 | - persistence / network 레이어 사용을 위한 규격을 포함 83 | - 직접 구현을 포함하지 않음 84 | - `service` 모듈에서의 테스트 용이성을 고려 85 | 86 | ### Repository Module (`repository-{type}`) 87 | 88 | - `infrastructure`에 정의된 persistence 규격을 구현함 89 | - RDB / NoSQL 등에 따라 적절한 type을 사용 (rdb? / jpa? / jdbc? / document?) 90 | 91 | ## Application Modules 92 | 93 | - Hexagonal Architecture에서 사용되고 있는 `application` 용어와 혼란이 있을 수 있음 94 | - 여기에서는 제공하는 기능에 따른 모듈 의존 관계와 서버 실행을 위한 환경 설정을 포함하는 모듈을 의미함 95 | 96 | ### Application Module (`application-{type}`) 97 | 98 | - `application-api`, `application-cron`과 같이 해당 어플리케이션 특성에 맞게 의존 관계 및 서버 실행을 위한 환경 설정을 포함함. 99 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/src/integrationTest/kotlin/team/flex/module/sample/corehr/company/department/DepartmentRepositoryIntegrationTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.department 6 | 7 | import org.assertj.core.api.Assertions.assertThat 8 | import org.junit.jupiter.api.Test 9 | import org.springframework.boot.test.autoconfigure.data.jdbc.DataJdbcTest 10 | import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase 11 | import org.springframework.test.context.TestConstructor 12 | import team.flex.module.sample.corehr.company.CompanyIdentity 13 | import team.flex.module.sample.corehr.company.department.repository.DepartmentEntity 14 | import team.flex.module.sample.corehr.company.department.repository.DepartmentJdbcRepository 15 | import team.flex.module.sample.corehr.company.department.repository.DepartmentRepository 16 | import team.flex.module.sample.corehr.company.of 17 | import java.time.Instant 18 | import java.time.temporal.ChronoUnit 19 | 20 | @DataJdbcTest 21 | @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) 22 | @TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL) 23 | class DepartmentRepositoryIntegrationTest( 24 | private val cut: DepartmentRepository, 25 | private val jdbcRepository: DepartmentJdbcRepository, 26 | ) { 27 | @Test 28 | fun crudTest() { 29 | jdbcRepository.findAll().also { 30 | assertThat(it).isEmpty() 31 | } 32 | 33 | val expected = 34 | DepartmentEntity( 35 | companyId = 1L, 36 | parentDepartmentId = null, 37 | name = "name", 38 | createdAt = Instant.now().truncatedTo(ChronoUnit.SECONDS), 39 | updatedAt = Instant.now().truncatedTo(ChronoUnit.SECONDS), 40 | ) 41 | 42 | val saved = 43 | jdbcRepository.save(expected).also { 44 | assertThat(it.id).isNotZero() 45 | } 46 | 47 | cut.findAllByCompanyIdentity( 48 | companyIdentity = CompanyIdentity.of(expected.companyId), 49 | ) 50 | .also { assertThat(it).hasSize(1) } 51 | .first() 52 | .also { 53 | it.assert(expected) 54 | } 55 | 56 | cut.findByDepartmentIdentity( 57 | companyIdentity = CompanyIdentity.of(expected.companyId), 58 | departmentIdentity = DepartmentIdentity.of(expected.departmentId), 59 | ) 60 | .also { 61 | it!!.assert(expected) 62 | } 63 | 64 | jdbcRepository.delete(saved) 65 | 66 | jdbcRepository.findAll().also { 67 | assertThat(it).isEmpty() 68 | } 69 | } 70 | 71 | private fun DepartmentModel.assert(other: DepartmentModel) { 72 | assertThat(companyId).isEqualTo(other.companyId) 73 | assertThat(name).isEqualTo(other.name) 74 | assertThat(createdAt).isEqualTo(other.createdAt) 75 | assertThat(updatedAt).isEqualTo(other.updatedAt) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/src/integrationTest/kotlin/team/flex/module/sample/corehr/employee/EmployeeRepositoryIntegrationTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.employee 6 | 7 | import org.assertj.core.api.Assertions.assertThat 8 | import org.junit.jupiter.api.Test 9 | import org.springframework.boot.test.autoconfigure.data.jdbc.DataJdbcTest 10 | import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase 11 | import org.springframework.test.context.TestConstructor 12 | import team.flex.module.sample.corehr.company.CompanyIdentity 13 | import team.flex.module.sample.corehr.company.of 14 | import team.flex.module.sample.corehr.employee.repository.EmployeeEntity 15 | import team.flex.module.sample.corehr.employee.repository.EmployeeJdbcRepository 16 | import team.flex.module.sample.corehr.employee.repository.EmployeeRepository 17 | import java.time.Instant 18 | import java.time.temporal.ChronoUnit 19 | 20 | @DataJdbcTest 21 | @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) 22 | @TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL) 23 | class EmployeeRepositoryIntegrationTest( 24 | private val cut: EmployeeRepository, 25 | private val jdbcRepository: EmployeeJdbcRepository, 26 | ) { 27 | @Test 28 | fun crudTest() { 29 | jdbcRepository.findAll().also { 30 | assertThat(it).isEmpty() 31 | } 32 | 33 | val expected = 34 | EmployeeEntity( 35 | companyId = 1, 36 | employeeNumber = "TEST_10001", 37 | name = "name", 38 | createdAt = Instant.now().truncatedTo(ChronoUnit.SECONDS), 39 | updatedAt = Instant.now().truncatedTo(ChronoUnit.SECONDS), 40 | ) 41 | 42 | val saved = 43 | jdbcRepository.save(expected).also { 44 | assertThat(it.id).isNotZero() 45 | } 46 | 47 | cut.findAllByCompanyIdentity( 48 | companyIdentity = CompanyIdentity.of(expected.companyId), 49 | ) 50 | .also { assertThat(it).hasSize(1) } 51 | .first() 52 | .also { actual -> 53 | actual.assert(expected) 54 | } 55 | 56 | cut.findByEmployeeIdentity( 57 | companyIdentity = CompanyIdentity.of(expected.companyId), 58 | employeeIdentity = EmployeeIdentity.of(expected.employeeId), 59 | ) 60 | .also { actual -> 61 | actual!!.assert(expected) 62 | } 63 | 64 | jdbcRepository.delete(saved) 65 | 66 | jdbcRepository.findAll().also { 67 | assertThat(it).isEmpty() 68 | } 69 | } 70 | 71 | private fun EmployeeModel.assert(expected: EmployeeModel) { 72 | assertThat(companyId).isEqualTo(expected.companyId) 73 | assertThat(employeeNumber).isEqualTo(expected.employeeNumber) 74 | assertThat(name).isEqualTo(expected.name) 75 | assertThat(createdAt).isEqualTo(expected.createdAt) 76 | assertThat(updatedAt).isEqualTo(expected.updatedAt) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /corehr/repository-jdbc/src/integrationTest/kotlin/team/flex/module/sample/corehr/company/jobrole/JobRoleRepositoryIntegrationTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.corehr.company.jobrole 6 | 7 | import org.assertj.core.api.Assertions.assertThat 8 | import org.junit.jupiter.api.Test 9 | import org.springframework.boot.test.autoconfigure.data.jdbc.DataJdbcTest 10 | import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase 11 | import org.springframework.context.annotation.Import 12 | import org.springframework.test.context.TestConstructor 13 | import team.flex.module.sample.corehr.company.CompanyIdentity 14 | import team.flex.module.sample.corehr.company.jobrole.repository.JobRoleEntity 15 | import team.flex.module.sample.corehr.company.jobrole.repository.JobRoleJdbcRepository 16 | import team.flex.module.sample.corehr.company.jobrole.repository.JobRoleRepository 17 | import team.flex.module.sample.corehr.company.jobrole.repository.JobRoleRepositoryAutoConfiguration 18 | import team.flex.module.sample.corehr.company.of 19 | import java.time.Instant 20 | import java.time.temporal.ChronoUnit 21 | 22 | @Import( 23 | JobRoleRepositoryAutoConfiguration::class, 24 | ) 25 | @DataJdbcTest 26 | @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) 27 | @TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL) 28 | class JobRoleRepositoryIntegrationTest( 29 | private val cut: JobRoleRepository, 30 | private val jdbcRepository: JobRoleJdbcRepository, 31 | ) { 32 | @Test 33 | fun crudTest() { 34 | jdbcRepository.findAll().also { 35 | assertThat(it).isEmpty() 36 | } 37 | 38 | val expected = 39 | JobRoleEntity( 40 | companyId = 1L, 41 | name = "name", 42 | createdAt = Instant.now().truncatedTo(ChronoUnit.SECONDS), 43 | updatedAt = Instant.now().truncatedTo(ChronoUnit.SECONDS), 44 | ) 45 | 46 | val saved = 47 | jdbcRepository.save(expected).also { 48 | assertThat(it.id).isNotZero() 49 | } 50 | 51 | cut.findAllByCompanyIdentity( 52 | companyIdentity = CompanyIdentity.of(expected.companyId), 53 | ) 54 | .also { assertThat(it).hasSize(1) } 55 | .first() 56 | .also { 57 | it.assert(expected) 58 | } 59 | 60 | cut.findByJobRoleIdentity( 61 | companyIdentity = CompanyIdentity.of(expected.companyId), 62 | jobRoleIdentity = JobRoleIdentity.of(expected.jobRoleId), 63 | ) 64 | .also { 65 | it!!.assert(expected) 66 | } 67 | 68 | jdbcRepository.delete(saved) 69 | 70 | jdbcRepository.findAll().also { 71 | assertThat(it).isEmpty() 72 | } 73 | } 74 | 75 | private fun JobRoleModel.assert(expected: JobRoleModel) { 76 | assertThat(companyId).isEqualTo(expected.companyId) 77 | assertThat(name).isEqualTo(expected.name) 78 | assertThat(createdAt).isEqualTo(expected.createdAt) 79 | assertThat(updatedAt).isEqualTo(expected.updatedAt) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH= 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /corehr/schema/src/main/resources/db/changelog/corehr/data.sql: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | insert into `company` (id, name, created_at, updated_at) values (1, '테스트 회사1', now(), now()); 6 | insert into `company` (id, name, created_at, updated_at) values (2, '테스트 회사2', now(), now()); 7 | 8 | insert into `employee` (id, company_id, employee_number, name, created_at, updated_at) values (1, 1, 'TCA_1001', '유저1-1', now(), now()); 9 | insert into `employee` (id, company_id, employee_number, name, created_at, updated_at) values (2, 1, 'TCA_1002', '유저1-2', now(), now()); 10 | insert into `employee` (id, company_id, employee_number, name, created_at, updated_at) values (3, 1, 'TCA_1003', '유저1-3', now(), now()); 11 | insert into `employee` (id, company_id, employee_number, name, created_at, updated_at) values (4, 1, 'TCA_1004', '유저1-4', now(), now()); 12 | insert into `employee` (id, company_id, employee_number, name, created_at, updated_at) values (5, 2, 'TCB_1001', '유저2-1', now(), now()); 13 | insert into `employee` (id, company_id, employee_number, name, created_at, updated_at) values (6, 2, 'TCB_1002', '유저2-2', now(), now()); 14 | insert into `employee` (id, company_id, employee_number, name, created_at, updated_at) values (7, 2, 'TCB_1003', '유저2-3', now(), now()); 15 | insert into `employee` (id, company_id, employee_number, name, created_at, updated_at) values (8, 2, 'TCB_1004', '유저2-4', now(), now()); 16 | 17 | insert into `job_role` (id, company_id, name, created_at, updated_at) values (1, 1, 'Product Engineer(Backend)', now(), now()); 18 | insert into `job_role` (id, company_id, name, created_at, updated_at) values (2, 1, 'Product Engineer(Frontend)', now(), now()); 19 | insert into `job_role` (id, company_id, name, created_at, updated_at) values (3, 1, 'Product Designer', now(), now()); 20 | insert into `job_role` (id, company_id, name, created_at, updated_at) values (4, 1, 'Product Manager', now(), now()); 21 | insert into `job_role` (id, company_id, name, created_at, updated_at) values (5, 2, 'Backend Developer', now(), now()); 22 | insert into `job_role` (id, company_id, name, created_at, updated_at) values (6, 2, 'Frontend Developer', now(), now()); 23 | 24 | insert into `department` (id, company_id, parent_id, name, created_at, updated_at) values (1, 1, null, 'CEO', now(), now()); 25 | insert into `department` (id, company_id, parent_id, name, created_at, updated_at) values (2, 1, 1, 'Core Squad', now(), now()); 26 | insert into `department` (id, company_id, parent_id, name, created_at, updated_at) values (3, 1, 1, 'Review Squad', now(), now()); 27 | insert into `department` (id, company_id, parent_id, name, created_at, updated_at) values (4 ,1, 1, 'Payroll Squad', now(), now()); 28 | insert into `department` (id, company_id, parent_id, name, created_at, updated_at) values (5 ,2, null, '대표', now(), now()); 29 | insert into `department` (id, company_id, parent_id, name, created_at, updated_at) values (6 ,2, 5, '개발실', now(), now()); 30 | insert into `department` (id, company_id, parent_id, name, created_at, updated_at) values (7, 2, 6, '서버 개발1팀', now(), now()); 31 | insert into `department` (id, company_id, parent_id, name, created_at, updated_at) values (8, 2, 6, '서버 개발2팀', now(), now()); 32 | insert into `department` (id, company_id, parent_id, name, created_at, updated_at) values (9, 2, 6, '앱 개발팀', now(), now()); 33 | -------------------------------------------------------------------------------- /payroll/service/src/main/kotlin/team/flex/module/sample/payroll/PayrollService.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.payroll 6 | 7 | import team.flex.module.sample.corehr.company.CompanyIdentity 8 | import team.flex.module.sample.corehr.employee.EmployeeIdentity 9 | import team.flex.module.sample.corehr.exception.EmployeeNotFoundException 10 | import team.flex.module.sample.payroll.repository.PayrollHistoryRepository 11 | import team.flex.module.sample.payroll.repository.PayrollRepository 12 | import java.time.Instant 13 | 14 | interface PayrollService { 15 | fun getHistory( 16 | companyIdentity: CompanyIdentity, 17 | employeeIdentity: EmployeeIdentity, 18 | ): PayrollHistory? 19 | 20 | fun addPayrollInfo( 21 | companyIdentity: CompanyIdentity, 22 | employeeIdentity: EmployeeIdentity, 23 | payrollAmount: Long, 24 | payDay: Int, 25 | ): Payroll 26 | 27 | // TODO: updatePayrollInfo 기능 추가 필요 28 | 29 | fun pay( 30 | companyIdentity: CompanyIdentity, 31 | employeeIdentity: EmployeeIdentity, 32 | ): PayrollHistory 33 | } 34 | 35 | internal class PayrollServiceImpl( 36 | private val payrollRepository: PayrollRepository, 37 | private val payrollHistoryRepository: PayrollHistoryRepository, 38 | ) : PayrollService { 39 | override fun getHistory( 40 | companyIdentity: CompanyIdentity, 41 | employeeIdentity: EmployeeIdentity, 42 | ): PayrollHistory? { 43 | return payrollHistoryRepository.findByEmployeeIdentity( 44 | companyIdentity = companyIdentity, 45 | employeeIdentity = employeeIdentity, 46 | ) 47 | } 48 | 49 | override fun addPayrollInfo( 50 | companyIdentity: CompanyIdentity, 51 | employeeIdentity: EmployeeIdentity, 52 | payrollAmount: Long, 53 | payDay: Int, 54 | ): Payroll { 55 | return payrollRepository.save( 56 | Payroll( 57 | id = 0L, 58 | employeeId = employeeIdentity.employeeId, 59 | companyId = companyIdentity.companyId, 60 | payday = payDay, 61 | payrollAmount = payrollAmount, 62 | createdAt = Instant.now(), 63 | updatedAt = Instant.now(), 64 | ), 65 | ) 66 | } 67 | 68 | override fun pay( 69 | companyIdentity: CompanyIdentity, 70 | employeeIdentity: EmployeeIdentity, 71 | ): PayrollHistory { 72 | val now = Instant.now() 73 | val payrollInfo = 74 | payrollRepository.findByEmployeeIdentity( 75 | companyIdentity = companyIdentity, 76 | employeeIdentity = employeeIdentity, 77 | ) ?: throw EmployeeNotFoundException() 78 | 79 | return try { 80 | payrollHistoryRepository.save( 81 | PayrollHistory( 82 | employeeId = employeeIdentity.employeeId, 83 | companyId = companyIdentity.companyId, 84 | payrollId = payrollInfo.id, 85 | payDatetime = now, 86 | payrollAmount = payrollInfo.payrollAmount, 87 | ), 88 | ) 89 | } catch (e: Exception) { 90 | throw RuntimeException( 91 | "Failed to process payroll for employee ${employeeIdentity.employeeId} at company ${companyIdentity.companyId}", 92 | e, 93 | ) 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /payroll/api/src/main/kotlin/team/flex/module/sample/payroll/PayrollApiController.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 flex Inc. - All Rights Reserved. 3 | */ 4 | 5 | package team.flex.module.sample.payroll 6 | 7 | import io.swagger.v3.oas.annotations.Operation 8 | import org.springframework.web.bind.annotation.GetMapping 9 | import org.springframework.web.bind.annotation.PathVariable 10 | import org.springframework.web.bind.annotation.PostMapping 11 | import org.springframework.web.bind.annotation.RequestBody 12 | import org.springframework.web.bind.annotation.RequestMapping 13 | import org.springframework.web.bind.annotation.RestController 14 | import team.flex.module.sample.corehr.company.CompanyIdentity 15 | import team.flex.module.sample.corehr.company.of 16 | import team.flex.module.sample.corehr.employee.EmployeeIdentity 17 | import team.flex.module.sample.corehr.employee.of 18 | import team.flex.module.sample.payroll.dto.PayRequest 19 | import team.flex.module.sample.payroll.dto.PayrollHistoryResponse 20 | import team.flex.module.sample.payroll.dto.PayrollInfoRequest 21 | import team.flex.module.sample.payroll.dto.PayrollInfoResponse 22 | 23 | @RestController 24 | @RequestMapping("/api/v2/payroll") 25 | class PayrollApiController( 26 | private val service: PayrollService, 27 | ) { 28 | @GetMapping("/companies/{companyId}/employees/{employeeId}") 29 | @Operation( 30 | summary = "급여 지급 기록 조회 API", 31 | operationId = "getPayrollHistoryByEmployeeId", 32 | ) 33 | fun getPayrollHistoryByEmployeeId( 34 | @PathVariable companyId: Long, 35 | @PathVariable employeeId: Long, 36 | ): PayrollHistoryResponse { 37 | return service.getHistory( 38 | CompanyIdentity.of(companyId), 39 | EmployeeIdentity.Companion.of(employeeId), 40 | ).let { 41 | if (it == null) { 42 | PayrollHistoryResponse.NotExist 43 | } else { 44 | PayrollHistoryResponse.Exist( 45 | employeeId = it.employeeId, 46 | companyId = it.companyId, 47 | payDatetime = it.payDatetime, 48 | payrollAmount = it.payrollAmount, 49 | ) 50 | } 51 | } 52 | } 53 | 54 | @PostMapping 55 | @Operation( 56 | summary = "급여 정보 저장 API", 57 | operationId = "savePayrollInfo", 58 | ) 59 | fun savePayrollInfo( 60 | @RequestBody request: PayrollInfoRequest, 61 | ): PayrollInfoResponse { 62 | return service.addPayrollInfo( 63 | companyIdentity = CompanyIdentity.of(request.companyId), 64 | employeeIdentity = EmployeeIdentity.Companion.of(request.employeeId), 65 | payrollAmount = request.payrollAmount, 66 | payDay = request.payday, 67 | ).let { 68 | PayrollInfoResponse( 69 | employeeId = it.employeeId, 70 | companyId = it.companyId, 71 | payday = it.payday, 72 | payrollAmount = it.payrollAmount, 73 | createdAt = it.createdAt, 74 | updatedAt = it.updatedAt, 75 | ) 76 | } 77 | } 78 | 79 | @PostMapping("/pay") 80 | @Operation( 81 | summary = "급여 지급 API", 82 | operationId = "pay", 83 | ) 84 | fun pay( 85 | @RequestBody request: PayRequest, 86 | ): PayrollHistoryResponse { 87 | return service.pay( 88 | CompanyIdentity.of(request.companyId), 89 | EmployeeIdentity.Companion.of(request.employeeId), 90 | ).let { 91 | PayrollHistoryResponse.Exist( 92 | employeeId = it.employeeId, 93 | companyId = it.companyId, 94 | payDatetime = it.payDatetime, 95 | payrollAmount = it.payrollAmount, 96 | ) 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH="\\\"\\\"" 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | if ! command -v java >/dev/null 2>&1 137 | then 138 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 139 | 140 | Please set the JAVA_HOME variable in your environment to match the 141 | location of your Java installation." 142 | fi 143 | fi 144 | 145 | # Increase the maximum file descriptors if we can. 146 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 147 | case $MAX_FD in #( 148 | max*) 149 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 150 | # shellcheck disable=SC2039,SC3045 151 | MAX_FD=$( ulimit -H -n ) || 152 | warn "Could not query maximum file descriptor limit" 153 | esac 154 | case $MAX_FD in #( 155 | '' | soft) :;; #( 156 | *) 157 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 158 | # shellcheck disable=SC2039,SC3045 159 | ulimit -n "$MAX_FD" || 160 | warn "Could not set maximum file descriptor limit to $MAX_FD" 161 | esac 162 | fi 163 | 164 | # Collect all arguments for the java command, stacking in reverse order: 165 | # * args from the command line 166 | # * the main class name 167 | # * -classpath 168 | # * -D...appname settings 169 | # * --module-path (only if needed) 170 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 171 | 172 | # For Cygwin or MSYS, switch paths to Windows format before running java 173 | if "$cygwin" || "$msys" ; then 174 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 175 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 176 | 177 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 178 | 179 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 180 | for arg do 181 | if 182 | case $arg in #( 183 | -*) false ;; # don't mess with options #( 184 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 185 | [ -e "$t" ] ;; #( 186 | *) false ;; 187 | esac 188 | then 189 | arg=$( cygpath --path --ignore --mixed "$arg" ) 190 | fi 191 | # Roll the args list around exactly as many times as the number of 192 | # args, so each arg winds up back in the position where it started, but 193 | # possibly modified. 194 | # 195 | # NB: a `for` loop captures its iteration list before it begins, so 196 | # changing the positional parameters here affects neither the number of 197 | # iterations, nor the values presented in `arg`. 198 | shift # remove old arg 199 | set -- "$@" "$arg" # push replacement arg 200 | done 201 | fi 202 | 203 | 204 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 205 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 206 | 207 | # Collect all arguments for the java command: 208 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 209 | # and any embedded shellness will be escaped. 210 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 211 | # treated as '${Hostname}' itself on the command line. 212 | 213 | set -- \ 214 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 215 | -classpath "$CLASSPATH" \ 216 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ 217 | "$@" 218 | 219 | # Stop when "xargs" is not available. 220 | if ! command -v xargs >/dev/null 2>&1 221 | then 222 | die "xargs is not available" 223 | fi 224 | 225 | # Use "xargs" to parse quoted args. 226 | # 227 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 228 | # 229 | # In Bash we could simply go: 230 | # 231 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 232 | # set -- "${ARGS[@]}" "$@" 233 | # 234 | # but POSIX shell has neither arrays nor command substitution, so instead we 235 | # post-process each arg (as a line of input to sed) to backslash-escape any 236 | # character that might be a shell metacharacter, then use eval to reverse 237 | # that process (while maintaining the separation between arguments), and wrap 238 | # the whole thing up as a single "set" statement. 239 | # 240 | # This will of course break if any of these variables contains a newline or 241 | # an unmatched quote. 242 | # 243 | 244 | eval "set -- $( 245 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 246 | xargs -n1 | 247 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 248 | tr '\n' ' ' 249 | )" '"$@"' 250 | 251 | exec "$JAVACMD" "$@" 252 | -------------------------------------------------------------------------------- /corehr/docs/architecture_guide.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'https://plantuml.com/component-diagram 3 | 4 | left to right direction 5 | 6 | title "Hexagonal Application - Architecture Guide" 7 | 8 | card Legend { 9 | card a 10 | card b 11 | card c 12 | card d 13 | card e 14 | card f 15 | card g 16 | card h 17 | 18 | a --> b : Call (gradle implement) 19 | c ..> d : Extend (gradle implement) 20 | e -[dotted]-> f : Reference (gradle api) 21 | g -[dotted]- h : Indirect Relation 22 | } 23 | 24 | actor user as "user" 25 | actor flex_admin as "flex admin" 26 | 27 | hexagon application as "Hexagonal Application" { 28 | frame "Inbound" { 29 | portin web 30 | portin raccoon 31 | 32 | rectangle inport as "in-port-internal" 33 | rectangle protocol as "protocol (in-port로 마이그레이션 - optional)" 34 | rectangle api as "api" 35 | rectangle api_internal as "api-internal" 36 | rectangle api_operation as "api-operation" 37 | rectangle api_internal_client as "api-internal-client" 38 | 39 | web --> api 40 | raccoon --> api_operation 41 | 42 | protocol --> inport 43 | 44 | api_internal ..> inport 45 | api_internal_client .left.> inport 46 | } 47 | 48 | frame "XX" { 49 | rectangle model as "model (dto?)" 50 | rectangle service as "service (use-case)" 51 | 52 | rectangle exception 53 | 54 | service .[dotted]left.> model 55 | model .[dotted]left.> exception 56 | service .[dotted]left.> exception 57 | } 58 | 59 | frame "Outbound" { 60 | rectangle infrastructure as "infrastructure (out-port)" 61 | rectangle repository as "repository-{type}" 62 | rectangle message_queue as "message-queue-{type}" 63 | 64 | repository .right.> infrastructure 65 | message_queue .right.> infrastructure 66 | } 67 | 68 | api -[dotted]right-> exception 69 | api_internal -[dotted]right-> exception 70 | api_operation -[dotted]right-> exception 71 | inport -[dotted]right-> exception 72 | 73 | inport ..> model : optional, 새로운 repo는 연결을 끊는 것이 default? 74 | 75 | api --> service 76 | api_internal --> service 77 | api_operation --> service 78 | 79 | infrastructure .[dotted]left.> model 80 | 81 | service --> infrastructure 82 | 83 | api_internal -[dotted]- api_internal_client 84 | } 85 | 86 | hexagon another_application as "Another Application" { 87 | frame another_inbound as "Inbound" { 88 | portin internal 89 | 90 | rectangle another_inport as "in-port-internal" 91 | rectangle another_api as "api" 92 | rectangle another_api_internal as "api-internal" 93 | rectangle another_api_operation as "api-operation" 94 | rectangle another_api_internal_client as "api-internal-client" 95 | 96 | another_inport <.. another_api_internal 97 | 98 | another_inport <.left. another_api_internal_client 99 | } 100 | 101 | frame another_domain as "??" { 102 | rectangle another_model as "model" 103 | rectangle another_service as "service (user-case)" 104 | rectangle another_exception as "exception" 105 | 106 | another_service .[dotted]left.> another_model 107 | another_service .[dotted]left.> another_exception 108 | } 109 | 110 | frame another_outbound as "Outbound" { 111 | rectangle another_infrastructure as "infrastructure (out-port)" 112 | rectangle another_repository as "repository-{type}" 113 | } 114 | 115 | another_api -[dotted]right-> another_exception 116 | another_api_internal -[dotted]right-> another_exception 117 | another_api_operation -[dotted]right-> another_exception 118 | 119 | another_api --> another_service 120 | another_api_internal --> another_service 121 | another_api_operation --> another_service 122 | 123 | another_infrastructure .[dotted]left.> another_model 124 | 125 | another_service --> another_infrastructure 126 | another_repository .right.> another_infrastructure 127 | 128 | another_api_internal -[dotted]- another_api_internal_client 129 | } 130 | 131 | application -[hidden]- another_application 132 | 133 | user --> web 134 | flex_admin --> raccoon 135 | 136 | 'Hexagonal <> Another 137 | infrastructure --> internal : ???? 138 | internal --> another_inport 139 | 140 | @enduml 141 | 142 | @startuml 143 | 'https://plantuml.com/component-diagram 144 | left to right direction 145 | 146 | title "Hexagonal Application - Code Naming Guide" 147 | 148 | card leg as "Legend" { 149 | card a 150 | card b 151 | card c 152 | card d 153 | card e 154 | card f 155 | card g 156 | card h 157 | 158 | a --> b : Call 159 | c ..> d : Extend 160 | e -[dotted]-> f : Reference 161 | g -[dotted]- h : Indirect Relation 162 | } 163 | 164 | hexagon application as "Hexagonal Application" { 165 | frame "Inbound" { 166 | package api as "api" { 167 | rectangle DomainApiController 168 | rectangle DomainApiRequest as "data class DomainApiRequest" 169 | rectangle DomainApiResponse as "data class DomainApiResponse" 170 | 171 | DomainApiController ..> DomainApiRequest 172 | DomainApiController ..> DomainApiResponse 173 | } 174 | 175 | package api_internal as "api-internal" { 176 | rectangle DomainInternalApiController 177 | } 178 | 179 | package operation_api as "api-operation" { 180 | rectangle DomainOperationApiController 181 | } 182 | 183 | package api_internal_client as "api-internal-client" { 184 | rectangle DomainInternalApiClient 185 | rectangle DomainInternalApiDto 186 | 187 | DomainInternalApiClient .[dotted].> DomainInternalApiDto 188 | } 189 | 190 | package in_port_internal as "in-port-internal" { 191 | rectangle DomainApi as "interface DomainApi" 192 | } 193 | 194 | DomainInternalApiController -[dotted]- DomainInternalApiClient 195 | 196 | DomainInternalApiClient ..> DomainApi 197 | } 198 | 199 | frame "??" { 200 | package model { 201 | rectangle DomainIdentity as "interface DomainIdentity" 202 | rectangle DomainProps as "interface DomainProps" 203 | rectangle DomainModel { 204 | rectangle domainIdentity 205 | rectangle domainProps 206 | } 207 | rectangle Domain as "class Domain" 208 | 209 | DomainModel -[dotted]-> DomainIdentity : composite 210 | DomainModel -[dotted]-> DomainProps : composite 211 | Domain ..> DomainModel 212 | } 213 | 214 | package service as "service (user-case)" { 215 | rectangle DomainCreateUseCase as "DomainCreateUseCase (public interface)" 216 | rectangle DomainReadUseCase as "DomainReadUseCase (public interface)" 217 | rectangle DomainQueryUseCase as "DomainQueryUseCase (public interface)" 218 | 219 | rectangle DomainService 220 | rectangle DomainQueryService 221 | 222 | DomainService ..> DomainCreateUseCase 223 | DomainService ..> DomainReadUseCase 224 | DomainQueryService ..> DomainQueryUseCase 225 | } 226 | 227 | package exception { 228 | rectangle DomainXXException 229 | } 230 | 231 | DomainCreateUseCase -[dotted]-> DomainModel 232 | DomainReadUseCase -[dotted]-> DomainModel 233 | DomainQueryUseCase -[dotted]-> DomainModel 234 | 235 | DomainService -[dotted]left-> DomainXXException 236 | } 237 | 238 | frame "Outbound" { 239 | package infrastructure as "infrastructure (out-port)" { 240 | rectangle DomainRepository 241 | } 242 | 243 | package repository as "repository-jpa" { 244 | rectangle DomainJpaRepository 245 | rectangle DomainJpaEntity 246 | rectangle DomainRepositoryImpl 247 | 248 | DomainJpaRepository -[dotted]-> DomainJpaEntity 249 | } 250 | 251 | DomainRepositoryImpl --> DomainJpaRepository 252 | 253 | DomainJpaRepository .right.> DomainRepository 254 | } 255 | 256 | DomainApi -[dotted]right-> DomainModel 257 | 258 | DomainApiController --> DomainCreateUseCase 259 | DomainApiController --> DomainReadUseCase 260 | DomainApiController --> DomainQueryUseCase 261 | 262 | DomainInternalApiController --> DomainCreateUseCase 263 | DomainInternalApiController --> DomainReadUseCase 264 | DomainInternalApiController --> DomainQueryUseCase 265 | 266 | DomainRepositoryImpl ..> DomainRepository 267 | 268 | DomainRepository .[dotted]left.> DomainModel 269 | 270 | DomainJpaEntity ..> DomainModel : (Optional) 271 | 272 | DomainService --> DomainRepository 273 | } 274 | 275 | hexagon another_application as "Another Application" { 276 | frame another_inbound as "Inbound" { 277 | package another_in_port_internal as "in-port-internal" { 278 | rectangle AnotherDomainApi 279 | } 280 | 281 | package another_api_internal_client as "api-internal-client" { 282 | rectangle AnotherDomainInternalApiClient 283 | } 284 | 285 | another_api_internal_client ..> another_in_port_internal 286 | } 287 | } 288 | 289 | application -[hidden]- another_application 290 | 291 | infrastructure ..> another_in_port_internal 292 | 293 | @enduml 294 | 295 | @startuml 296 | 'https://plantuml.com/component-diagram 297 | left to right direction 298 | 299 | title "Hexagonal Application - Code Naming Guide" 300 | 301 | card leg as "Legend" { 302 | card a 303 | card b 304 | card c 305 | card d 306 | card e 307 | card f 308 | card g 309 | card h 310 | 311 | a --> b : Call 312 | c ..> d : Extend 313 | e -[dotted]-> f : Reference 314 | g -[dotted]-> h : Indirect Relation 315 | } 316 | 317 | hexagon application as "Hexagonal Application" { 318 | frame "Inbound" { 319 | package api as "api" { 320 | rectangle HexagonalDomainApiController 321 | } 322 | 323 | package internal_api as "internal-api" { 324 | rectangle HexagonalDomainInternalApiController 325 | } 326 | 327 | package operation_api as "operation-api" { 328 | rectangle HexagonalDomainOperationApiController 329 | } 330 | 331 | package internal_api_client as "internal-api-client" { 332 | rectangle HexagonalDomainInternalApiClient 333 | } 334 | 335 | package protocol as "protocol (in-port)" { 336 | rectangle HexagonalDomainXXService 337 | } 338 | 339 | package exception { 340 | rectangle HexagonalDomainXXException 341 | } 342 | 343 | HexagonalDomainInternalApiController -[dotted]- HexagonalDomainInternalApiClient 344 | 345 | HexagonalDomainInternalApiController ..> HexagonalDomainXXService 346 | HexagonalDomainInternalApiClient ..> HexagonalDomainXXService 347 | } 348 | 349 | frame "Domain" { 350 | package model { 351 | rectangle HexagonalDomainIdentity 352 | rectangle HexagonalDomainModel 353 | rectangle HexagonalDomainEntity 354 | 355 | HexagonalDomainEntity ..> HexagonalDomainIdentity 356 | HexagonalDomainEntity ..> HexagonalDomainModel 357 | } 358 | 359 | package service as "service (user-case)" { 360 | rectangle HexagonalDomainUseCase 361 | rectangle HexagonalDomainXXServiceImpl 362 | 363 | HexagonalDomainXXServiceImpl ..> HexagonalDomainUseCase : ???? 364 | } 365 | 366 | HexagonalDomainXXServiceImpl -[dotted]right-> HexagonalDomainEntity 367 | HexagonalDomainXXService -[dotted]right-> HexagonalDomainEntity 368 | } 369 | 370 | frame "Outbound" { 371 | package infrastructure as "infrastructure (out-port)" { 372 | rectangle HexagonalDomainRepository 373 | rectangle AnotherDomainService 374 | } 375 | 376 | package repository as "repository-{type}" { 377 | rectangle HexagonalDomainRepositoryImpl 378 | rectangle HexagonalResponse 379 | 380 | HexagonalDomainRepositoryImpl -[dotted]-> HexagonalResponse 381 | } 382 | 383 | HexagonalDomainRepositoryImpl .right.> HexagonalDomainRepository 384 | } 385 | 386 | HexagonalDomainApiController -up-> HexagonalDomainXXServiceImpl 387 | HexagonalDomainInternalApiController -up-> HexagonalDomainXXServiceImpl 388 | HexagonalDomainOperationApiController -up-> HexagonalDomainXXServiceImpl 389 | 390 | HexagonalDomainRepository .up[dotted].> HexagonalDomainEntity 391 | 392 | HexagonalDomainXXServiceImpl -up-> HexagonalDomainRepository 393 | } 394 | 395 | hexagon another_application as "Another Application" { 396 | frame another_inbound as "Inbound" { 397 | rectangle AnotherDomainInternalApiClient as "internal-api-client" 398 | rectangle AnotherDomainProtocolService as "protocol (in-port)" 399 | 400 | AnotherDomainInternalApiClient ..> AnotherDomainProtocolService 401 | } 402 | } 403 | 404 | application -[hidden]- another_application 405 | 406 | AnotherDomainService ..> AnotherDomainProtocolService : ???? 407 | 408 | @enduml 409 | --------------------------------------------------------------------------------