├── axonserver
├── data
│ └── .gitkeep
└── config
│ └── axonserver.yaml
├── docker
├── .env
└── docker_postgres_init.sql
├── backend
├── gradle.properties
├── clients
│ ├── gradle.properties
│ ├── grpc
│ │ ├── src
│ │ │ └── main
│ │ │ │ ├── resources
│ │ │ │ ├── certs
│ │ │ │ │ └── server-truststore.jks
│ │ │ │ └── application.yaml
│ │ │ │ └── kotlin
│ │ │ │ └── com
│ │ │ │ └── novatecgmbh
│ │ │ │ └── grpc
│ │ │ │ └── client
│ │ │ │ └── demo
│ │ │ │ ├── GrpcDemoClient.kt
│ │ │ │ └── commands
│ │ │ │ └── UserCommands.kt
│ │ └── build.gradle.kts
│ ├── rsocket
│ │ ├── src
│ │ │ └── main
│ │ │ │ ├── resources
│ │ │ │ └── application.yaml
│ │ │ │ └── kotlin
│ │ │ │ └── com
│ │ │ │ └── novatecgmbh
│ │ │ │ └── rsocket
│ │ │ │ └── client
│ │ │ │ └── demo
│ │ │ │ ├── RSocketDemoClient.kt
│ │ │ │ └── commands
│ │ │ │ └── ChatService.kt
│ │ └── build.gradle.kts
│ └── settings.gradle.kts
├── apis
│ ├── grpc
│ │ ├── gradle.properties
│ │ ├── src
│ │ │ └── main
│ │ │ │ ├── resources
│ │ │ │ ├── certs
│ │ │ │ │ ├── server-keystore.p12
│ │ │ │ │ ├── server-truststore.jks
│ │ │ │ │ ├── server-csr.conf
│ │ │ │ │ ├── generate.sh
│ │ │ │ │ └── server.csr
│ │ │ │ └── application.yaml
│ │ │ │ └── kotlin
│ │ │ │ └── com
│ │ │ │ └── novatecgmbh
│ │ │ │ └── eventsourcing
│ │ │ │ └── axon
│ │ │ │ ├── project
│ │ │ │ └── LocalDateExtension.kt
│ │ │ │ └── GrpcApi.kt
│ │ └── build.gradle.kts
│ ├── gradle.properties
│ ├── graphql
│ │ ├── src
│ │ │ └── main
│ │ │ │ ├── resources
│ │ │ │ ├── graphql
│ │ │ │ │ ├── base.graphqls
│ │ │ │ │ ├── user.graphqls
│ │ │ │ │ └── company.graphqls
│ │ │ │ └── application.yaml
│ │ │ │ └── kotlin
│ │ │ │ └── com
│ │ │ │ └── novatecgmbh
│ │ │ │ └── eventsourcing
│ │ │ │ └── axon
│ │ │ │ ├── GraphQlApi.kt
│ │ │ │ ├── application
│ │ │ │ └── config
│ │ │ │ │ ├── WebMvcConfig.kt
│ │ │ │ │ └── CorsConfig.kt
│ │ │ │ ├── company
│ │ │ │ └── company
│ │ │ │ │ └── graphql
│ │ │ │ │ └── CompanyMutationsController.kt
│ │ │ │ └── project
│ │ │ │ └── participant
│ │ │ │ └── graphql
│ │ │ │ └── ParticipantMutationsController.kt
│ │ └── build.gradle.kts
│ ├── rest
│ │ ├── src
│ │ │ ├── test
│ │ │ │ └── kotlin
│ │ │ │ │ └── com
│ │ │ │ │ └── novatecgmbh
│ │ │ │ │ └── eventsourcing
│ │ │ │ │ └── axon
│ │ │ │ │ └── demo
│ │ │ │ │ └── DemoApplicationTests.kt
│ │ │ └── main
│ │ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── novatecgmbh
│ │ │ │ │ └── eventsourcing
│ │ │ │ │ └── axon
│ │ │ │ │ ├── RestApi.kt
│ │ │ │ │ ├── project
│ │ │ │ │ ├── task
│ │ │ │ │ │ └── web
│ │ │ │ │ │ │ └── dto
│ │ │ │ │ │ │ ├── UnassignTaskDto.kt
│ │ │ │ │ │ │ ├── RenameTaskDto.kt
│ │ │ │ │ │ │ ├── AssignTaskDto.kt
│ │ │ │ │ │ │ ├── AddTodoDto.kt
│ │ │ │ │ │ │ ├── RescheduleTaskDto.kt
│ │ │ │ │ │ │ └── CreateTaskDto.kt
│ │ │ │ │ ├── project
│ │ │ │ │ │ └── web
│ │ │ │ │ │ │ └── dto
│ │ │ │ │ │ │ ├── RenameProjectDto.kt
│ │ │ │ │ │ │ ├── RescheduleProjectDto.kt
│ │ │ │ │ │ │ ├── UpdateProjectDto.kt
│ │ │ │ │ │ │ └── CreateProjectDto.kt
│ │ │ │ │ └── participant
│ │ │ │ │ │ └── web
│ │ │ │ │ │ └── dto
│ │ │ │ │ │ └── CreateParticipantDto.kt
│ │ │ │ │ ├── user
│ │ │ │ │ └── user
│ │ │ │ │ │ └── web
│ │ │ │ │ │ └── dto
│ │ │ │ │ │ ├── RenameUserDto.kt
│ │ │ │ │ │ └── RegisterUserDto.kt
│ │ │ │ │ ├── company
│ │ │ │ │ ├── company
│ │ │ │ │ │ └── web
│ │ │ │ │ │ │ └── dto
│ │ │ │ │ │ │ └── CreateCompanyDto.kt
│ │ │ │ │ └── employee
│ │ │ │ │ │ └── web
│ │ │ │ │ │ └── dto
│ │ │ │ │ │ └── CreateEmployeeDto.kt
│ │ │ │ │ └── application
│ │ │ │ │ └── config
│ │ │ │ │ ├── WebMvcConfig.kt
│ │ │ │ │ └── CorsConfig.kt
│ │ │ │ └── resources
│ │ │ │ └── application.yml
│ │ └── build.gradle.kts
│ ├── rsocket
│ │ ├── src
│ │ │ └── main
│ │ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── novatecgmbh
│ │ │ │ │ └── eventsourcing
│ │ │ │ │ └── axon
│ │ │ │ │ ├── RsocketApi.kt
│ │ │ │ │ ├── project
│ │ │ │ │ ├── task
│ │ │ │ │ │ └── rsocket
│ │ │ │ │ │ │ └── dto
│ │ │ │ │ │ │ ├── UnassignTaskDto.kt
│ │ │ │ │ │ │ ├── RenameTaskDto.kt
│ │ │ │ │ │ │ ├── RemoveTodoDto.kt
│ │ │ │ │ │ │ ├── MarkTodoAsDoneDto.kt
│ │ │ │ │ │ │ ├── AssignTaskDto.kt
│ │ │ │ │ │ │ ├── AddTodoDto.kt
│ │ │ │ │ │ │ ├── RescheduleTaskDto.kt
│ │ │ │ │ │ │ └── CreateTaskDto.kt
│ │ │ │ │ ├── project
│ │ │ │ │ │ └── rsocket
│ │ │ │ │ │ │ └── dto
│ │ │ │ │ │ │ ├── RenameProjectDto.kt
│ │ │ │ │ │ │ ├── RescheduleProjectDto.kt
│ │ │ │ │ │ │ └── CreateProjectDto.kt
│ │ │ │ │ └── participant
│ │ │ │ │ │ └── rsocket
│ │ │ │ │ │ └── dto
│ │ │ │ │ │ └── CreateParticipantDto.kt
│ │ │ │ │ ├── company
│ │ │ │ │ ├── company
│ │ │ │ │ │ └── rsocket
│ │ │ │ │ │ │ └── dtos
│ │ │ │ │ │ │ └── CreateCompanyDto.kt
│ │ │ │ │ └── employee
│ │ │ │ │ │ └── rsocket
│ │ │ │ │ │ └── dtos
│ │ │ │ │ │ └── CreateEmployeeDto.kt
│ │ │ │ │ └── application
│ │ │ │ │ └── config
│ │ │ │ │ └── TemporaryWorkaroundConfig.kt
│ │ │ │ └── resources
│ │ │ │ └── application.yml
│ │ └── rsc-collection
│ │ │ └── authenticate.sh
│ ├── grpc-lib
│ │ ├── src
│ │ │ └── main
│ │ │ │ └── proto
│ │ │ │ ├── userservice.proto
│ │ │ │ └── projectservice.proto
│ │ └── build.gradle.kts
│ ├── common
│ │ ├── build.gradle.kts
│ │ └── src
│ │ │ └── main
│ │ │ └── kotlin
│ │ │ └── com
│ │ │ └── novatecgmbh
│ │ │ └── eventsourcing
│ │ │ └── axon
│ │ │ └── application
│ │ │ └── security
│ │ │ ├── CustomUserAuthenticationConverter.kt
│ │ │ └── CustomUserDetailsService.kt
│ └── settings.gradle.kts
├── services
│ ├── company
│ │ ├── application
│ │ │ └── src
│ │ │ │ ├── main
│ │ │ │ ├── resources
│ │ │ │ │ ├── db
│ │ │ │ │ │ └── migration
│ │ │ │ │ │ │ ├── h2
│ │ │ │ │ │ │ └── V1__INITIAL_SCHEMA.sql
│ │ │ │ │ │ │ └── postgres
│ │ │ │ │ │ │ ├── V4__ID_MAPPING_TABLE.sql
│ │ │ │ │ │ │ ├── V2__COMMAND_MODEL_TABLES.sql
│ │ │ │ │ │ │ ├── V3__QUERY_MODEL_TABLE.sql
│ │ │ │ │ │ │ └── V1__AXON_TABLES.sql
│ │ │ │ │ └── application.yaml
│ │ │ │ └── kotlin
│ │ │ │ │ └── com
│ │ │ │ │ └── novatecgmbh
│ │ │ │ │ └── eventsourcing
│ │ │ │ │ └── axon
│ │ │ │ │ ├── CompanyApplication.kt
│ │ │ │ │ └── company
│ │ │ │ │ ├── company
│ │ │ │ │ └── query
│ │ │ │ │ │ ├── CompanyProjectionRepository.kt
│ │ │ │ │ │ ├── CompanyProjection.kt
│ │ │ │ │ │ └── CompanyQueryHandler.kt
│ │ │ │ │ ├── references
│ │ │ │ │ ├── ReferenceCheckerService.kt
│ │ │ │ │ └── RootContextIdMappingRepository.kt
│ │ │ │ │ └── employee
│ │ │ │ │ ├── command
│ │ │ │ │ └── view
│ │ │ │ │ │ ├── EmployeeUniqueKeyRepository.kt
│ │ │ │ │ │ ├── EmployeeUniqueKeyProjector.kt
│ │ │ │ │ │ └── EmployeeUniqueKeyProjection.kt
│ │ │ │ │ └── query
│ │ │ │ │ ├── EmployeeProjectionRepository.kt
│ │ │ │ │ └── EmployeeQueryHandler.kt
│ │ │ │ └── test
│ │ │ │ ├── resources
│ │ │ │ └── application.yml
│ │ │ │ └── kotlin
│ │ │ │ └── com
│ │ │ │ └── novatecgmbh
│ │ │ │ └── eventsourcing
│ │ │ │ └── axon
│ │ │ │ └── CompanyApplicationTests.kt
│ │ ├── gradle.properties
│ │ ├── api
│ │ │ ├── src
│ │ │ │ └── main
│ │ │ │ │ └── kotlin
│ │ │ │ │ └── com
│ │ │ │ │ └── novatecgmbh
│ │ │ │ │ └── eventsourcing
│ │ │ │ │ └── axon
│ │ │ │ │ └── company
│ │ │ │ │ ├── company
│ │ │ │ │ └── api
│ │ │ │ │ │ ├── Events.kt
│ │ │ │ │ │ ├── Queries.kt
│ │ │ │ │ │ ├── ValueObjects.kt
│ │ │ │ │ │ └── Commands.kt
│ │ │ │ │ └── employee
│ │ │ │ │ └── api
│ │ │ │ │ ├── ValueObjects.kt
│ │ │ │ │ ├── Queries.kt
│ │ │ │ │ └── Events.kt
│ │ │ └── build.gradle.kts
│ │ └── settings.gradle.kts
│ ├── project
│ │ ├── application
│ │ │ └── src
│ │ │ │ ├── main
│ │ │ │ ├── resources
│ │ │ │ │ ├── db
│ │ │ │ │ │ └── migration
│ │ │ │ │ │ │ ├── h2
│ │ │ │ │ │ │ └── V1__INITIAL_SCHEMA.sql
│ │ │ │ │ │ │ └── postgres
│ │ │ │ │ │ │ ├── V7__ADD_TASK_ASSIGNEE.sql
│ │ │ │ │ │ │ ├── V8__ADD_EMAIL_AND_PHONE.sql
│ │ │ │ │ │ │ ├── V9__ADD_NAME_AND_COMPANY_TO_TASK.sql
│ │ │ │ │ │ │ ├── V5__ID_MAPPING_TABLE.sql
│ │ │ │ │ │ │ ├── V4__ACCESS_CONTROL_LIST_TABLE.sql
│ │ │ │ │ │ │ ├── V6__ADD_TASK_TODOS.sql
│ │ │ │ │ │ │ ├── V2__COMMAND_MODEL_TABLES.sql
│ │ │ │ │ │ │ └── V1__AXON_TABLES.sql
│ │ │ │ │ └── application.yaml
│ │ │ │ └── kotlin
│ │ │ │ │ └── com
│ │ │ │ │ └── novatecgmbh
│ │ │ │ │ └── eventsourcing
│ │ │ │ │ └── axon
│ │ │ │ │ ├── ProjectApplication.kt
│ │ │ │ │ └── project
│ │ │ │ │ ├── project
│ │ │ │ │ ├── query
│ │ │ │ │ │ └── ProjectProjectionRepository.kt
│ │ │ │ │ └── command
│ │ │ │ │ │ └── InternalCommands.kt
│ │ │ │ │ ├── participant
│ │ │ │ │ ├── query
│ │ │ │ │ │ └── ParticipantProjectionRepository.kt
│ │ │ │ │ └── command
│ │ │ │ │ │ └── views
│ │ │ │ │ │ ├── ParticipantUniqueKeyRepository.kt
│ │ │ │ │ │ ├── ParticipantUniqueKeyProjector.kt
│ │ │ │ │ │ └── ParticipantUniqueKeyProjection.kt
│ │ │ │ │ ├── references
│ │ │ │ │ └── ReferenceCheckerService.kt
│ │ │ │ │ └── authorization
│ │ │ │ │ └── ProjectAuthorizationService.kt
│ │ │ │ └── test
│ │ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── novatecgmbh
│ │ │ │ │ └── eventsourcing
│ │ │ │ │ └── axon
│ │ │ │ │ └── ProjectApplicationTests.kt
│ │ │ │ └── resources
│ │ │ │ └── application.yml
│ │ ├── gradle.properties
│ │ ├── api
│ │ │ ├── src
│ │ │ │ └── main
│ │ │ │ │ └── kotlin
│ │ │ │ │ └── com
│ │ │ │ │ └── novatecgmbh
│ │ │ │ │ └── eventsourcing
│ │ │ │ │ └── axon
│ │ │ │ │ └── project
│ │ │ │ │ ├── participant
│ │ │ │ │ └── api
│ │ │ │ │ │ ├── ValueObjects.kt
│ │ │ │ │ │ ├── Events.kt
│ │ │ │ │ │ ├── Commands.kt
│ │ │ │ │ │ └── Queries.kt
│ │ │ │ │ ├── project
│ │ │ │ │ └── api
│ │ │ │ │ │ ├── ValueObjects.kt
│ │ │ │ │ │ └── Queries.kt
│ │ │ │ │ └── task
│ │ │ │ │ └── api
│ │ │ │ │ ├── ValueObjects.kt
│ │ │ │ │ └── Queries.kt
│ │ │ └── build.gradle.kts
│ │ └── settings.gradle.kts
│ ├── user
│ │ ├── application
│ │ │ └── src
│ │ │ │ ├── main
│ │ │ │ ├── resources
│ │ │ │ │ ├── db
│ │ │ │ │ │ └── migration
│ │ │ │ │ │ │ ├── h2
│ │ │ │ │ │ │ └── V1__INITIAL_SCHEMA.sql
│ │ │ │ │ │ │ └── postgres
│ │ │ │ │ │ │ ├── V4__ID_MAPPING_TABLE.sql
│ │ │ │ │ │ │ ├── V3__QUERY_MODEL_TABLE.sql
│ │ │ │ │ │ │ ├── V2__COMMAND_MODEL_TABLES.sql
│ │ │ │ │ │ │ └── V1__AXON_TABLES.sql
│ │ │ │ │ └── application.yaml
│ │ │ │ └── kotlin
│ │ │ │ │ └── com
│ │ │ │ │ └── novatecgmbh
│ │ │ │ │ └── eventsourcing
│ │ │ │ │ └── axon
│ │ │ │ │ └── user
│ │ │ │ │ ├── UserApplication.kt
│ │ │ │ │ └── user
│ │ │ │ │ ├── query
│ │ │ │ │ ├── UserProjectionRepository.kt
│ │ │ │ │ └── UserProjection.kt
│ │ │ │ │ └── command
│ │ │ │ │ └── view
│ │ │ │ │ ├── UserUniqueKeyRepository.kt
│ │ │ │ │ ├── UserUniqueKeyProjection.kt
│ │ │ │ │ └── UserUniqueKeyProjector.kt
│ │ │ │ └── test
│ │ │ │ ├── resources
│ │ │ │ └── application.yml
│ │ │ │ └── kotlin
│ │ │ │ └── com
│ │ │ │ └── novatecgmbh
│ │ │ │ └── eventsourcing
│ │ │ │ └── axon
│ │ │ │ └── user
│ │ │ │ └── UserApplicationTest.kt
│ │ ├── gradle.properties
│ │ ├── api
│ │ │ ├── build.gradle.kts
│ │ │ └── src
│ │ │ │ └── main
│ │ │ │ └── kotlin
│ │ │ │ └── com
│ │ │ │ └── novatecgmbh
│ │ │ │ └── eventsourcing
│ │ │ │ └── axon
│ │ │ │ └── user
│ │ │ │ └── api
│ │ │ │ ├── ValueObjects.kt
│ │ │ │ ├── Queries.kt
│ │ │ │ ├── Events.kt
│ │ │ │ └── Commands.kt
│ │ └── settings.gradle.kts
│ ├── common
│ │ ├── gradle.properties
│ │ ├── application
│ │ │ ├── src
│ │ │ │ └── main
│ │ │ │ │ └── kotlin
│ │ │ │ │ └── com
│ │ │ │ │ └── novatecgmbh
│ │ │ │ │ └── eventsourcing
│ │ │ │ │ └── axon
│ │ │ │ │ ├── common
│ │ │ │ │ ├── command
│ │ │ │ │ │ ├── Exceptions.kt
│ │ │ │ │ │ └── BaseAggregate.kt
│ │ │ │ │ └── references
│ │ │ │ │ │ └── RootContextIdMapping.kt
│ │ │ │ │ └── application
│ │ │ │ │ └── sequencing
│ │ │ │ │ ├── RootContextId.kt
│ │ │ │ │ └── RootContextIdentifierSequencingPolicy.kt
│ │ │ └── build.gradle.kts
│ │ ├── api
│ │ │ ├── src
│ │ │ │ └── main
│ │ │ │ │ └── kotlin
│ │ │ │ │ └── com
│ │ │ │ │ └── novatecgmbh
│ │ │ │ │ └── eventsourcing
│ │ │ │ │ └── axon
│ │ │ │ │ ├── common
│ │ │ │ │ └── api
│ │ │ │ │ │ ├── AggregateReference.kt
│ │ │ │ │ │ └── ExceptionStatusCode.kt
│ │ │ │ │ ├── user
│ │ │ │ │ └── api
│ │ │ │ │ │ └── UserId.kt
│ │ │ │ │ └── application
│ │ │ │ │ └── security
│ │ │ │ │ ├── UserProfile.kt
│ │ │ │ │ ├── UnregisteredUserPrincipal.kt
│ │ │ │ │ ├── RegisteredUserPrincipal.kt
│ │ │ │ │ └── SecurityContextHelper.kt
│ │ │ └── build.gradle.kts
│ │ ├── auditing
│ │ │ ├── build.gradle.kts
│ │ │ └── src
│ │ │ │ └── main
│ │ │ │ └── kotlin
│ │ │ │ └── com
│ │ │ │ └── novatecgmbh
│ │ │ │ └── eventsourcing
│ │ │ │ └── axon
│ │ │ │ └── application
│ │ │ │ └── auditing
│ │ │ │ └── AuditUserId.kt
│ │ └── settings.gradle.kts
│ └── settings.gradle.kts
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── data-import
│ ├── gradle.properties
│ ├── initial
│ │ ├── src
│ │ │ └── main
│ │ │ │ ├── resources
│ │ │ │ └── application.yml
│ │ │ │ └── kotlin
│ │ │ │ └── com
│ │ │ │ └── novatecgmbh
│ │ │ │ └── eventsourcing
│ │ │ │ └── axon
│ │ │ │ └── DataImporterApplication.kt
│ │ └── build.gradle.kts
│ └── settings.gradle.kts
├── build-logic
│ ├── commons-kotlin
│ │ ├── build.gradle.kts
│ │ └── src
│ │ │ └── main
│ │ │ └── kotlin
│ │ │ └── com.novatecgmbh.commons-kotlin.gradle.kts
│ └── settings.gradle.kts
├── settings.gradle.kts
├── platforms
│ ├── test-platform
│ │ └── build.gradle.kts
│ ├── settings.gradle.kts
│ ├── plugins-platform
│ │ └── build.gradle.kts
│ └── product-platform
│ │ └── build.gradle.kts
└── build.sh
├── frontend
├── src
│ ├── react-app-env.d.ts
│ ├── images
│ │ └── keycloak-bg.png
│ ├── keycloak.ts
│ ├── setupTests.ts
│ ├── app
│ │ ├── utils.ts
│ │ ├── can-ndjson-stream.d.ts
│ │ ├── store.ts
│ │ └── hooks.ts
│ ├── theme.ts
│ ├── components
│ │ ├── Home.tsx
│ │ ├── HighlightingAnimation.ts
│ │ ├── scaffold
│ │ │ └── scaffoldSlice.ts
│ │ ├── PrivateRoute.tsx
│ │ └── TableToolbar.tsx
│ ├── features
│ │ ├── api
│ │ │ └── apiSlice.ts
│ │ └── tasks
│ │ │ └── taskDrawerSlice.ts
│ ├── index.tsx
│ └── index.css
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── silent-check-sso.html
│ └── manifest.json
├── frontend.iml
├── .idea
│ └── frontend.iml
├── tsconfig.json
└── .gitignore
└── .idea
├── modules
├── cli
│ └── cli.test.iml
├── user
│ ├── web
│ │ └── user.web.test.iml
│ ├── api
│ │ └── com.novatecgmbh.eventsourcing.axon.user.user.api.test.iml
│ ├── application-without-web
│ │ └── user.application-without-web.test.iml
│ ├── application
│ │ └── com.novatecgmbh.eventsourcing.axon.user.user.application.test.iml
│ └── command-query
│ │ └── com.novatecgmbh.eventsourcing.axon.user.user.command-query.test.iml
├── common
│ ├── web
│ │ └── common.web.test.iml
│ ├── command-query
│ │ └── common.command-query.test.iml
│ ├── api
│ │ └── com.novatecgmbh.eventsourcing.axon.common.common.api.test.iml
│ └── auditing
│ │ └── com.novatecgmbh.eventsourcing.axon.common.common.auditing.test.iml
├── company
│ ├── web
│ │ └── company.web.test.iml
│ ├── rsocket
│ │ └── company.rsocket.test.iml
│ ├── command-query
│ │ └── company.command-query.test.iml
│ ├── api
│ │ └── com.novatecgmbh.eventsourcing.axon.company.company.api.test.iml
│ ├── application-without-web
│ │ └── company.application-without-web.test.iml
│ └── application
│ │ └── com.novatecgmbh.eventsourcing.axon.company.company.application.test.iml
├── project
│ ├── web
│ │ └── project.web.test.iml
│ ├── rsocket
│ │ └── project.rsocket.test.iml
│ ├── command-query
│ │ └── project.command-query.test.iml
│ ├── api
│ │ └── com.novatecgmbh.eventsourcing.axon.project.project.api.test.iml
│ ├── application-without-web
│ │ └── project.application-without-web.test.iml
│ └── application
│ │ └── com.novatecgmbh.eventsourcing.axon.project.project.application.test.iml
├── graph-ql
│ └── backend.graph-ql.test.iml
├── api-gateway
│ ├── axon
│ │ └── api-gateway.axon.test.iml
│ ├── rest
│ │ └── api-gateway.rest.test.iml
│ ├── common
│ │ └── api-gateway.common.test.iml
│ ├── graphql
│ │ ├── api-gateway.graphql.test.iml
│ │ ├── com.novatecgmbh.eventsourcing.axon.api-gateway.graphql.iml
│ │ ├── com.novatecgmbh.eventsourcing.axon.api-gateway.graphql.main.iml
│ │ └── com.novatecgmbh.eventsourcing.axon.api-gateway.graphql.test.iml
│ ├── spring-cloud
│ │ └── api-gateway.spring-cloud.test.iml
│ ├── apis.iml
│ ├── websocket-stomp
│ │ └── api-gateway.websocket-stomp.test.iml
│ ├── websocket-rsocket
│ │ └── api-gateway.websocket-rsocket.test.iml
│ ├── common-spring-security
│ │ └── api-gateway.common-spring-security.test.iml
│ └── graph-ql
│ │ ├── api-gateway.graphql.main.iml
│ │ └── api-gateway.graphql.test.iml
├── apis
│ ├── api-common
│ │ ├── apis.api-common.test.iml
│ │ └── apis.common.iml
│ ├── websocket-stomp
│ │ └── apis.websocket-stomp.test.iml
│ ├── graphql-websocket
│ │ └── apis.graphql-websocket.test.iml
│ ├── websocket-rsocket
│ │ └── apis.websocket-rsocket.test.iml
│ ├── apis.iml
│ ├── grpc
│ │ ├── apis.grpc.iml
│ │ ├── apis.grpc.main.iml
│ │ └── apis.grpc.test.iml
│ ├── rest
│ │ ├── apis.rest.iml
│ │ └── apis.rest.main.iml
│ ├── graphql
│ │ └── apis.graphql.iml
│ ├── grpc-lib
│ │ ├── apis.grpc-lib.iml
│ │ └── apis.grpc-lib.main.iml
│ └── rsocket
│ │ └── apis.rsocket.iml
├── common-webmvc
│ └── backend.common-webmvc.test.iml
├── backend.iml
├── services
│ ├── common
│ │ ├── command-query
│ │ │ └── common.command-query.test.iml
│ │ ├── common.iml
│ │ ├── api
│ │ │ └── common.api.iml
│ │ ├── auditing
│ │ │ └── common.auditing.iml
│ │ └── application
│ │ │ └── common.application.iml
│ ├── company
│ │ ├── command-query
│ │ │ └── company.command-query.test.iml
│ │ ├── company.iml
│ │ ├── api
│ │ │ └── company.api.iml
│ │ └── application
│ │ │ └── company.application.iml
│ ├── project
│ │ ├── command-query
│ │ │ ├── project.command-query.test.iml
│ │ │ └── project.command-query.iml
│ │ ├── project.iml
│ │ ├── api
│ │ │ └── project.api.iml
│ │ └── application
│ │ │ └── project.application.iml
│ ├── user
│ │ ├── user.iml
│ │ ├── api
│ │ │ ├── user.api.iml
│ │ │ ├── user.api.main.iml
│ │ │ └── user.api.test.iml
│ │ ├── application
│ │ │ ├── user.application.iml
│ │ │ └── user.application.test.iml
│ │ └── command-query
│ │ │ ├── user.command-query.main.iml
│ │ │ └── user.command-query.test.iml
│ └── backend.services.iml
├── clients
│ ├── clients.iml
│ ├── grpc
│ │ ├── clients.grpc.iml
│ │ ├── clients.grpc.main.iml
│ │ └── clients.grpc.test.iml
│ └── rsocket
│ │ ├── clients.rsocket.iml
│ │ ├── clients.rsocket.main.iml
│ │ └── clients.rsocket.test.iml
├── common-spring-webmvc
│ └── backend.common-spring-webmvc.test.iml
├── platforms
│ ├── platforms.iml
│ ├── test-platform
│ │ └── platforms.test-platform.iml
│ ├── plugins-platform
│ │ └── platforms.plugins-platform.iml
│ └── product-platform
│ │ └── platforms.product-platform.iml
├── build-logic
│ ├── build-logic.iml
│ └── commons-kotlin
│ │ └── build-logic.commons-kotlin.iml
└── data-import
│ ├── data-import.iml
│ └── initial
│ └── data-import.initial.iml
├── codeStyles
├── codeStyleConfig.xml
└── Project.xml
├── ktfmt.xml
├── vcs.xml
├── google-java-format.xml
├── jpa-buddy.xml
├── kotlinScripting.xml
├── runConfigurations
├── Frontend.xml
├── Docker_Container_for_YATT.xml
├── UserApplication.xml
├── CompanyApplication.xml
├── ProjectApplication.xml
├── gRPC_API.xml
├── GraphQL_API.xml
├── RSocket_API.xml
└── Data_Importer.xml
├── event-sourcing-with-axon.iml
├── modules.xml
├── misc.xml
├── checkstyle-idea.xml
└── jarRepositories.xml
/axonserver/data/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docker/.env:
--------------------------------------------------------------------------------
1 | COMPOSE_PROJECT_NAME=yatt
--------------------------------------------------------------------------------
/backend/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx2048m
--------------------------------------------------------------------------------
/frontend/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/backend/clients/gradle.properties:
--------------------------------------------------------------------------------
1 | springBootVersion=2.7.0
2 | kotlinVersion=1.6.21
--------------------------------------------------------------------------------
/axonserver/config/axonserver.yaml:
--------------------------------------------------------------------------------
1 | axoniq:
2 | axonserver:
3 | hostname: axonserver
--------------------------------------------------------------------------------
/frontend/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/backend/apis/grpc/gradle.properties:
--------------------------------------------------------------------------------
1 | protobufVersion=3.19.1
2 | protobufPluginVersion=0.8.18
3 | grpcVersion=1.42.1
--------------------------------------------------------------------------------
/backend/services/company/application/src/main/resources/db/migration/h2/V1__INITIAL_SCHEMA.sql:
--------------------------------------------------------------------------------
1 | ../../schema-h2.sql
--------------------------------------------------------------------------------
/backend/services/project/application/src/main/resources/db/migration/h2/V1__INITIAL_SCHEMA.sql:
--------------------------------------------------------------------------------
1 | ../../schema-h2.sql
--------------------------------------------------------------------------------
/backend/services/user/application/src/main/resources/db/migration/h2/V1__INITIAL_SCHEMA.sql:
--------------------------------------------------------------------------------
1 | ../../schema-h2.sql
--------------------------------------------------------------------------------
/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovatecConsulting/YATT/HEAD/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/frontend/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovatecConsulting/YATT/HEAD/frontend/public/logo192.png
--------------------------------------------------------------------------------
/frontend/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovatecConsulting/YATT/HEAD/frontend/public/logo512.png
--------------------------------------------------------------------------------
/frontend/src/images/keycloak-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovatecConsulting/YATT/HEAD/frontend/src/images/keycloak-bg.png
--------------------------------------------------------------------------------
/backend/apis/gradle.properties:
--------------------------------------------------------------------------------
1 | springBootVersion=2.7.0
2 | kotlinVersion=1.6.21
3 | springDependencyManagementPluginVersion=1.0.11.RELEASE
--------------------------------------------------------------------------------
/backend/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovatecConsulting/YATT/HEAD/backend/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/backend/data-import/gradle.properties:
--------------------------------------------------------------------------------
1 | springBootVersion=2.7.0
2 | kotlinVersion=1.6.21
3 | springDependencyManagementPluginVersion=1.0.11.RELEASE
--------------------------------------------------------------------------------
/backend/services/common/gradle.properties:
--------------------------------------------------------------------------------
1 | springBootVersion=2.7.0
2 | kotlinVersion=1.6.21
3 | springDependencyManagementPluginVersion=1.0.11.RELEASE
--------------------------------------------------------------------------------
/backend/services/user/gradle.properties:
--------------------------------------------------------------------------------
1 | springBootVersion=2.7.0
2 | kotlinVersion=1.6.21
3 | springDependencyManagementPluginVersion=1.0.11.RELEASE
--------------------------------------------------------------------------------
/backend/services/company/gradle.properties:
--------------------------------------------------------------------------------
1 | springBootVersion=2.7.0
2 | kotlinVersion=1.6.21
3 | springDependencyManagementPluginVersion=1.0.11.RELEASE
--------------------------------------------------------------------------------
/backend/services/project/gradle.properties:
--------------------------------------------------------------------------------
1 | springBootVersion=2.7.0
2 | kotlinVersion=1.6.21
3 | springDependencyManagementPluginVersion=1.0.11.RELEASE
--------------------------------------------------------------------------------
/backend/apis/graphql/src/main/resources/graphql/base.graphqls:
--------------------------------------------------------------------------------
1 | type AggregateReference {
2 | identifier: String
3 | displayName: String
4 | }
5 |
6 | scalar Date
--------------------------------------------------------------------------------
/backend/apis/grpc/src/main/resources/certs/server-keystore.p12:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovatecConsulting/YATT/HEAD/backend/apis/grpc/src/main/resources/certs/server-keystore.p12
--------------------------------------------------------------------------------
/backend/services/project/application/src/main/resources/db/migration/postgres/V7__ADD_TASK_ASSIGNEE.sql:
--------------------------------------------------------------------------------
1 | alter table if exists tasks
2 | add column participant_id varchar (255);
--------------------------------------------------------------------------------
/backend/services/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "services"
2 |
3 | includeBuild("common")
4 | includeBuild("company")
5 | includeBuild("project")
6 | includeBuild("user")
7 |
--------------------------------------------------------------------------------
/.idea/modules/cli/cli.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/backend/apis/grpc/src/main/resources/certs/server-truststore.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovatecConsulting/YATT/HEAD/backend/apis/grpc/src/main/resources/certs/server-truststore.jks
--------------------------------------------------------------------------------
/backend/clients/grpc/src/main/resources/certs/server-truststore.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovatecConsulting/YATT/HEAD/backend/clients/grpc/src/main/resources/certs/server-truststore.jks
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/frontend/public/silent-check-sso.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/ktfmt.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules/user/web/user.web.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/common/web/common.web.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/company/web/company.web.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/project/web/project.web.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules/graph-ql/backend.graph-ql.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/google-java-format.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules/api-gateway/axon/api-gateway.axon.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/api-gateway/rest/api-gateway.rest.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/apis/api-common/apis.api-common.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/company/rsocket/company.rsocket.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/project/rsocket/project.rsocket.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/jpa-buddy.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules/api-gateway/common/api-gateway.common.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/api-gateway/graphql/api-gateway.graphql.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/common-webmvc/backend.common-webmvc.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/kotlinScripting.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules/apis/websocket-stomp/apis.websocket-stomp.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/common/command-query/common.command-query.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/company/command-query/company.command-query.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/project/command-query/project.command-query.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/apis/graphql-websocket/apis.graphql-websocket.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/apis/websocket-rsocket/apis.websocket-rsocket.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/backend.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/user/api/com.novatecgmbh.eventsourcing.axon.user.user.api.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/api-gateway/spring-cloud/api-gateway.spring-cloud.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/apis/apis.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/common/api/com.novatecgmbh.eventsourcing.axon.common.common.api.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/services/common/command-query/common.command-query.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/services/company/command-query/company.command-query.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/services/project/command-query/project.command-query.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/api-gateway/apis.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/clients/clients.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/common-spring-webmvc/backend.common-spring-webmvc.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/company/api/com.novatecgmbh.eventsourcing.axon.company.company.api.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/project/api/com.novatecgmbh.eventsourcing.axon.project.project.api.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/frontend/src/keycloak.ts:
--------------------------------------------------------------------------------
1 | import Keycloak from "keycloak-js";
2 |
3 | const keycloak = Keycloak({
4 | url: 'http://localhost:8999',
5 | realm: 'eventsourcing-with-axon',
6 | clientId: 'my-backend'
7 | });
8 |
9 | export default keycloak;
--------------------------------------------------------------------------------
/.idea/modules/api-gateway/websocket-stomp/api-gateway.websocket-stomp.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/apis/grpc/apis.grpc.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/apis/rest/apis.rest.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/platforms/platforms.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/services/common/common.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/services/user/user.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/api-gateway/websocket-rsocket/api-gateway.websocket-rsocket.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/apis/api-common/apis.common.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/apis/graphql/apis.graphql.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/apis/grpc-lib/apis.grpc-lib.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/apis/grpc/apis.grpc.main.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/apis/grpc/apis.grpc.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/apis/rest/apis.rest.main.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/apis/rsocket/apis.rsocket.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/build-logic/build-logic.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/clients/grpc/clients.grpc.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/common/auditing/com.novatecgmbh.eventsourcing.axon.common.common.auditing.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/data-import/data-import.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/services/backend.services.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/services/company/company.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/services/project/project.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/services/user/api/user.api.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/user/application-without-web/user.application-without-web.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/user/application/com.novatecgmbh.eventsourcing.axon.user.user.application.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/apis/grpc-lib/apis.grpc-lib.main.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/clients/grpc/clients.grpc.main.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/clients/grpc/clients.grpc.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/clients/rsocket/clients.rsocket.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/company/application-without-web/company.application-without-web.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/project/application-without-web/project.application-without-web.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/services/common/api/common.api.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/services/company/api/company.api.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/services/project/api/project.api.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/services/user/api/user.api.main.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/services/user/api/user.api.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/clients/rsocket/clients.rsocket.main.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/clients/rsocket/clients.rsocket.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/user/command-query/com.novatecgmbh.eventsourcing.axon.user.user.command-query.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/backend/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/.idea/modules/api-gateway/common-spring-security/api-gateway.common-spring-security.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/company/application/com.novatecgmbh.eventsourcing.axon.company.company.application.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/data-import/initial/data-import.initial.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/project/application/com.novatecgmbh.eventsourcing.axon.project.project.application.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules/services/common/auditing/common.auditing.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/services/user/application/user.application.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/backend/services/project/application/src/main/resources/db/migration/postgres/V8__ADD_EMAIL_AND_PHONE.sql:
--------------------------------------------------------------------------------
1 | alter table if exists participant
2 | add column user_email varchar (255);
3 |
4 | alter table if exists participant
5 | add column user_telephone varchar (255);
--------------------------------------------------------------------------------
/.idea/modules/platforms/test-platform/platforms.test-platform.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/services/common/application/common.application.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/services/user/application/user.application.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/services/company/application/company.application.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/services/project/application/project.application.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/services/project/command-query/project.command-query.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/services/user/command-query/user.command-query.main.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/build-logic/commons-kotlin/build-logic.commons-kotlin.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/platforms/plugins-platform/platforms.plugins-platform.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/platforms/product-platform/platforms.product-platform.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/backend/build-logic/commons-kotlin/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | `kotlin-dsl`
3 | }
4 |
5 | dependencies {
6 | implementation(platform("com.novatecgmbh.platform:plugins-platform"))
7 |
8 | implementation("org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin")
9 | }
--------------------------------------------------------------------------------
/backend/build-logic/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | dependencyResolutionManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | }
6 | }
7 |
8 | includeBuild("../platforms")
9 |
10 | rootProject.name = "build-logic"
11 | include("commons-kotlin")
12 |
--------------------------------------------------------------------------------
/backend/data-import/initial/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: data-importer
4 | jackson:
5 | serialization:
6 | fail-on-empty-beans: false
7 |
8 | logging:
9 | level:
10 | ROOT: warn
11 | org:
12 | springframework.boot: info
--------------------------------------------------------------------------------
/frontend/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect';
6 |
--------------------------------------------------------------------------------
/.idea/modules/api-gateway/graphql/com.novatecgmbh.eventsourcing.axon.api-gateway.graphql.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/frontend/src/app/utils.ts:
--------------------------------------------------------------------------------
1 | export function parseDate(isoDate: string): Date {
2 | const splitDate = isoDate.split("-");
3 | const year = parseInt(splitDate[0]);
4 | const month = parseInt(splitDate[1])-1;
5 | const day = parseInt(splitDate[2]);
6 |
7 | return new Date(year, month, day);
8 | }
--------------------------------------------------------------------------------
/.idea/modules/api-gateway/graphql/com.novatecgmbh.eventsourcing.axon.api-gateway.graphql.main.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/backend/services/company/application/src/test/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | datasource:
3 | url: jdbc:h2:mem:db;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
4 | username: sa
5 | password: sa
6 | flyway:
7 | locations: classpath:db/migration/h2
8 | axon:
9 | axonserver:
10 | enabled: false
--------------------------------------------------------------------------------
/backend/services/user/application/src/test/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | datasource:
3 | url: jdbc:h2:mem:db;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
4 | username: sa
5 | password: sa
6 | flyway:
7 | locations: classpath:db/migration/h2
8 | axon:
9 | axonserver:
10 | enabled: false
--------------------------------------------------------------------------------
/backend/services/common/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/common/command/Exceptions.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.common.command
2 |
3 | import java.lang.RuntimeException
4 |
5 | class AlreadyExistsException : RuntimeException()
6 |
7 | class PreconditionFailedException(message: String) : RuntimeException(message)
8 |
--------------------------------------------------------------------------------
/backend/apis/rest/src/test/kotlin/com/novatecgmbh/eventsourcing/axon/demo/DemoApplicationTests.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.demo
2 |
3 | import org.junit.jupiter.api.Test
4 | import org.springframework.boot.test.context.SpringBootTest
5 |
6 | @SpringBootTest
7 | class DemoApplicationTests {
8 |
9 | @Test fun contextLoads() {}
10 | }
11 |
--------------------------------------------------------------------------------
/backend/services/common/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/common/api/AggregateReference.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.common.api
2 |
3 | import javax.persistence.Embeddable
4 | import javax.persistence.Embedded
5 |
6 | @Embeddable
7 | data class AggregateReference(@Embedded val identifier: T, val displayName: String? = null)
8 |
--------------------------------------------------------------------------------
/backend/services/project/application/src/main/resources/db/migration/postgres/V9__ADD_NAME_AND_COMPANY_TO_TASK.sql:
--------------------------------------------------------------------------------
1 | alter table if exists tasks
2 | add column assignee_company_name varchar (255);
3 |
4 | alter table if exists tasks
5 | add column assignee_first_name varchar (255);
6 |
7 | alter table if exists tasks
8 | add column assignee_last_name varchar (255);
--------------------------------------------------------------------------------
/.idea/modules/services/user/command-query/user.command-query.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/backend/services/company/application/src/main/resources/db/migration/postgres/V4__ID_MAPPING_TABLE.sql:
--------------------------------------------------------------------------------
1 | create table root_context_id_mapping
2 | (
3 | aggregate_identifier varchar(255) not null,
4 | aggregate_type varchar(255) not null,
5 | root_context_id varchar(255) not null,
6 | primary key (aggregate_identifier, aggregate_type, root_context_id)
7 | );
--------------------------------------------------------------------------------
/backend/services/project/application/src/main/resources/db/migration/postgres/V5__ID_MAPPING_TABLE.sql:
--------------------------------------------------------------------------------
1 | create table root_context_id_mapping
2 | (
3 | aggregate_identifier varchar(255) not null,
4 | aggregate_type varchar(255) not null,
5 | root_context_id varchar(255) not null,
6 | primary key (aggregate_identifier, aggregate_type, root_context_id)
7 | );
--------------------------------------------------------------------------------
/backend/services/user/application/src/main/resources/db/migration/postgres/V4__ID_MAPPING_TABLE.sql:
--------------------------------------------------------------------------------
1 | create table root_context_id_mapping
2 | (
3 | aggregate_identifier varchar(255) not null,
4 | aggregate_type varchar(255) not null,
5 | root_context_id varchar(255) not null,
6 | primary key (aggregate_identifier, aggregate_type, root_context_id)
7 | );
--------------------------------------------------------------------------------
/frontend/frontend.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/backend/clients/rsocket/src/main/resources/application.yaml:
--------------------------------------------------------------------------------
1 | custom:
2 | auth:
3 | url: http://localhost:8999/realms/eventsourcing-with-axon/protocol/openid-connect/token
4 | username: test1
5 | password: test
6 | rsocket:
7 | server:
8 | uri: ws://localhost:8086/rsocket
9 |
10 | logging:
11 | level:
12 | root: info
13 | #io.rsocket.FrameLogger: debug
--------------------------------------------------------------------------------
/frontend/.idea/frontend.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/backend/apis/rest/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/RestApi.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon
2 |
3 | import org.springframework.boot.autoconfigure.SpringBootApplication
4 | import org.springframework.boot.runApplication
5 |
6 | @SpringBootApplication class ApiGateway
7 |
8 | fun main(args: Array) {
9 | runApplication(*args)
10 | }
11 |
--------------------------------------------------------------------------------
/backend/services/common/auditing/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.novatecgmbh.commons-kotlin")
3 | }
4 |
5 | group = "${group}.common"
6 |
7 | dependencies {
8 | implementation("com.novatecgmbh.eventsourcing.axon.common:api")
9 |
10 | implementation("org.axonframework:axon-modelling")
11 | implementation("org.springframework.boot:spring-boot-starter-security")
12 | }
--------------------------------------------------------------------------------
/backend/services/company/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/company/api/Events.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.company.api
2 |
3 | abstract class CompanyEvent(open val aggregateIdentifier: CompanyId)
4 |
5 | data class CompanyCreatedEvent(override val aggregateIdentifier: CompanyId, val name: String) :
6 | CompanyEvent(aggregateIdentifier)
7 |
--------------------------------------------------------------------------------
/backend/services/company/application/src/test/kotlin/com/novatecgmbh/eventsourcing/axon/CompanyApplicationTests.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon
2 |
3 | import org.junit.jupiter.api.Test
4 | import org.springframework.boot.test.context.SpringBootTest
5 |
6 | @SpringBootTest
7 | class CompanyApplicationTests {
8 |
9 | @Test
10 | fun contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/backend/services/project/application/src/test/kotlin/com/novatecgmbh/eventsourcing/axon/ProjectApplicationTests.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon
2 |
3 | import org.junit.jupiter.api.Test
4 | import org.springframework.boot.test.context.SpringBootTest
5 |
6 | @SpringBootTest
7 | class ProjectApplicationTests {
8 |
9 | @Test
10 | fun contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/backend/services/user/application/src/test/kotlin/com/novatecgmbh/eventsourcing/axon/user/UserApplicationTest.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.user
2 |
3 | import org.junit.jupiter.api.Test
4 | import org.springframework.boot.test.context.SpringBootTest
5 |
6 | @SpringBootTest
7 | class UserApplicationTest {
8 |
9 | @Test
10 | fun contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/.idea/modules/api-gateway/graphql/com.novatecgmbh.eventsourcing.axon.api-gateway.graphql.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/backend/apis/graphql/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/GraphQlApi.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon
2 |
3 | import org.springframework.boot.autoconfigure.SpringBootApplication
4 | import org.springframework.boot.runApplication
5 |
6 | @SpringBootApplication class GraphQlApi
7 |
8 | fun main(args: Array) {
9 | runApplication(*args)
10 | }
11 |
--------------------------------------------------------------------------------
/backend/apis/rsocket/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/RsocketApi.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon
2 |
3 | import org.springframework.boot.autoconfigure.SpringBootApplication
4 | import org.springframework.boot.runApplication
5 |
6 | @SpringBootApplication class RsocketApi
7 |
8 | fun main(args: Array) {
9 | runApplication(*args)
10 | }
11 |
--------------------------------------------------------------------------------
/backend/services/common/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/common/api/ExceptionStatusCode.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.common.api
2 |
3 | enum class ExceptionStatusCode {
4 | ALREADY_EXISTS,
5 | CONCURRENT_MODIFICATION,
6 | ILLEGAL_ARGUMENT,
7 | ILLEGAL_STATE,
8 | NOT_FOUND,
9 | UNKNOWN,
10 | ACCESS_DENIED,
11 | PRECONDITION_FAILED
12 | }
13 |
--------------------------------------------------------------------------------
/frontend/src/theme.ts:
--------------------------------------------------------------------------------
1 | import {createTheme} from "@mui/material";
2 | import {deDE} from "@mui/material/locale";
3 |
4 | export const theme = createTheme({
5 | shape: {
6 | borderRadius: 10,
7 | },
8 | components: {
9 | MuiTextField: {
10 | defaultProps: {
11 | margin: 'normal'
12 | },
13 | },
14 | },
15 | }, deDE);
--------------------------------------------------------------------------------
/backend/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | // This is an empty umbrella build including all the component builds.
2 | // This build is not necessarily needed. The component builds work independently.
3 | rootProject.name = "backend"
4 |
5 | includeBuild("platforms")
6 | includeBuild("build-logic")
7 |
8 | includeBuild("data-import")
9 |
10 | includeBuild("apis")
11 | includeBuild("services")
12 |
13 | includeBuild("clients")
14 |
--------------------------------------------------------------------------------
/backend/services/project/application/src/main/resources/db/migration/postgres/V4__ACCESS_CONTROL_LIST_TABLE.sql:
--------------------------------------------------------------------------------
1 | create table project_acls
2 | (
3 | aggregate_identifier varchar(255) not null,
4 | aggregate_type varchar(255) not null,
5 | permission varchar(255) not null,
6 | identifier varchar(255) not null,
7 | primary key (aggregate_identifier, aggregate_type, permission, identifier)
8 | );
--------------------------------------------------------------------------------
/backend/services/company/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/CompanyApplication.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon
2 |
3 | import org.springframework.boot.autoconfigure.SpringBootApplication
4 | import org.springframework.boot.runApplication
5 |
6 | @SpringBootApplication class CompanyApplication
7 |
8 | fun main(args: Array) {
9 | runApplication(*args)
10 | }
11 |
--------------------------------------------------------------------------------
/backend/services/project/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/ProjectApplication.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon
2 |
3 | import org.springframework.boot.autoconfigure.SpringBootApplication
4 | import org.springframework.boot.runApplication
5 |
6 | @SpringBootApplication class ProjectApplication
7 |
8 | fun main(args: Array) {
9 | runApplication(*args)
10 | }
11 |
--------------------------------------------------------------------------------
/backend/services/user/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/user/UserApplication.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.user
2 |
3 | import org.springframework.boot.autoconfigure.SpringBootApplication
4 | import org.springframework.boot.runApplication
5 |
6 | @SpringBootApplication class UserApplication
7 |
8 | fun main(args: Array) {
9 | runApplication(*args)
10 | }
11 |
--------------------------------------------------------------------------------
/backend/apis/rest/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/task/web/dto/UnassignTaskDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.task.web.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.task.api.TaskId
4 | import com.novatecgmbh.eventsourcing.axon.project.task.api.UnassignTaskCommand
5 |
6 | class UnassignTaskDto {
7 | fun toCommand(taskId: TaskId) = UnassignTaskCommand(identifier = taskId)
8 | }
9 |
--------------------------------------------------------------------------------
/backend/data-import/initial/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/DataImporterApplication.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon
2 |
3 | import org.springframework.boot.autoconfigure.SpringBootApplication
4 | import org.springframework.boot.runApplication
5 |
6 | @SpringBootApplication class DataImporterApplication
7 |
8 | fun main(args: Array) {
9 | runApplication(*args)
10 | }
11 |
--------------------------------------------------------------------------------
/backend/services/company/application/src/main/resources/db/migration/postgres/V2__COMMAND_MODEL_TABLES.sql:
--------------------------------------------------------------------------------
1 | create table employee_unique_key
2 | (
3 | identifier varchar(255) not null,
4 | company_id varchar(255) not null,
5 | user_id varchar(255) not null,
6 | primary key (identifier)
7 | );
8 |
9 | alter table if exists employee_unique_key
10 | add constraint UKikkx651fvk6yi9iymk7620mgp unique (company_id, user_id);
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/backend/services/user/application/src/main/resources/db/migration/postgres/V3__QUERY_MODEL_TABLE.sql:
--------------------------------------------------------------------------------
1 | create table users
2 | (
3 | identifier varchar(255) not null,
4 | email varchar(255) not null,
5 | external_user_id varchar(255) not null,
6 | firstname varchar(255) not null,
7 | lastname varchar(255) not null,
8 | telephone varchar(255) not null,
9 | primary key (identifier)
10 | );
--------------------------------------------------------------------------------
/backend/services/project/application/src/test/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | datasource:
3 | url: jdbc:h2:mem:db;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
4 | username: sa
5 | password: sa
6 | jpa:
7 | hibernate:
8 | ddl-auto: create-drop
9 | properties:
10 | hibernate:
11 | dialect: org.hibernate.dialect.H2Dialect
12 | flyway:
13 | enabled: false
14 | axon:
15 | axonserver:
16 | enabled: false
--------------------------------------------------------------------------------
/.idea/modules/api-gateway/graph-ql/api-gateway.graphql.main.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Frontend.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/backend/apis/rsocket/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/task/rsocket/dto/UnassignTaskDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.task.rsocket.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.task.api.TaskId
4 | import com.novatecgmbh.eventsourcing.axon.project.task.api.UnassignTaskCommand
5 |
6 | data class UnassignTaskDto(val taskId: TaskId) {
7 | fun toCommand() = UnassignTaskCommand(identifier = taskId)
8 | }
9 |
--------------------------------------------------------------------------------
/backend/apis/rest/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/task/web/dto/RenameTaskDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.task.web.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.task.api.RenameTaskCommand
4 | import com.novatecgmbh.eventsourcing.axon.project.task.api.TaskId
5 |
6 | data class RenameTaskDto(val name: String) {
7 | fun toCommand(taskId: TaskId) = RenameTaskCommand(identifier = taskId, name = name)
8 | }
9 |
--------------------------------------------------------------------------------
/backend/services/common/auditing/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/application/auditing/AuditUserId.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.application.auditing
2 |
3 | import org.axonframework.messaging.annotation.MetaDataValue
4 |
5 | @MustBeDocumented
6 | @Target(AnnotationTarget.VALUE_PARAMETER)
7 | @Retention(AnnotationRetention.RUNTIME)
8 | @MetaDataValue(AUDIT_USER_ID_META_DATA_KEY, required = true)
9 | annotation class AuditUserId
10 |
--------------------------------------------------------------------------------
/backend/services/user/api/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.novatecgmbh.commons-kotlin")
3 | kotlin("plugin.jpa")
4 | }
5 |
6 | group = "${group}.user"
7 |
8 | dependencies {
9 | implementation("com.novatecgmbh.eventsourcing.axon.common:api")
10 |
11 | implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
12 | implementation("javax.persistence:javax.persistence-api")
13 | implementation("org.axonframework:axon-modelling")
14 | }
15 |
--------------------------------------------------------------------------------
/backend/apis/rest/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/user/user/web/dto/RenameUserDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.user.user.web.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.user.api.RenameUserCommand
4 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
5 |
6 | data class RenameUserDto(val firstname: String, val lastname: String) {
7 | fun toCommand(identifier: UserId) = RenameUserCommand(identifier, firstname, lastname)
8 | }
9 |
--------------------------------------------------------------------------------
/backend/apis/rsocket/rsc-collection/authenticate.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export ACCESS_TOKEN=`curl -s --location --request POST 'http://localhost:8999/realms/eventsourcing-with-axon/protocol/openid-connect/token' \
4 | --header 'Content-Type: application/x-www-form-urlencoded' \
5 | --data-urlencode 'client_id=my-backend' \
6 | --data-urlencode 'username=test1' \
7 | --data-urlencode 'password=test' \
8 | --data-urlencode 'grant_type=password' | jq -r .access_token`
9 |
--------------------------------------------------------------------------------
/backend/clients/grpc/src/main/resources/application.yaml:
--------------------------------------------------------------------------------
1 | custom:
2 | auth:
3 | url: http://localhost:8999/realms/eventsourcing-with-axon/protocol/openid-connect/token
4 | username: test1
5 | password: test
6 |
7 | grpc:
8 | client:
9 | grpcapi:
10 | address: static://localhost:8089
11 | security:
12 | clientAuthEnabled: false
13 | trust-store: classpath:certs/server-truststore.jks
14 | trust-store-password: server
15 |
--------------------------------------------------------------------------------
/backend/apis/rest/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/company/web/dto/CreateCompanyDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.company.web.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CreateCompanyCommand
5 |
6 | data class CreateCompanyDto(val name: String) {
7 | fun toCommand(companyId: CompanyId) = CreateCompanyCommand(companyId, name)
8 | }
9 |
--------------------------------------------------------------------------------
/backend/services/common/api/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.novatecgmbh.commons-kotlin")
3 | kotlin("plugin.jpa")
4 | }
5 |
6 | group = "${group}.common"
7 |
8 | dependencies {
9 | implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
10 | implementation("org.axonframework:axon-modelling")
11 | implementation("javax.persistence:javax.persistence-api")
12 | implementation("org.springframework.boot:spring-boot-starter-security")
13 | }
14 |
--------------------------------------------------------------------------------
/backend/apis/rsocket/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/task/rsocket/dto/RenameTaskDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.task.rsocket.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.task.api.RenameTaskCommand
4 | import com.novatecgmbh.eventsourcing.axon.project.task.api.TaskId
5 |
6 | data class RenameTaskDto(val identifier: TaskId, val name: String) {
7 | fun toCommand() = RenameTaskCommand(identifier = identifier, name = name)
8 | }
9 |
--------------------------------------------------------------------------------
/backend/services/company/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/company/query/CompanyProjectionRepository.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.company.query
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import org.springframework.data.jpa.repository.JpaRepository
5 | import org.springframework.stereotype.Repository
6 |
7 | @Repository interface CompanyProjectionRepository : JpaRepository
8 |
--------------------------------------------------------------------------------
/backend/apis/rest/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/project/web/dto/RenameProjectDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.project.web.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
4 | import com.novatecgmbh.eventsourcing.axon.project.project.api.RenameProjectCommand
5 |
6 | data class RenameProjectDto(val version: Long, val name: String) {
7 | fun toCommand(projectId: ProjectId) = RenameProjectCommand(projectId, version, name)
8 | }
9 |
--------------------------------------------------------------------------------
/backend/apis/rsocket/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/company/rsocket/dtos/CreateCompanyDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.company.rsocket.dtos
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CreateCompanyCommand
5 |
6 | data class CreateCompanyDto(val companyId: CompanyId = CompanyId(), val name: String) {
7 | fun toCommand() = CreateCompanyCommand(companyId, name)
8 | }
9 |
--------------------------------------------------------------------------------
/backend/services/common/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/user/api/UserId.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.user.api
2 |
3 | import com.fasterxml.jackson.annotation.JsonValue
4 | import java.io.Serializable
5 | import java.util.*
6 | import javax.persistence.Embeddable
7 |
8 | @Embeddable
9 | data class UserId(@get:JsonValue val identifier: String) : Serializable {
10 | constructor() : this(UUID.randomUUID().toString())
11 |
12 | override fun toString(): String = identifier
13 | }
--------------------------------------------------------------------------------
/backend/services/user/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/user/api/ValueObjects.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.user.api
2 |
3 | import com.fasterxml.jackson.annotation.JsonValue
4 | import java.io.Serializable
5 | import java.util.*
6 | import javax.persistence.Embeddable
7 |
8 | @Embeddable
9 | data class UserId(@get:JsonValue val identifier: String) : Serializable {
10 | constructor() : this(UUID.randomUUID().toString())
11 |
12 | override fun toString(): String = identifier
13 | }
--------------------------------------------------------------------------------
/backend/clients/grpc/src/main/kotlin/com/novatecgmbh/grpc/client/demo/GrpcDemoClient.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.grpc.client.demo
2 |
3 | import org.springframework.boot.autoconfigure.SpringBootApplication
4 | import org.springframework.boot.context.properties.ConfigurationPropertiesScan
5 | import org.springframework.boot.runApplication
6 |
7 |
8 | @SpringBootApplication
9 | @ConfigurationPropertiesScan
10 | class GrpcDemoClient
11 |
12 | fun main(args: Array) {
13 | runApplication(*args)
14 | }
15 |
--------------------------------------------------------------------------------
/backend/services/company/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/company/api/Queries.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.company.api
2 |
3 | import com.novatecgmbh.eventsourcing.axon.common.api.AggregateReference
4 |
5 | class AllCompaniesQuery
6 |
7 | data class CompanyQuery(val companyId: CompanyId)
8 |
9 | data class CompanyQueryResult(val identifier: CompanyId, val version: Long, val name: String) {
10 | fun toAggregateReference() = AggregateReference(identifier, name)
11 | }
12 |
--------------------------------------------------------------------------------
/.idea/event-sourcing-with-axon.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/backend/platforms/test-platform/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("java-platform")
3 | }
4 |
5 | group = "com.novatecgmbh.platform"
6 |
7 | // allow the definition of dependencies to other platforms like the JUnit 5 BOM
8 | javaPlatform.allowDependencies()
9 |
10 | dependencies {
11 | api(platform("org.junit:junit-bom:5.8.1"))
12 |
13 | constraints {
14 | api("com.tngtech.archunit:archunit-junit5:0.21.0")
15 | api("org.apache.commons:commons-lang3:3.11")
16 | api("io.mockk:mockk:1.12.0")
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/backend/services/user/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/user/api/Queries.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.user.api
2 |
3 | class AllUsersQuery
4 |
5 | data class FindUserByExternalUserIdQuery(val externalUserId: String)
6 |
7 | data class UserQuery(val userId: UserId)
8 |
9 | data class UserQueryResult(
10 | val identifier: UserId,
11 | val externalUserId: String,
12 | val firstname: String,
13 | val lastname: String,
14 | val email: String,
15 | val telephone: String
16 | )
17 |
--------------------------------------------------------------------------------
/backend/apis/grpc/src/main/resources/certs/server-csr.conf:
--------------------------------------------------------------------------------
1 | [req]
2 | distinguished_name = req_distinguished_name
3 | req_extensions = v3_req
4 | prompt = no
5 |
6 | [req_distinguished_name]
7 | C = DE
8 | ST = Thuringia
9 | L = Erfurt
10 | O = Alice Corp
11 | OU = Team Foo
12 | CN = grpc-server
13 |
14 | [v3_req]
15 | keyUsage = critical, digitalSignature, keyEncipherment, dataEncipherment, cRLSign, keyCertSign
16 | extendedKeyUsage = serverAuth
17 | subjectAltName = @alt_names
18 | [alt_names]
19 | DNS.1 = grpc-server
20 | DNS.2 = localhost
21 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Docker_Container_for_YATT.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/modules/api-gateway/graph-ql/api-gateway.graphql.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/backend/apis/grpc/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/LocalDateExtension.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project
2 |
3 | import com.google.protobuf.Timestamp
4 | import java.time.Instant
5 | import java.time.LocalDate
6 | import java.time.LocalTime.MIDNIGHT
7 | import java.time.ZoneOffset.UTC
8 |
9 | fun LocalDate.toTimestamp() =
10 | Timestamp.newBuilder().setSeconds(this.toEpochSecond(MIDNIGHT, UTC)).build()
11 |
12 | fun Timestamp.toLocalDate() = LocalDate.ofInstant(Instant.ofEpochSecond(this.seconds), UTC)
13 |
--------------------------------------------------------------------------------
/backend/services/common/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/application/security/UserProfile.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.application.security
2 |
3 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
4 |
5 | interface UserProfile
6 |
7 | data class RegisteredUserProfile(
8 | val identifier: UserId,
9 | val externalUserId: String,
10 | val firstname: String,
11 | val lastname: String
12 | ) : UserProfile
13 |
14 | data class UnregisteredUserProfile(val externalUserId: String) : UserProfile
15 |
--------------------------------------------------------------------------------
/backend/services/company/api/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.novatecgmbh.commons-kotlin")
3 | kotlin("plugin.jpa")
4 | }
5 |
6 | group = "${group}.company"
7 |
8 | dependencies {
9 | implementation("com.novatecgmbh.eventsourcing.axon.common:api")
10 | implementation("com.novatecgmbh.eventsourcing.axon.user:api")
11 |
12 | implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
13 | implementation("javax.persistence:javax.persistence-api")
14 | implementation("org.axonframework:axon-modelling")
15 | }
16 |
--------------------------------------------------------------------------------
/backend/services/company/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/company/api/ValueObjects.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.company.api
2 |
3 | import com.fasterxml.jackson.annotation.JsonValue
4 | import java.io.Serializable
5 | import java.util.*
6 | import javax.persistence.Embeddable
7 |
8 | @Embeddable
9 | data class CompanyId(@get:JsonValue val identifier: String) : Serializable {
10 | constructor() : this(UUID.randomUUID().toString())
11 |
12 | override fun toString(): String = identifier
13 | }
14 |
--------------------------------------------------------------------------------
/backend/services/company/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/employee/api/ValueObjects.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.employee.api
2 |
3 | import com.fasterxml.jackson.annotation.JsonValue
4 | import java.io.Serializable
5 | import java.util.*
6 | import javax.persistence.Embeddable
7 |
8 | @Embeddable
9 | data class EmployeeId(@get:JsonValue val identifier: String) : Serializable {
10 | constructor() : this(UUID.randomUUID().toString())
11 |
12 | override fun toString(): String = identifier
13 | }
14 |
--------------------------------------------------------------------------------
/backend/apis/rsocket/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/task/rsocket/dto/RemoveTodoDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.task.rsocket.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.task.api.RemoveTodoCommand
4 | import com.novatecgmbh.eventsourcing.axon.project.task.api.TaskId
5 | import com.novatecgmbh.eventsourcing.axon.project.task.api.TodoId
6 |
7 | data class RemoveTodoDto(val taskId: TaskId, val todoId: TodoId) {
8 | fun toCommand() = RemoveTodoCommand(identifier = taskId, todoId = todoId)
9 | }
10 |
--------------------------------------------------------------------------------
/backend/services/company/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/company/api/Commands.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.company.api
2 |
3 | import org.axonframework.modelling.command.TargetAggregateIdentifier
4 |
5 | abstract class CompanyCommand(
6 | @TargetAggregateIdentifier open val aggregateIdentifier: CompanyId,
7 | )
8 |
9 | data class CreateCompanyCommand(
10 | @TargetAggregateIdentifier override val aggregateIdentifier: CompanyId,
11 | val name: String
12 | ) : CompanyCommand(aggregateIdentifier)
13 |
--------------------------------------------------------------------------------
/backend/services/project/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/participant/api/ValueObjects.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.participant.api
2 |
3 | import com.fasterxml.jackson.annotation.JsonValue
4 | import java.io.Serializable
5 | import java.util.*
6 | import javax.persistence.Embeddable
7 |
8 | @Embeddable
9 | data class ParticipantId(@get:JsonValue val identifier: String) : Serializable {
10 | constructor() : this(UUID.randomUUID().toString())
11 |
12 | override fun toString(): String = identifier
13 | }
14 |
--------------------------------------------------------------------------------
/backend/apis/rsocket/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/project/rsocket/dto/RenameProjectDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.project.rsocket.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
4 | import com.novatecgmbh.eventsourcing.axon.project.project.api.RenameProjectCommand
5 |
6 | data class RenameProjectDto(
7 | val identifier: ProjectId,
8 | val version: Long,
9 | val name: String,
10 | ) {
11 | fun toCommand() = RenameProjectCommand(identifier, version, name)
12 | }
13 |
--------------------------------------------------------------------------------
/backend/apis/rsocket/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/task/rsocket/dto/MarkTodoAsDoneDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.task.rsocket.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.task.api.MarkTodoAsDoneCommand
4 | import com.novatecgmbh.eventsourcing.axon.project.task.api.TaskId
5 | import com.novatecgmbh.eventsourcing.axon.project.task.api.TodoId
6 |
7 | data class MarkTodoAsDoneDto(val taskId: TaskId, val todoId: TodoId) {
8 | fun toCommand() = MarkTodoAsDoneCommand(identifier = taskId, todoId = todoId)
9 | }
--------------------------------------------------------------------------------
/backend/apis/grpc/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/GrpcApi.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon
2 |
3 | import org.springframework.boot.autoconfigure.SpringBootApplication
4 | import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties
5 | import org.springframework.boot.context.properties.EnableConfigurationProperties
6 | import org.springframework.boot.runApplication
7 |
8 | @SpringBootApplication
9 | class GrpcApi
10 |
11 | fun main(args: Array) {
12 | runApplication(*args)
13 | }
14 |
--------------------------------------------------------------------------------
/backend/apis/rest/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/task/web/dto/AssignTaskDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.task.web.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.participant.api.ParticipantId
4 | import com.novatecgmbh.eventsourcing.axon.project.task.api.AssignTaskCommand
5 | import com.novatecgmbh.eventsourcing.axon.project.task.api.TaskId
6 |
7 | data class AssignTaskDto(val assignee: ParticipantId) {
8 | fun toCommand(taskId: TaskId) = AssignTaskCommand(identifier = taskId, assignee = assignee)
9 | }
10 |
--------------------------------------------------------------------------------
/backend/services/user/application/src/main/resources/db/migration/postgres/V2__COMMAND_MODEL_TABLES.sql:
--------------------------------------------------------------------------------
1 | create table user_unique_key
2 | (
3 | identifier varchar(255) not null,
4 | email varchar(255) not null,
5 | external_user_id varchar(255) not null,
6 | primary key (identifier)
7 | );
8 |
9 | alter table if exists user_unique_key
10 | add constraint UK_7rl1lg6v0c2b4os3hd99hy0un unique (email);
11 |
12 | alter table if exists user_unique_key
13 | add constraint UK_hbn7ykwddd2spkc6buayh0lm1 unique (external_user_id);
14 |
15 |
16 |
--------------------------------------------------------------------------------
/backend/apis/graphql/src/main/resources/graphql/user.graphqls:
--------------------------------------------------------------------------------
1 | type Query {
2 | users: [User]
3 | }
4 |
5 | type Mutation {
6 | registerUser(
7 | firstname: String!
8 | lastname: String!
9 | email: String!
10 | telephone: String!
11 | ): ID!
12 | renameUser(
13 | identifier: ID!
14 | firstname: String!
15 | lastname: String!
16 | ): Int!
17 | }
18 |
19 | type User {
20 | identifier: ID!
21 | firstname: String!
22 | lastname: String!
23 | email: String!
24 | telephone: String!
25 | }
26 |
--------------------------------------------------------------------------------
/backend/clients/rsocket/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.novatecgmbh.commons-kotlin")
3 | id("org.springframework.boot")
4 | kotlin("plugin.spring")
5 | kotlin("jvm") version "1.6.20"
6 | }
7 |
8 | group = "${group}.clients"
9 |
10 | dependencies {
11 | implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
12 | implementation("org.springframework.boot:spring-boot-starter-rsocket")
13 | implementation("org.springframework.security:spring-security-rsocket")
14 | implementation("org.springframework.shell:spring-shell-starter:2.1.0-M3")
15 | }
16 |
--------------------------------------------------------------------------------
/backend/apis/rest/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/task/web/dto/AddTodoDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.task.web.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.task.api.AddTodoCommand
4 | import com.novatecgmbh.eventsourcing.axon.project.task.api.TaskId
5 | import com.novatecgmbh.eventsourcing.axon.project.task.api.TodoId
6 |
7 | data class AddTodoDto(val description: String) {
8 | fun toCommand(taskId: TaskId) =
9 | AddTodoCommand(identifier = taskId, todoId = TodoId(), description = description)
10 | }
11 |
--------------------------------------------------------------------------------
/backend/services/user/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/user/user/query/UserProjectionRepository.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.user.user.query
2 |
3 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
4 | import java.util.*
5 | import org.springframework.data.jpa.repository.JpaRepository
6 | import org.springframework.stereotype.Repository
7 |
8 | @Repository
9 | interface UserProjectionRepository : JpaRepository {
10 | fun findByExternalUserId(externalUserId: String): Optional
11 | }
12 |
--------------------------------------------------------------------------------
/frontend/src/app/can-ndjson-stream.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'can-ndjson-stream' {
2 | export type CancelCallback = (reason?: string) => Promise
3 | export type Result = {
4 | done: boolean;
5 | value: T | undefined;
6 | };
7 |
8 | export default function ndjsonStream(data: ReadableStream): {
9 | getReader: () => {
10 | read: () => Promise>;
11 | releaseLock: () => void;
12 | cancel: CancelCallback;
13 | };
14 | cancel: CancelCallback;
15 | }
16 | }
--------------------------------------------------------------------------------
/.idea/runConfigurations/UserApplication.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/CompanyApplication.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/ProjectApplication.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/backend/apis/rest/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/task/web/dto/RescheduleTaskDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.task.web.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.task.api.RescheduleTaskCommand
4 | import com.novatecgmbh.eventsourcing.axon.project.task.api.TaskId
5 | import java.time.LocalDate
6 |
7 | data class RescheduleTaskDto(val startDate: LocalDate, val endDate: LocalDate) {
8 | fun toCommand(taskId: TaskId) =
9 | RescheduleTaskCommand(identifier = taskId, startDate = startDate, endDate = endDate)
10 | }
11 |
--------------------------------------------------------------------------------
/backend/apis/rsocket/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/task/rsocket/dto/AssignTaskDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.task.rsocket.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.participant.api.ParticipantId
4 | import com.novatecgmbh.eventsourcing.axon.project.task.api.AssignTaskCommand
5 | import com.novatecgmbh.eventsourcing.axon.project.task.api.TaskId
6 |
7 | data class AssignTaskDto(val taskId: TaskId, val assignee: ParticipantId) {
8 | fun toCommand() = AssignTaskCommand(identifier = taskId, assignee = assignee)
9 | }
10 |
--------------------------------------------------------------------------------
/backend/platforms/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | maven("https://repo.spring.io/milestone")
5 | maven("https://repo.spring.io/snapshot")
6 | }
7 | }
8 |
9 | dependencyResolutionManagement {
10 | repositories {
11 | mavenCentral()
12 | maven("https://repo.spring.io/milestone")
13 | maven("https://repo.spring.io/snapshot")
14 | }
15 | }
16 |
17 | rootProject.name = "platforms"
18 | include("plugins-platform")
19 | include("product-platform")
20 | include("test-platform")
21 |
22 |
--------------------------------------------------------------------------------
/backend/services/project/api/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.novatecgmbh.commons-kotlin")
3 | kotlin("plugin.jpa")
4 | }
5 |
6 | group = "${group}.project"
7 |
8 | dependencies {
9 | implementation("com.novatecgmbh.eventsourcing.axon.common:api")
10 | implementation("com.novatecgmbh.eventsourcing.axon.company:api")
11 | implementation("com.novatecgmbh.eventsourcing.axon.user:api")
12 |
13 | implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
14 | implementation("javax.persistence:javax.persistence-api")
15 | implementation("org.axonframework:axon-modelling")
16 | }
17 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/backend/services/user/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/user/user/command/view/UserUniqueKeyRepository.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.user.user.command.view
2 |
3 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
4 | import org.springframework.data.jpa.repository.JpaRepository
5 | import org.springframework.stereotype.Repository
6 |
7 | @Repository
8 | interface UserUniqueKeyRepository : JpaRepository {
9 | fun existsByExternalUserId(externalUserId: String): Boolean
10 | fun existsByEmail(email: String): Boolean
11 | }
12 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/gRPC_API.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/backend/apis/rest/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/user/user/web/dto/RegisterUserDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.user.user.web.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.user.api.RegisterUserCommand
4 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
5 |
6 | data class RegisterUserDto(
7 | val firstname: String,
8 | val lastname: String,
9 | val email: String,
10 | val telephone: String
11 | ) {
12 | fun toCommand(externalUserId: String) =
13 | RegisterUserCommand(UserId(), externalUserId, firstname, lastname, email, telephone)
14 | }
15 |
--------------------------------------------------------------------------------
/backend/apis/rest/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: rest-api
4 | jackson:
5 | serialization:
6 | fail-on-empty-beans: false
7 | security:
8 | oauth2:
9 | resourceserver:
10 | jwt:
11 | issuer-uri: http://localhost:8999/realms/eventsourcing-with-axon
12 | jwk-set-uri: http://localhost:8999/realms/eventsourcing-with-axon/protocol/openid-connect/certs
13 |
14 | app:
15 | cors:
16 | allowed-origins: http://localhost:3000
17 |
18 | logging:
19 | level:
20 | ROOT: warn
21 | org:
22 | springframework.boot: info
--------------------------------------------------------------------------------
/backend/apis/rsocket/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/task/rsocket/dto/AddTodoDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.task.rsocket.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.task.api.AddTodoCommand
4 | import com.novatecgmbh.eventsourcing.axon.project.task.api.TaskId
5 | import com.novatecgmbh.eventsourcing.axon.project.task.api.TodoId
6 |
7 | data class AddTodoDto(val taskId: TaskId, val todoId: TodoId = TodoId(), val description: String) {
8 | fun toCommand() = AddTodoCommand(identifier = taskId, todoId = todoId, description = description)
9 | }
10 |
--------------------------------------------------------------------------------
/backend/apis/rsocket/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/task/rsocket/dto/RescheduleTaskDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.task.rsocket.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.task.api.RescheduleTaskCommand
4 | import com.novatecgmbh.eventsourcing.axon.project.task.api.TaskId
5 | import java.time.LocalDate
6 |
7 | data class RescheduleTaskDto(val identifier: TaskId, val startDate: LocalDate, val endDate: LocalDate) {
8 | fun toCommand() =
9 | RescheduleTaskCommand(identifier = identifier, startDate = startDate, endDate = endDate)
10 | }
11 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/GraphQL_API.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/RSocket_API.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/backend/apis/grpc-lib/src/main/proto/userservice.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | import "google/protobuf/timestamp.proto";
4 | import "google/protobuf/empty.proto";
5 |
6 | option java_multiple_files = true;
7 | option java_package = "com.novatecgmbh.eventsourcing.axon";
8 | option java_outer_classname = "UserServiceProto";
9 |
10 | service UserService {
11 | rpc findAll(google.protobuf.Empty) returns (UserList) {}
12 | }
13 |
14 | message UserList {
15 | repeated User users = 1;
16 | }
17 |
18 | message User {
19 | string identifier = 1;
20 | string firstname = 2;
21 | string lastname = 3;
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/backend/services/project/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/project/query/ProjectProjectionRepository.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.project.query
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
4 | import org.springframework.data.jpa.repository.JpaRepository
5 | import org.springframework.stereotype.Repository
6 |
7 | @Repository
8 | interface ProjectProjectionRepository : JpaRepository {
9 | fun findAllByIdentifierInOrderByName(projectIds: Collection): List
10 | }
11 |
--------------------------------------------------------------------------------
/backend/services/project/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/project/api/ValueObjects.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.project.api
2 |
3 | import com.fasterxml.jackson.annotation.JsonValue
4 | import java.io.Serializable
5 | import java.util.*
6 | import javax.persistence.Embeddable
7 |
8 | @Embeddable
9 | data class ProjectId(@get:JsonValue val identifier: String) : Serializable {
10 | constructor() : this(UUID.randomUUID().toString())
11 |
12 | override fun toString(): String = identifier
13 | }
14 |
15 | enum class ProjectStatus {
16 | ON_TIME,
17 | DELAYED
18 | }
19 |
--------------------------------------------------------------------------------
/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/frontend/src/components/Home.tsx:
--------------------------------------------------------------------------------
1 | import {Redirect} from "react-router-dom";
2 | import {useAppSelector} from "../app/hooks";
3 | import {selectIsAuthenticated, selectIsRegistered} from "../features/auth/authSlice";
4 |
5 | export function Home() {
6 | const isAuthenticated = useAppSelector(selectIsAuthenticated);
7 | const isRegistered = useAppSelector(selectIsRegistered);
8 |
9 | if (isRegistered) {
10 | return
11 | } else if (isAuthenticated) {
12 | return
13 | } else {
14 | return
15 | }
16 | }
--------------------------------------------------------------------------------
/backend/services/common/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/application/sequencing/RootContextId.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.application.sequencing
2 |
3 | import com.novatecgmbh.eventsourcing.axon.application.sequencing.RootContextIdentifierSequencingPolicy.Companion.ROOT_CONTEXT_IDENTIFIER_META_DATA_KEY
4 | import org.axonframework.messaging.annotation.MetaDataValue
5 |
6 | @MustBeDocumented
7 | @Target(AnnotationTarget.VALUE_PARAMETER)
8 | @Retention(AnnotationRetention.RUNTIME)
9 | @MetaDataValue(ROOT_CONTEXT_IDENTIFIER_META_DATA_KEY, required = true)
10 | annotation class RootContextId
11 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Data_Importer.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/backend/apis/grpc/src/main/resources/application.yaml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: grpc-api
4 | security:
5 | oauth2:
6 | resourceserver:
7 | jwt:
8 | issuer-uri: http://localhost:8999/realms/eventsourcing-with-axon
9 |
10 | grpc:
11 | server:
12 | security:
13 | enabled: true
14 | # certificateChain: classpath:certs/server.crt
15 | # privateKey: classpath:certs/serverprivate.key
16 | key-store: classpath:certs/server-keystore.p12
17 | key-store-password: server
18 |
19 | logging:
20 | level:
21 | ROOT: info
22 | org:
23 | springframework.boot: info
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
--------------------------------------------------------------------------------
/backend/apis/rest/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/project/web/dto/RescheduleProjectDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.project.web.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
4 | import com.novatecgmbh.eventsourcing.axon.project.project.api.RescheduleProjectCommand
5 | import java.time.LocalDate
6 |
7 | data class RescheduleProjectDto(
8 | val version: Long,
9 | val startDate: LocalDate,
10 | val deadline: LocalDate
11 | ) {
12 | fun toCommand(projectId: ProjectId) =
13 | RescheduleProjectCommand(projectId, version, startDate, deadline)
14 | }
15 |
--------------------------------------------------------------------------------
/backend/apis/grpc/src/main/resources/certs/generate.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | openssl genrsa -des3 -out serverprivate.key 2048
4 | # enter passphrase: server
5 |
6 | openssl req -new -key serverprivate.key -out server.csr -config server-csr.conf
7 |
8 | openssl x509 -req -days 3650 -in server.csr -signkey serverprivate.key -out server.crt -extfile server-csr.conf -extensions v3_req
9 |
10 | keytool -import -file server.crt -alias serverCA -keystore server-truststore.jks
11 | # enter passphrase: server
12 |
13 | openssl pkcs12 -export -in server.crt -inkey serverprivate.key -certfile server.crt -name "servercert" -out server-keystore.p12
14 | # enter passphrase: server
--------------------------------------------------------------------------------
/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/backend/apis/rest/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/project/web/dto/UpdateProjectDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.project.web.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
4 | import com.novatecgmbh.eventsourcing.axon.project.project.api.UpdateProjectCommand
5 | import java.time.LocalDate
6 |
7 | data class UpdateProjectDto(
8 | val version: Long,
9 | val name: String,
10 | val startDate: LocalDate,
11 | val deadline: LocalDate
12 | ) {
13 | fun toCommand(projectId: ProjectId) =
14 | UpdateProjectCommand(projectId, version, name, startDate, deadline)
15 | }
16 |
--------------------------------------------------------------------------------
/backend/apis/rsocket/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/project/rsocket/dto/RescheduleProjectDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.project.rsocket.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
4 | import com.novatecgmbh.eventsourcing.axon.project.project.api.RescheduleProjectCommand
5 | import java.time.LocalDate
6 |
7 | data class RescheduleProjectDto(
8 | val identifier: ProjectId,
9 | val version: Long,
10 | val startDate: LocalDate,
11 | val deadline: LocalDate,
12 | ) {
13 | fun toCommand() = RescheduleProjectCommand(identifier, version, startDate, deadline)
14 | }
15 |
--------------------------------------------------------------------------------
/backend/apis/rest/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/employee/web/dto/CreateEmployeeDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.employee.web.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.company.employee.api.CreateEmployeeCommand
5 | import com.novatecgmbh.eventsourcing.axon.company.employee.api.EmployeeId
6 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
7 |
8 | data class CreateEmployeeDto(val companyId: CompanyId, val userId: UserId) {
9 | fun toCommand(identifier: EmployeeId) = CreateEmployeeCommand(identifier, companyId, userId)
10 | }
11 |
--------------------------------------------------------------------------------
/backend/platforms/plugins-platform/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("java-platform")
3 | }
4 |
5 | group = "com.novatecgmbh.platform"
6 |
7 | dependencies {
8 | constraints {
9 | api("io.spring.gradle:dependency-management-plugin:1.0.11.RELEASE")
10 | api("org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.6.10")
11 | api("org.jetbrains.kotlin.plugin.jpa:org.jetbrains.kotlin.plugin.jpa.gradle.plugin:1.6.10")
12 | api("org.jetbrains.kotlin.plugin.spring:org.jetbrains.kotlin.plugin.spring.gradle.plugin:1.6.10")
13 | api("org.springframework.boot:org.springframework.boot.gradle.plugin:2.7.0")
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/backend/services/project/application/src/main/resources/db/migration/postgres/V6__ADD_TASK_TODOS.sql:
--------------------------------------------------------------------------------
1 | create table task_todos
2 | (
3 | task_identifier varchar(255) not null,
4 | description varchar(255) not null,
5 | is_done boolean not null,
6 | identifier varchar(255)
7 | );
8 |
9 | alter table if exists task_todos
10 | add constraint UK_TaskTodos_Identifier unique (identifier, task_identifier);
11 |
12 | alter table if exists task_todos
13 | add constraint FK_TaskTodos_TaskIdentifier
14 | foreign key (task_identifier)
15 | references tasks;
16 |
17 | create index IX_TaskTodo_TaskIden on task_todos (task_identifier);
--------------------------------------------------------------------------------
/.idea/checkstyle-idea.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
16 |
--------------------------------------------------------------------------------
/backend/clients/rsocket/src/main/kotlin/com/novatecgmbh/rsocket/client/demo/RSocketDemoClient.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.rsocket.client.demo
2 |
3 | import org.springframework.boot.autoconfigure.SpringBootApplication
4 | import org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration
5 | import org.springframework.boot.context.properties.ConfigurationPropertiesScan
6 | import org.springframework.boot.runApplication
7 |
8 | @SpringBootApplication(exclude = [RSocketSecurityAutoConfiguration::class])
9 | @ConfigurationPropertiesScan
10 | class RSocketDemoClient
11 |
12 | fun main(args: Array) {
13 | runApplication(*args)
14 | }
15 |
--------------------------------------------------------------------------------
/backend/services/user/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/user/user/command/view/UserUniqueKeyProjection.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.user.user.command.view
2 |
3 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
4 | import javax.persistence.Column
5 | import javax.persistence.EmbeddedId
6 | import javax.persistence.Entity
7 | import javax.persistence.Table
8 |
9 | @Entity
10 | @Table(name = "user_unique_key")
11 | class UserUniqueKeyProjection(
12 | @EmbeddedId var identifier: UserId,
13 | @Column(nullable = false, unique = true) var externalUserId: String,
14 | @Column(nullable = false, unique = true) var email: String
15 | )
16 |
--------------------------------------------------------------------------------
/backend/services/project/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/project/command/InternalCommands.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.project.command
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectCommand
4 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
5 | import java.time.LocalDate
6 | import org.axonframework.modelling.command.TargetAggregateIdentifier
7 |
8 | data class UpdateActualScheduleInternalCommand(
9 | @TargetAggregateIdentifier override val aggregateIdentifier: ProjectId,
10 | val startDate: LocalDate,
11 | val endDate: LocalDate,
12 | ) : ProjectCommand(aggregateIdentifier)
13 |
--------------------------------------------------------------------------------
/backend/apis/rest/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/application/config/WebMvcConfig.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.application.config
2 |
3 | import com.novatecgmbh.eventsourcing.axon.application.security.UserPrincipalResolver
4 | import org.springframework.context.annotation.Configuration
5 | import org.springframework.web.method.support.HandlerMethodArgumentResolver
6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
7 |
8 | @Configuration
9 | class WebMvcConfig : WebMvcConfigurer {
10 |
11 | override fun addArgumentResolvers(resolvers: MutableList) {
12 | resolvers.add(UserPrincipalResolver())
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/backend/services/user/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/user/api/Events.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.user.api
2 |
3 | abstract class UserEvent(
4 | open val aggregateIdentifier: UserId,
5 | )
6 |
7 | data class UserRegisteredEvent(
8 | override val aggregateIdentifier: UserId,
9 | val externalUserId: String,
10 | val firstname: String,
11 | val lastname: String,
12 | val email: String,
13 | val telephone: String
14 | ) : UserEvent(aggregateIdentifier)
15 |
16 | data class UserRenamedEvent(
17 | override val aggregateIdentifier: UserId,
18 | val firstname: String,
19 | val lastname: String,
20 | ) : UserEvent(aggregateIdentifier)
21 |
--------------------------------------------------------------------------------
/backend/apis/graphql/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/application/config/WebMvcConfig.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.application.config
2 |
3 | import com.novatecgmbh.eventsourcing.axon.application.security.UserPrincipalResolver
4 | import org.springframework.context.annotation.Configuration
5 | import org.springframework.web.method.support.HandlerMethodArgumentResolver
6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
7 |
8 | @Configuration
9 | class WebMvcConfig : WebMvcConfigurer {
10 |
11 | override fun addArgumentResolvers(resolvers: MutableList) {
12 | resolvers.add(UserPrincipalResolver())
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/backend/services/project/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/participant/api/Events.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.participant.api
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
5 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
6 |
7 | abstract class ParticipantEvent(open val aggregateIdentifier: ParticipantId)
8 |
9 | data class ParticipantCreatedEvent(
10 | override val aggregateIdentifier: ParticipantId,
11 | val projectId: ProjectId,
12 | val companyId: CompanyId,
13 | val userId: UserId
14 | ) : ParticipantEvent(aggregateIdentifier)
15 |
--------------------------------------------------------------------------------
/frontend/src/features/api/apiSlice.ts:
--------------------------------------------------------------------------------
1 | import {createApi, fetchBaseQuery} from "@reduxjs/toolkit/query/react";
2 | import {selectToken} from "../auth/authSlice";
3 | import {RootState} from "../../app/store";
4 |
5 | export const baseUrl = 'http://localhost:8080/v2';
6 |
7 | export const apiSlice = createApi({
8 | reducerPath: 'api',
9 | tagTypes: ['currentUser'],
10 | baseQuery: fetchBaseQuery({
11 | baseUrl: baseUrl,
12 | prepareHeaders: (headers, {getState}) => {
13 | const token = selectToken(getState() as RootState);
14 | headers.set('Authorization', `Bearer ${token}`);
15 | return headers;
16 | }
17 | }),
18 | endpoints: builder => ({})
19 | });
--------------------------------------------------------------------------------
/backend/apis/rsocket/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/employee/rsocket/dtos/CreateEmployeeDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.employee.rsocket.dtos
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.company.employee.api.CreateEmployeeCommand
5 | import com.novatecgmbh.eventsourcing.axon.company.employee.api.EmployeeId
6 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
7 |
8 | data class CreateEmployeeDto(
9 | val identifier: EmployeeId = EmployeeId(),
10 | val companyId: CompanyId,
11 | val userId: UserId
12 | ) {
13 | fun toCommand() = CreateEmployeeCommand(identifier, companyId, userId)
14 | }
15 |
--------------------------------------------------------------------------------
/frontend/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import {store} from './app/store';
6 | import {Provider} from 'react-redux';
7 | import * as serviceWorker from './serviceWorker';
8 |
9 | ReactDOM.render(
10 |
11 |
12 |
13 |
14 | ,
15 | document.getElementById('root')
16 | );
17 |
18 | // If you want your app to work offline and load faster, you can change
19 | // unregister() to register() below. Note this comes with some pitfalls.
20 | // Learn more about service workers: https://bit.ly/CRA-PWA
21 | serviceWorker.unregister();
22 |
--------------------------------------------------------------------------------
/backend/apis/rest/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/project/web/dto/CreateProjectDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.project.web.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.project.project.api.CreateProjectCommand
5 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
6 | import java.time.LocalDate
7 |
8 | data class CreateProjectDto(
9 | val name: String,
10 | val startDate: LocalDate,
11 | val deadline: LocalDate,
12 | val companyId: CompanyId
13 | ) {
14 | fun toCommand(projectId: ProjectId) =
15 | CreateProjectCommand(projectId, name, startDate, deadline, companyId)
16 | }
17 |
--------------------------------------------------------------------------------
/backend/services/company/application/src/main/resources/db/migration/postgres/V3__QUERY_MODEL_TABLE.sql:
--------------------------------------------------------------------------------
1 | create table company
2 | (
3 | identifier varchar(255) not null,
4 | name varchar(255) not null,
5 | version int8 not null,
6 | primary key (identifier)
7 | );
8 |
9 | create table employee
10 | (
11 | identifier varchar(255) not null,
12 | company_id varchar(255) not null,
13 | is_admin boolean not null,
14 | is_project_manager boolean not null,
15 | user_first_name varchar(255),
16 | user_id varchar(255) not null,
17 | user_last_name varchar(255),
18 | version int8 not null,
19 | primary key (identifier)
20 | );
--------------------------------------------------------------------------------
/backend/clients/grpc/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.novatecgmbh.commons-kotlin")
3 | id("org.springframework.boot")
4 | kotlin("plugin.spring")
5 | kotlin("jvm") version "1.6.20"
6 | }
7 |
8 | group = "${group}.clients"
9 |
10 | dependencies {
11 | implementation("com.google.protobuf:protobuf-java-util:3.19.2")
12 | implementation("com.novatecgmbh.eventsourcing.axon.apis:grpc-lib")
13 | implementation("net.devh:grpc-client-spring-boot-starter:2.13.1.RELEASE")
14 | implementation("org.springframework.boot:spring-boot-starter")
15 | implementation("org.springframework.boot:spring-boot-starter")
16 | implementation("org.springframework.shell:spring-shell-starter:2.1.0-M3")
17 | implementation("org.springframework:spring-web")
18 | }
19 |
--------------------------------------------------------------------------------
/backend/services/company/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/references/ReferenceCheckerService.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.references
2 |
3 | import org.springframework.stereotype.Service
4 |
5 | @Service
6 | class ReferenceCheckerService(private val repository: RootContextIdMappingRepository) {
7 | fun assertCompanyExists(companyId: String) {
8 | if (!repository.existsCompanyById(companyId)) {
9 | throw IllegalArgumentException("Referenced Company does not exist")
10 | }
11 | }
12 |
13 | fun assertUserExists(userId: String) {
14 | if (!repository.existsUserById(userId)) {
15 | throw IllegalArgumentException("Referenced User does not exist")
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/backend/services/company/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/employee/command/view/EmployeeUniqueKeyRepository.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.employee.command.view
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.company.employee.api.EmployeeId
5 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
6 | import org.springframework.data.jpa.repository.JpaRepository
7 | import org.springframework.stereotype.Repository
8 |
9 | @Repository
10 | interface EmployeeUniqueKeyRepository : JpaRepository {
11 | fun existsByCompanyIdAndUserId(companyId: CompanyId, userId: UserId): Boolean
12 | }
13 |
--------------------------------------------------------------------------------
/backend/services/project/application/src/main/resources/db/migration/postgres/V2__COMMAND_MODEL_TABLES.sql:
--------------------------------------------------------------------------------
1 | create table participant_unique_key
2 | (
3 | identifier varchar(255) not null,
4 | company_id varchar(255) not null,
5 | project_id varchar(255) not null,
6 | user_id varchar(255) not null,
7 | primary key (identifier)
8 | );
9 |
10 | alter table if exists participant_unique_key
11 | add constraint participant_unique_key_constraint unique (project_id, company_id, user_id);
12 |
13 | create table task_schedule_projection
14 | (
15 | identifier varchar(255) not null,
16 | end_date date not null,
17 | project_id varchar(255) not null,
18 | start_date date not null,
19 | primary key (identifier)
20 | );
21 |
22 |
--------------------------------------------------------------------------------
/backend/apis/rsocket/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/project/rsocket/dto/CreateProjectDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.project.rsocket.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.project.project.api.CreateProjectCommand
5 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
6 | import java.time.LocalDate
7 |
8 | data class CreateProjectDto(
9 | val identifier: ProjectId = ProjectId(),
10 | val name: String,
11 | val startDate: LocalDate,
12 | val deadline: LocalDate,
13 | val companyId: CompanyId
14 | ) {
15 | fun toCommand() = CreateProjectCommand(identifier, name, startDate, deadline, companyId)
16 | }
17 |
--------------------------------------------------------------------------------
/backend/services/company/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/employee/query/EmployeeProjectionRepository.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.employee.query
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.company.employee.api.EmployeeId
5 | import org.springframework.data.jpa.repository.JpaRepository
6 | import org.springframework.stereotype.Repository
7 |
8 | @Repository
9 | interface EmployeeProjectionRepository : JpaRepository {
10 | fun findAllByCompanyId(companyId: CompanyId): MutableIterable
11 | fun findAllByCompanyIdIn(companyId: Set): MutableIterable
12 | }
13 |
--------------------------------------------------------------------------------
/backend/services/project/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/participant/query/ParticipantProjectionRepository.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.participant.query
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.participant.api.ParticipantId
4 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
5 | import org.springframework.data.jpa.repository.JpaRepository
6 | import org.springframework.stereotype.Repository
7 |
8 | @Repository
9 | interface ParticipantProjectionRepository : JpaRepository {
10 | fun findAllByProjectId(projectId: ProjectId): List
11 | fun findAllByProjectIdIn(projectId: Set): List
12 | }
13 |
--------------------------------------------------------------------------------
/backend/services/company/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/company/query/CompanyProjection.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.company.query
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyQueryResult
5 | import javax.persistence.Column
6 | import javax.persistence.EmbeddedId
7 | import javax.persistence.Entity
8 | import javax.persistence.Table
9 |
10 | @Entity
11 | @Table(name = "company")
12 | class CompanyProjection(
13 | @EmbeddedId var identifier: CompanyId,
14 | @Column(nullable = false) var version: Long,
15 | @Column(nullable = false) var name: String
16 | ) {
17 | fun toQueryResult() = CompanyQueryResult(identifier, version, name)
18 | }
19 |
--------------------------------------------------------------------------------
/backend/services/common/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/application/security/UnregisteredUserPrincipal.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.application.security
2 |
3 | import org.springframework.security.core.GrantedAuthority
4 | import org.springframework.security.core.userdetails.UserDetails
5 |
6 | data class UnregisteredUserPrincipal(private val username: String) : UserDetails {
7 | override fun getAuthorities(): MutableCollection = mutableSetOf()
8 |
9 | override fun getPassword(): String? = null
10 |
11 | override fun getUsername(): String = username
12 |
13 | override fun isAccountNonExpired() = true
14 |
15 | override fun isAccountNonLocked() = true
16 |
17 | override fun isCredentialsNonExpired() = true
18 |
19 | override fun isEnabled() = true
20 | }
21 |
--------------------------------------------------------------------------------
/backend/apis/common/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.novatecgmbh.commons-kotlin")
3 | id("io.spring.dependency-management")
4 | kotlin("plugin.spring")
5 | }
6 |
7 | group = "${group}.apis"
8 |
9 | dependencies {
10 | api("com.novatecgmbh.eventsourcing.axon.common:api")
11 | api("com.novatecgmbh.eventsourcing.axon.common:auditing")
12 | api("com.novatecgmbh.eventsourcing.axon.company:api")
13 | api("com.novatecgmbh.eventsourcing.axon.project:api")
14 | api("com.novatecgmbh.eventsourcing.axon.user:api")
15 |
16 | implementation("org.axonframework:axon-spring-boot-starter")
17 | implementation("org.axonframework.extensions.kotlin:axon-kotlin")
18 | implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
19 | implementation("org.springframework.boot:spring-boot-starter-security")
20 | }
21 |
--------------------------------------------------------------------------------
/backend/services/user/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/user/api/Commands.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.user.api
2 |
3 | import org.axonframework.modelling.command.TargetAggregateIdentifier
4 |
5 | abstract class UserCommand(
6 | open val aggregateIdentifier: UserId,
7 | )
8 |
9 | data class RegisterUserCommand(
10 | @TargetAggregateIdentifier override val aggregateIdentifier: UserId,
11 | val externalUserId: String,
12 | val firstname: String,
13 | val lastname: String,
14 | val email: String,
15 | val telephone: String
16 | ) : UserCommand(aggregateIdentifier)
17 |
18 | data class RenameUserCommand(
19 | @TargetAggregateIdentifier override val aggregateIdentifier: UserId,
20 | val firstname: String,
21 | val lastname: String
22 | ) : UserCommand(aggregateIdentifier)
23 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
25 |
26 | # dependencies
27 | /node_modules
28 | /.pnp
29 | .pnp.js
30 |
31 | # testing
32 | /coverage
33 |
34 | # production
35 | /build
36 |
37 | # misc
38 | .DS_Store
39 | .env.local
40 | .env.development.local
41 | .env.test.local
42 | .env.production.local
43 |
44 | npm-debug.log*
45 | yarn-debug.log*
46 | yarn-error.log*
47 |
--------------------------------------------------------------------------------
/frontend/src/components/HighlightingAnimation.ts:
--------------------------------------------------------------------------------
1 | import {useEffect, useRef, useState} from "react";
2 |
3 | export function useHighlighting(property: any) {
4 | const [highlightingColor, setHighlightingColor] = useState("none")
5 | const previousProp = useRef(property).current
6 | useEffect(() => {
7 | if (previousProp !== property) {
8 | setHighlightingColor("AnimateHighlighting 2s");
9 | const timer = setTimeout(() => {
10 | setHighlightingColor("none");
11 | }, 2000); // The duration of the animation defined in the CSS file
12 |
13 | // Clear the timer before setting a new one
14 | return () => {
15 | clearTimeout(timer);
16 | };
17 | }
18 | }, [property, previousProp]);
19 | return highlightingColor
20 | }
--------------------------------------------------------------------------------
/backend/services/common/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/application/sequencing/RootContextIdentifierSequencingPolicy.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.application.sequencing
2 |
3 | import org.axonframework.eventhandling.EventMessage
4 | import org.axonframework.eventhandling.async.SequencingPolicy
5 |
6 | class RootContextIdentifierSequencingPolicy(
7 | private val fallbackSequencingPolicy: SequencingPolicy>
8 | ) : SequencingPolicy> {
9 | override fun getSequenceIdentifierFor(event: EventMessage<*>): Any? =
10 | event.metaData[ROOT_CONTEXT_IDENTIFIER_META_DATA_KEY]
11 | ?: fallbackSequencingPolicy.getSequenceIdentifierFor(event)
12 |
13 | companion object {
14 | const val ROOT_CONTEXT_IDENTIFIER_META_DATA_KEY = "rootContextId"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/backend/apis/graphql/src/main/resources/application.yaml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: graphql-api
4 | graphql:
5 | graphiql:
6 | enabled: false
7 | schema:
8 | printer:
9 | enabled: true
10 | websocket:
11 | path: /wsgraphql
12 | jackson:
13 | serialization:
14 | fail-on-empty-beans: false
15 | security:
16 | oauth2:
17 | resourceserver:
18 | jwt:
19 | issuer-uri: http://localhost:8999/realms/eventsourcing-with-axon
20 |
21 | app:
22 | cors:
23 | allowed-origins: http://localhost:3000
24 |
25 | logging:
26 | level:
27 | ROOT: warn
28 | io.jaegertracing.internal.reporters.LoggingReporter: info
29 | org:
30 | axonframework.eventhandling.pooled.Coordinator: info
31 | flywaydb.core.internal.command: info
32 | springframework.boot: info
--------------------------------------------------------------------------------
/backend/apis/graphql/src/main/resources/graphql/company.graphqls:
--------------------------------------------------------------------------------
1 | extend type Query {
2 | company(identifier:ID!): Company
3 | companies: [Company]
4 | employee(identifier:ID!): Employee
5 | }
6 |
7 | extend type Mutation {
8 | createCompany(name: String!): ID!
9 | createEmployee(companyId: ID!, userId: ID!): ID!
10 | grantProjectManagerPermissionToEmployee(identifier: ID!): Int!
11 | removeProjectManagerPermissionFromEmployee(identifier: ID!): Int!
12 | grantAdminPermissionToEmployee(identifier: ID!): Int!
13 | removeAdminPermissionFromEmployee(identifier: ID!): Int!
14 | }
15 |
16 | type Company {
17 | identifier: ID!
18 | name: String!
19 | employees: [Employee]
20 | }
21 |
22 | type Employee {
23 | identifier: ID!,
24 | user: User!
25 | isAdmin: Boolean!
26 | isProjectManager: Boolean!
27 | }
--------------------------------------------------------------------------------
/backend/services/project/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/task/api/ValueObjects.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.task.api
2 |
3 | import com.fasterxml.jackson.annotation.JsonValue
4 | import java.io.Serializable
5 | import java.util.*
6 | import javax.persistence.Embeddable
7 |
8 | @Embeddable
9 | data class TaskId(@get:JsonValue val identifier: String) : Serializable {
10 | constructor() : this(UUID.randomUUID().toString())
11 |
12 | override fun toString(): String = identifier
13 | }
14 |
15 | @Embeddable
16 | data class TodoId(@get:JsonValue var identifier: String) : Serializable {
17 | constructor() : this(UUID.randomUUID().toString())
18 |
19 | override fun toString(): String = identifier
20 | }
21 |
22 | enum class TaskStatusEnum {
23 | PLANNED,
24 | STARTED,
25 | COMPLETED
26 | }
27 |
--------------------------------------------------------------------------------
/frontend/src/features/tasks/taskDrawerSlice.ts:
--------------------------------------------------------------------------------
1 | import {createSlice, PayloadAction} from "@reduxjs/toolkit";
2 | import {RootState} from "../../app/store";
3 |
4 | export interface TodoDrawerState {
5 | selectedTaskId?: string;
6 | }
7 |
8 | export const slice = createSlice({
9 | name: "taskDrawer",
10 | initialState: {} as TodoDrawerState,
11 | reducers: {
12 | taskSelected(state, action: PayloadAction) {
13 | state.selectedTaskId = action.payload;
14 | },
15 | closeTaskDrawer(state, _: PayloadAction) {
16 | state.selectedTaskId = undefined;
17 | }
18 | }
19 | });
20 |
21 | export const {taskSelected, closeTaskDrawer} = slice.actions;
22 |
23 | export const selectSelectedTaskId = (state: RootState) => state.taskDrawer.selectedTaskId;
24 |
25 | export default slice.reducer;
--------------------------------------------------------------------------------
/backend/apis/rest/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/participant/web/dto/CreateParticipantDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.participant.web.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.project.participant.api.CreateParticipantCommand
5 | import com.novatecgmbh.eventsourcing.axon.project.participant.api.ParticipantId
6 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
7 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
8 |
9 | data class CreateParticipantDto(
10 | val projectId: ProjectId,
11 | val companyId: CompanyId,
12 | val userId: UserId
13 | ) {
14 | fun toCommand(identifier: ParticipantId) =
15 | CreateParticipantCommand(identifier, projectId, companyId, userId)
16 | }
17 |
--------------------------------------------------------------------------------
/backend/apis/rsocket/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: rsocket
4 |
5 | jackson:
6 | serialization:
7 | fail-on-empty-beans: false
8 | rsocket:
9 | server:
10 | transport: websocket
11 | mapping-path: /rsocket
12 | security:
13 | oauth2:
14 | resourceserver:
15 | jwt:
16 | issuer-uri: http://localhost:8999/realms/eventsourcing-with-axon
17 | jwk-set-uri: http://localhost:8999/realms/eventsourcing-with-axon/protocol/openid-connect/certs
18 |
19 | app:
20 | cors:
21 | allowed-origins: http://localhost:3000
22 |
23 | opentracing:
24 | spring:
25 | cloud:
26 | websocket:
27 | enabled: false
28 |
29 | logging:
30 | level:
31 | ROOT: info
32 | org:
33 | springframework.boot: info
34 | springframework.web.socket: debug
--------------------------------------------------------------------------------
/backend/apis/grpc-lib/src/main/proto/projectservice.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | import "google/protobuf/empty.proto";
4 | import "google/protobuf/timestamp.proto";
5 |
6 | option java_multiple_files = true;
7 | option java_package = "com.novatecgmbh.eventsourcing.axon";
8 | option java_outer_classname = "ProjectServiceProtos";
9 |
10 | service ProjectService {
11 | rpc findMyProjects(google.protobuf.Empty) returns (ProjectList) {}
12 | }
13 |
14 | message ProjectList {
15 | repeated Project projects = 1;
16 | }
17 |
18 | message Project {
19 | string identifier = 1;
20 | string name = 2;
21 | enum ProjectStatus {
22 | ON_TIME = 0;
23 | DELAYED = 1;
24 | }
25 | ProjectStatus status = 3;
26 | google.protobuf.Timestamp start_date = 4;
27 | google.protobuf.Timestamp deadline = 5;
28 | google.protobuf.Timestamp actual_end_date = 6;
29 | }
30 |
--------------------------------------------------------------------------------
/backend/services/project/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/participant/api/Commands.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.participant.api
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
5 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
6 | import org.axonframework.modelling.command.TargetAggregateIdentifier
7 |
8 | abstract class ParticipantCommand(
9 | @TargetAggregateIdentifier open val aggregateIdentifier: ParticipantId,
10 | )
11 |
12 | data class CreateParticipantCommand(
13 | @TargetAggregateIdentifier override val aggregateIdentifier: ParticipantId,
14 | val projectId: ProjectId,
15 | val companyId: CompanyId,
16 | val userId: UserId,
17 | ) : ParticipantCommand(aggregateIdentifier)
18 |
--------------------------------------------------------------------------------
/backend/services/user/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/user/user/command/view/UserUniqueKeyProjector.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.user.user.command.view
2 |
3 | import com.novatecgmbh.eventsourcing.axon.user.api.UserRegisteredEvent
4 | import org.axonframework.config.ProcessingGroup
5 | import org.axonframework.eventhandling.EventHandler
6 | import org.springframework.stereotype.Component
7 |
8 | @Component
9 | @ProcessingGroup("user-unique-key-projector")
10 | class UserUniqueKeyProjector(private val repository: UserUniqueKeyRepository) {
11 | @EventHandler
12 | fun on(event: UserRegisteredEvent) {
13 | repository.save(
14 | UserUniqueKeyProjection(
15 | identifier = event.aggregateIdentifier,
16 | externalUserId = event.externalUserId,
17 | email = event.email))
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/docker/docker_postgres_init.sql:
--------------------------------------------------------------------------------
1 | CREATE USER userapp WITH PASSWORD 'axon' CREATEDB;
2 | CREATE DATABASE userdb
3 | WITH
4 | OWNER = userapp
5 | ENCODING = 'UTF8'
6 | LC_COLLATE = 'en_US.utf8'
7 | LC_CTYPE = 'en_US.utf8'
8 | TABLESPACE = pg_default
9 | CONNECTION LIMIT = -1;
10 |
11 | CREATE USER companyapp WITH PASSWORD 'axon' CREATEDB;
12 | CREATE DATABASE companydb
13 | WITH
14 | OWNER = companyapp
15 | ENCODING = 'UTF8'
16 | LC_COLLATE = 'en_US.utf8'
17 | LC_CTYPE = 'en_US.utf8'
18 | TABLESPACE = pg_default
19 | CONNECTION LIMIT = -1;
20 |
21 | CREATE USER projectapp WITH PASSWORD 'axon' CREATEDB;
22 | CREATE DATABASE projectdb
23 | WITH
24 | OWNER = projectapp
25 | ENCODING = 'UTF8'
26 | LC_COLLATE = 'en_US.utf8'
27 | LC_CTYPE = 'en_US.utf8'
28 | TABLESPACE = pg_default
29 | CONNECTION LIMIT = -1;
--------------------------------------------------------------------------------
/backend/platforms/product-platform/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("java-platform")
3 | }
4 |
5 | group = "com.novatecgmbh.platform"
6 |
7 | // allow the definition of dependencies to other platforms like the Axon BOM
8 | javaPlatform.allowDependencies()
9 |
10 | dependencies {
11 | api(platform("org.springframework.boot:spring-boot-dependencies:2.7.0"))
12 | api(platform("org.springframework.cloud:spring-cloud-dependencies:2021.0.2"))
13 | api(platform("org.axonframework:axon-bom:4.5.11"))
14 |
15 | constraints {
16 | api("io.opentracing.contrib:opentracing-spring-jaeger-cloud-starter:3.3.1")
17 | api("io.opentracing.contrib:opentracing-spring-jaeger-starter:3.2.2")
18 | api("io.projectreactor:reactor-core:3.4.16")
19 | api("io.projectreactor.kotlin:reactor-kotlin-extension:1.1.6")
20 | api("javax.persistence:javax.persistence-api:2.2")
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/backend/apis/rsocket/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/participant/rsocket/dto/CreateParticipantDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.participant.rsocket.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.project.participant.api.CreateParticipantCommand
5 | import com.novatecgmbh.eventsourcing.axon.project.participant.api.ParticipantId
6 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
7 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
8 |
9 | data class CreateParticipantDto(
10 | val identifier: ParticipantId = ParticipantId(),
11 | val projectId: ProjectId,
12 | val companyId: CompanyId,
13 | val userId: UserId
14 | ) {
15 | fun toCommand() = CreateParticipantCommand(identifier, projectId, companyId, userId)
16 | }
17 |
--------------------------------------------------------------------------------
/backend/services/common/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/common/command/BaseAggregate.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.common.command
2 |
3 | import com.novatecgmbh.eventsourcing.axon.application.sequencing.RootContextIdentifierSequencingPolicy.Companion.ROOT_CONTEXT_IDENTIFIER_META_DATA_KEY
4 | import org.axonframework.messaging.MetaData
5 | import org.axonframework.modelling.command.AggregateLifecycle
6 |
7 | abstract class BaseAggregate {
8 | abstract fun getRootContextId(): String
9 |
10 | protected fun apply(
11 | payload: Any,
12 | metaData: MetaData = MetaData(mutableMapOf()),
13 | rootContextId: String = getRootContextId()
14 | ) {
15 | AggregateLifecycle.apply(
16 | payload,
17 | metaData.mergedWith(mutableMapOf(ROOT_CONTEXT_IDENTIFIER_META_DATA_KEY to rootContextId)))
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/frontend/src/components/scaffold/scaffoldSlice.ts:
--------------------------------------------------------------------------------
1 | import {createSlice, PayloadAction} from "@reduxjs/toolkit";
2 | import {RootState} from "../../app/store";
3 |
4 | export interface ScaffoldState {
5 | isNavDrawerOpen: boolean;
6 | }
7 |
8 | const initialState = {
9 | isNavDrawerOpen: false,
10 | } as ScaffoldState;
11 |
12 | const slice = createSlice({
13 | name: 'scaffold',
14 | initialState: initialState,
15 | reducers: {
16 | openNavDrawer(state, _: PayloadAction) {
17 | state.isNavDrawerOpen = true;
18 | },
19 | closeNavDrawer(state, _: PayloadAction) {
20 | state.isNavDrawerOpen = false;
21 | },
22 | }
23 | })
24 |
25 | export const selectIsNavDrawerOpen = (state: RootState) => state.scaffold.isNavDrawerOpen;
26 |
27 | export const {openNavDrawer, closeNavDrawer} = slice.actions
28 |
29 | export default slice.reducer;
--------------------------------------------------------------------------------
/backend/services/company/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/employee/api/Queries.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.employee.api
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
5 |
6 | class AllEmployeesQuery
7 |
8 | data class EmployeesByCompanyQuery(val companyId: CompanyId)
9 |
10 | data class EmployeesByMultipleCompaniesQuery(val companyIds: Set)
11 |
12 | data class EmployeeQuery(val employeeId: EmployeeId)
13 |
14 | data class EmployeeQueryResult(
15 | val identifier: EmployeeId,
16 | val version: Long,
17 | val companyId: CompanyId,
18 | val userId: UserId,
19 | val userFirstName: String? = null,
20 | val userLastName: String? = null,
21 | val isAdmin: Boolean = false,
22 | val isProjectManager: Boolean = false
23 | )
24 |
--------------------------------------------------------------------------------
/backend/clients/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | maven("https://repo.spring.io/milestone")
5 | maven("https://repo.spring.io/snapshot")
6 | }
7 | includeBuild("../build-logic")
8 |
9 | val springBootVersion: String by settings
10 | val kotlinVersion: String by settings
11 | plugins {
12 | id("org.springframework.boot") version springBootVersion
13 | kotlin("jvm") version kotlinVersion
14 | kotlin("plugin.jpa") version kotlinVersion
15 | kotlin("plugin.spring") version kotlinVersion
16 | }
17 | }
18 |
19 | dependencyResolutionManagement {
20 | repositories {
21 | mavenCentral()
22 | maven ("https://repo.spring.io/milestone")
23 | maven("https://repo.spring.io/snapshot")
24 | }
25 | }
26 |
27 | includeBuild("../platforms")
28 |
29 | rootProject.name = "clients"
30 | include("grpc")
31 | include("rsocket")
32 |
--------------------------------------------------------------------------------
/backend/apis/grpc-lib/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.google.protobuf.gradle.*
2 |
3 | plugins {
4 | id("com.google.protobuf") version "0.8.18"
5 | id("com.novatecgmbh.commons-kotlin")
6 | }
7 |
8 | group = "${group}.apis"
9 |
10 | dependencies {
11 | implementation(platform("io.grpc:grpc-bom:1.43.2"))
12 | implementation("io.grpc:grpc-netty-shaded")
13 | implementation("io.grpc:grpc-protobuf")
14 | implementation("io.grpc:grpc-stub")
15 | implementation("jakarta.annotation:jakarta.annotation-api:1.3.5")
16 | }
17 |
18 | protobuf {
19 | protoc { artifact = "com.google.protobuf:protoc:3.19.2" }
20 | plugins { id("grpc") { artifact = "io.grpc:protoc-gen-grpc-java:1.43.2" } }
21 | generateProtoTasks {
22 | ofSourceSet("main").forEach {
23 | it.plugins {
24 | // Apply the "grpc" plugin whose spec is defined above, without options.
25 | id("grpc")
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/backend/apis/rest/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/task/web/dto/CreateTaskDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.task.web.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
4 | import com.novatecgmbh.eventsourcing.axon.project.task.api.CreateTaskCommand
5 | import com.novatecgmbh.eventsourcing.axon.project.task.api.TaskId
6 | import java.time.LocalDate
7 |
8 | data class CreateTaskDto(
9 | val projectId: ProjectId,
10 | val name: String,
11 | val description: String?,
12 | val startDate: LocalDate,
13 | val endDate: LocalDate
14 | ) {
15 | fun toCommand(identifier: TaskId) =
16 | CreateTaskCommand(
17 | identifier = identifier,
18 | projectId = projectId,
19 | name = name,
20 | description = description,
21 | startDate = startDate,
22 | endDate = endDate)
23 | }
24 |
--------------------------------------------------------------------------------
/backend/services/company/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/employee/command/view/EmployeeUniqueKeyProjector.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.employee.command.view
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.employee.api.EmployeeCreatedEvent
4 | import org.axonframework.config.ProcessingGroup
5 | import org.axonframework.eventhandling.EventHandler
6 | import org.springframework.stereotype.Component
7 |
8 | @Component
9 | @ProcessingGroup("employee-unique-key-projector")
10 | class EmployeeUniqueKeyProjector(private val repository: EmployeeUniqueKeyRepository) {
11 | @EventHandler
12 | fun on(event: EmployeeCreatedEvent) {
13 | repository.save(
14 | EmployeeUniqueKeyProjection(
15 | identifier = event.aggregateIdentifier,
16 | companyId = event.companyId,
17 | userId = event.userId)
18 | )
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/frontend/src/app/store.ts:
--------------------------------------------------------------------------------
1 | import {configureStore, ThunkAction, Action} from '@reduxjs/toolkit';
2 | import {apiSlice} from "../features/api/apiSlice";
3 | import authSlice from "../features/auth/authSlice";
4 | import taskDrawerSlice from "../features/tasks/taskDrawerSlice";
5 | import scaffoldSlice from "../components/scaffold/scaffoldSlice";
6 |
7 | export const store = configureStore({
8 | reducer: {
9 | [apiSlice.reducerPath]: apiSlice.reducer,
10 | auth: authSlice,
11 | taskDrawer: taskDrawerSlice,
12 | scaffold: scaffoldSlice,
13 | },
14 | middleware: getDefaultMiddleware => getDefaultMiddleware().concat(apiSlice.middleware)
15 | });
16 |
17 | export type AppDispatch = typeof store.dispatch;
18 | export type RootState = ReturnType;
19 | export type AppThunk = ThunkAction>;
23 |
--------------------------------------------------------------------------------
/backend/apis/grpc/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.google.protobuf") version "0.8.18"
3 | id("com.novatecgmbh.commons-kotlin")
4 | id("io.spring.dependency-management")
5 | kotlin("plugin.spring")
6 | }
7 |
8 | group = "${group}.apis"
9 |
10 | dependencies {
11 | implementation("com.novatecgmbh.eventsourcing.axon.apis:common")
12 | implementation("com.novatecgmbh.eventsourcing.axon.apis:grpc-lib")
13 |
14 | implementation("io.projectreactor:reactor-core")
15 | implementation("jakarta.annotation:jakarta.annotation-api:1.3.5")
16 |
17 | implementation("net.devh:grpc-spring-boot-starter:2.13.1.RELEASE")
18 | implementation("org.axonframework:axon-spring-boot-starter")
19 | implementation("org.axonframework.extensions.kotlin:axon-kotlin")
20 | implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
21 | implementation("org.springframework.boot:spring-boot-starter-security")
22 | }
23 |
--------------------------------------------------------------------------------
/backend/services/project/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/participant/command/views/ParticipantUniqueKeyRepository.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.participant.command.views
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.project.participant.api.ParticipantId
5 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
6 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
7 | import org.springframework.data.jpa.repository.JpaRepository
8 | import org.springframework.stereotype.Repository
9 |
10 | @Repository
11 | interface ParticipantUniqueKeyRepository :
12 | JpaRepository {
13 | fun existsByProjectIdAndCompanyIdAndUserId(
14 | projectId: ProjectId,
15 | companyId: CompanyId,
16 | userId: UserId
17 | ): Boolean
18 | }
19 |
--------------------------------------------------------------------------------
/backend/apis/graphql/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/company/graphql/CompanyMutationsController.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.company.graphql
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CreateCompanyCommand
5 | import java.util.concurrent.CompletableFuture
6 | import org.axonframework.commandhandling.gateway.CommandGateway
7 | import org.springframework.graphql.data.method.annotation.Argument
8 | import org.springframework.graphql.data.method.annotation.MutationMapping
9 | import org.springframework.stereotype.Controller
10 |
11 | @Controller
12 | class CompanyMutationsController(val commandGateway: CommandGateway) {
13 |
14 | @MutationMapping
15 | fun createCompany(@Argument name: String): CompletableFuture =
16 | commandGateway.send(CreateCompanyCommand(CompanyId(), name))
17 | }
18 |
--------------------------------------------------------------------------------
/backend/clients/grpc/src/main/kotlin/com/novatecgmbh/grpc/client/demo/commands/UserCommands.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.grpc.client.demo.commands
2 |
3 | import com.google.protobuf.Empty
4 | import com.google.protobuf.util.JsonFormat
5 | import com.novatecgmbh.eventsourcing.axon.UserServiceGrpc
6 | import io.grpc.CallCredentials
7 | import net.devh.boot.grpc.client.inject.GrpcClient
8 | import org.springframework.shell.standard.ShellComponent
9 | import org.springframework.shell.standard.ShellMethod
10 |
11 | @ShellComponent
12 | class UserCommands(private val callCredentials: CallCredentials) {
13 |
14 | @GrpcClient("grpcapi") private lateinit var userService: UserServiceGrpc.UserServiceBlockingStub
15 |
16 | @ShellMethod("List all registered users")
17 | fun users(): String {
18 | val users = userService.withCallCredentials(callCredentials).findAll(Empty.newBuilder().build())
19 | return JsonFormat.printer().print(users)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/backend/apis/rsocket/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/task/rsocket/dto/CreateTaskDto.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.task.rsocket.dto
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
4 | import com.novatecgmbh.eventsourcing.axon.project.task.api.CreateTaskCommand
5 | import com.novatecgmbh.eventsourcing.axon.project.task.api.TaskId
6 | import java.time.LocalDate
7 |
8 | data class CreateTaskDto(
9 | val identifier: TaskId = TaskId(),
10 | val projectId: ProjectId,
11 | val name: String,
12 | val description: String?,
13 | val startDate: LocalDate,
14 | val endDate: LocalDate
15 | ) {
16 | fun toCommand() =
17 | CreateTaskCommand(
18 | identifier = identifier,
19 | projectId = projectId,
20 | name = name,
21 | description = description,
22 | startDate = startDate,
23 | endDate = endDate)
24 | }
25 |
--------------------------------------------------------------------------------
/backend/services/project/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/references/ReferenceCheckerService.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.references
2 |
3 | import org.springframework.stereotype.Service
4 |
5 | @Service
6 | class ReferenceCheckerService(private val repository: RootContextIdMappingRepository) {
7 | fun assertProjectExists(projectId: String) {
8 | if (!repository.existsProjectById(projectId)) {
9 | throw IllegalArgumentException("Referenced Project does not exist")
10 | }
11 | }
12 |
13 | fun assertCompanyExists(companyId: String) {
14 | if (!repository.existsCompanyById(companyId)) {
15 | throw IllegalArgumentException("Referenced Company does not exist")
16 | }
17 | }
18 |
19 | fun assertUserExists(userId: String) {
20 | if (!repository.existsUserById(userId)) {
21 | throw IllegalArgumentException("Referenced User does not exist")
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/backend/services/common/application/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.novatecgmbh.commons-kotlin")
3 | id("io.spring.dependency-management")
4 | kotlin("plugin.spring")
5 | kotlin("plugin.jpa")
6 | }
7 |
8 | group = "${group}.common"
9 |
10 | dependencies {
11 | implementation("com.novatecgmbh.eventsourcing.axon.common:api")
12 |
13 | implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
14 | implementation("io.projectreactor:reactor-core")
15 | implementation("org.axonframework:axon-spring-boot-starter")
16 | implementation("org.axonframework.extensions.kotlin:axon-kotlin")
17 | implementation("org.axonframework.extensions.tracing:axon-tracing-spring-boot-starter")
18 | implementation("org.springframework.boot:spring-boot-starter-data-jpa")
19 |
20 | testImplementation("com.h2database:h2")
21 | testImplementation("org.axonframework:axon-test")
22 | testImplementation("org.springframework.boot:spring-boot-starter-test")
23 | }
24 |
--------------------------------------------------------------------------------
/backend/services/user/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/user/user/query/UserProjection.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.user.user.query
2 |
3 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
4 | import com.novatecgmbh.eventsourcing.axon.user.api.UserQueryResult
5 | import javax.persistence.Column
6 | import javax.persistence.EmbeddedId
7 | import javax.persistence.Entity
8 | import javax.persistence.Table
9 |
10 | @Entity
11 | @Table(name = "users")
12 | class UserProjection(
13 | @EmbeddedId var identifier: UserId,
14 | @Column(nullable = false) var externalUserId: String,
15 | @Column(nullable = false) var firstname: String,
16 | @Column(nullable = false) var lastname: String,
17 | @Column(nullable = false) var email: String,
18 | @Column(nullable = false) var telephone: String
19 | ) {
20 | fun toQueryResult() = UserQueryResult(identifier, externalUserId, firstname, lastname, email, telephone)
21 | }
22 |
--------------------------------------------------------------------------------
/backend/services/project/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/participant/command/views/ParticipantUniqueKeyProjector.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.participant.command.views
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.participant.api.ParticipantCreatedEvent
4 | import org.axonframework.config.ProcessingGroup
5 | import org.axonframework.eventhandling.EventHandler
6 | import org.springframework.stereotype.Component
7 |
8 | @Component
9 | @ProcessingGroup("participant-unique-key-projector")
10 | class ParticipantUniqueKeyProjector(private val repository: ParticipantUniqueKeyRepository) {
11 | @EventHandler
12 | fun on(event: ParticipantCreatedEvent) {
13 | repository.save(
14 | ParticipantUniqueKeyProjection(
15 | identifier = event.aggregateIdentifier,
16 | projectId = event.projectId,
17 | companyId = event.companyId,
18 | userId = event.userId))
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/backend/services/company/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/company/query/CompanyQueryHandler.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.company.query
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.AllCompaniesQuery
4 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyQuery
5 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyQueryResult
6 | import java.util.*
7 | import org.axonframework.queryhandling.QueryHandler
8 | import org.springframework.stereotype.Component
9 |
10 | @Component
11 | class CompanyQueryHandler(val repository: CompanyProjectionRepository) {
12 |
13 | @QueryHandler
14 | fun handle(query: CompanyQuery): Optional =
15 | repository.findById(query.companyId).map { it.toQueryResult() }
16 |
17 | @QueryHandler
18 | fun handle(query: AllCompaniesQuery): Iterable =
19 | repository.findAll().map { it.toQueryResult() }
20 | }
21 |
--------------------------------------------------------------------------------
/backend/services/company/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/employee/command/view/EmployeeUniqueKeyProjection.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.employee.command.view
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.company.employee.api.EmployeeId
5 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
6 | import javax.persistence.*
7 |
8 | @Entity
9 | @Table(
10 | name = "employee_unique_key",
11 | uniqueConstraints = [UniqueConstraint(columnNames = ["companyId", "userId"])])
12 | class EmployeeUniqueKeyProjection(
13 | @EmbeddedId var identifier: EmployeeId,
14 | @Embedded
15 | @AttributeOverride(name = "identifier", column = Column(name = "companyId", nullable = false))
16 | var companyId: CompanyId,
17 | @Embedded
18 | @AttributeOverride(name = "identifier", column = Column(name = "userId", nullable = false))
19 | var userId: UserId,
20 | )
21 |
--------------------------------------------------------------------------------
/backend/services/project/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/participant/api/Queries.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.participant.api
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
5 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
6 |
7 | data class ParticipantByProjectQuery(val projectId: ProjectId)
8 |
9 | data class ParticipantByMultipleProjectsQuery(val projectIds: Set)
10 |
11 | data class ParticipantQuery(val participantId: ParticipantId)
12 |
13 | data class ParticipantQueryResult(
14 | val identifier: ParticipantId,
15 | val version: Long,
16 | val projectId: ProjectId,
17 | val companyId: CompanyId,
18 | val companyName: String?,
19 | val userId: UserId,
20 | val userFirstName: String?,
21 | val userLastName: String?,
22 | val userEmail: String?,
23 | val userTelephone: String?,
24 | )
25 |
--------------------------------------------------------------------------------
/backend/services/common/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/application/security/RegisteredUserPrincipal.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.application.security
2 |
3 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
4 | import org.springframework.security.core.GrantedAuthority
5 | import org.springframework.security.core.userdetails.UserDetails
6 |
7 | data class RegisteredUserPrincipal(
8 | val identifier: UserId,
9 | val externalUserId: String,
10 | val firstname: String,
11 | val lastname: String,
12 | ) : UserDetails {
13 | override fun getAuthorities(): MutableCollection = mutableSetOf()
14 |
15 | override fun getPassword(): String? = null
16 |
17 | override fun getUsername(): String = externalUserId
18 |
19 | override fun isAccountNonExpired(): Boolean = true
20 |
21 | override fun isAccountNonLocked(): Boolean = true
22 |
23 | override fun isCredentialsNonExpired(): Boolean = true
24 |
25 | override fun isEnabled(): Boolean = true
26 | }
27 |
--------------------------------------------------------------------------------
/backend/services/user/application/src/main/resources/application.yaml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: user-service
4 | datasource:
5 | url: jdbc:postgresql://localhost:5432/userdb
6 | username: userapp
7 | password: axon
8 | flyway:
9 | locations: classpath:db/migration/postgres
10 | jackson:
11 | serialization:
12 | fail-on-empty-beans: false
13 | jpa:
14 | hibernate:
15 | ddl-auto: validate
16 | properties:
17 | hibernate:
18 | dialect: org.hibernate.dialect.PostgreSQL10Dialect
19 | open-in-view: false
20 | sql:
21 | init:
22 | mode: always
23 | platform: postgres
24 |
25 | logging:
26 | level:
27 | ROOT: WARN
28 | org:
29 | axonframework.eventhandling.pooled.Coordinator: info
30 | flywaydb.core.internal.command: info
31 | springframework.boot: info
32 |
33 | axon:
34 | serializer:
35 | events: jackson
36 | eventhandling:
37 | processors:
38 | user-unique-key-projector:
39 | mode: subscribing
40 |
--------------------------------------------------------------------------------
/backend/services/company/application/src/main/resources/application.yaml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: company-service
4 | datasource:
5 | url: jdbc:postgresql://localhost:5432/companydb
6 | username: companyapp
7 | password: axon
8 | flyway:
9 | locations: classpath:db/migration/postgres
10 | jackson:
11 | serialization:
12 | fail-on-empty-beans: false
13 | jpa:
14 | hibernate:
15 | ddl-auto: validate
16 | properties:
17 | hibernate:
18 | dialect: org.hibernate.dialect.PostgreSQL10Dialect
19 | open-in-view: false
20 | sql:
21 | init:
22 | mode: always
23 | platform: postgres
24 |
25 | logging:
26 | level:
27 | ROOT: warn
28 | org:
29 | axonframework.eventhandling.pooled.Coordinator: info
30 | flywaydb.core.internal.command: info
31 | springframework.boot: info
32 |
33 | axon:
34 | serializer:
35 | events: jackson
36 | eventhandling:
37 | processors:
38 | employee-unique-key-projector:
39 | mode: subscribing
40 |
--------------------------------------------------------------------------------
/frontend/src/app/hooks.ts:
--------------------------------------------------------------------------------
1 | import {TypedUseSelectorHook, useDispatch, useSelector} from 'react-redux';
2 | import type {RootState, AppDispatch} from './store';
3 | import {useEffect, useState} from "react";
4 |
5 | // Use throughout your app instead of plain `useDispatch` and `useSelector`
6 | export const useAppDispatch = () => useDispatch();
7 | export const useAppSelector: TypedUseSelectorHook = useSelector;
8 |
9 | function getWindowDimensions() {
10 | const {innerWidth: width, innerHeight: height} = window;
11 | return {width, height};
12 | }
13 |
14 | export function useWindowDimensions() {
15 | const [windowDimensions, setWindowDimensions] = useState(getWindowDimensions());
16 |
17 | useEffect(() => {
18 | function handleResize() {
19 | setWindowDimensions(getWindowDimensions());
20 | }
21 |
22 | window.addEventListener('resize', handleResize);
23 | return () => window.removeEventListener('resize', handleResize);
24 | }, []);
25 |
26 | return windowDimensions;
27 | }
28 |
--------------------------------------------------------------------------------
/backend/apis/common/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/application/security/CustomUserAuthenticationConverter.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.application.security
2 |
3 | import org.springframework.core.convert.converter.Converter
4 | import org.springframework.security.authentication.AbstractAuthenticationToken
5 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
6 | import org.springframework.security.core.userdetails.UserDetailsService
7 | import org.springframework.security.oauth2.jwt.Jwt
8 |
9 | class CustomUserAuthenticationConverter(val userDetailsService: UserDetailsService) :
10 | Converter {
11 | override fun convert(jwt: Jwt): AbstractAuthenticationToken =
12 | try {
13 | userDetailsService.loadUserByUsername(jwt.claims["sub"] as String)
14 | } catch (ex: Exception) {
15 | UnregisteredUserPrincipal(jwt.claims["sub"] as String)
16 | }.let { UsernamePasswordAuthenticationToken(it, "n/a", it.authorities) }
17 | }
18 |
--------------------------------------------------------------------------------
/backend/clients/rsocket/src/main/kotlin/com/novatecgmbh/rsocket/client/demo/commands/ChatService.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.rsocket.client.demo.commands
2 |
3 | import java.time.Instant
4 | import org.springframework.messaging.rsocket.RSocketRequester
5 | import org.springframework.stereotype.Service
6 | import reactor.core.publisher.Flux
7 |
8 | @Service
9 | class ChatService(private val rsocketRequester: RSocketRequester) {
10 |
11 | fun subscribeMessages(projectIdentifier: String): Flux =
12 | rsocketRequester
13 | .route("projects.{id}.chat", projectIdentifier)
14 | .retrieveFlux(ChatMessage::class.java)
15 |
16 | fun sendMessage(projectIdentifier: String, message: String) {
17 | rsocketRequester
18 | .route("projects.{id}.chat.send", projectIdentifier)
19 | .data(message)
20 | .send()
21 | .block()
22 | }
23 |
24 | data class ChatMessage(
25 | var userIdentifier: String,
26 | var userName: String,
27 | var timestamp: Instant,
28 | var message: String
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/backend/services/common/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/application/security/SecurityContextHelper.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.application.security
2 |
3 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
4 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
5 | import org.springframework.security.core.context.SecurityContextHolder
6 |
7 | object SecurityContextHelper {
8 | fun setAuthentication(userId: String) {
9 | SecurityContextHolder.getContext().authentication =
10 | UsernamePasswordAuthenticationToken(
11 | RegisteredUserPrincipal(UserId(userId), "", "", ""), null)
12 | }
13 |
14 | fun getUser(): UserId? {
15 | val auth =
16 | SecurityContextHolder.getContext().authentication
17 | ?: throw RuntimeException("Authentication from security context holder is null!")
18 | val principal = auth.principal
19 |
20 | return if (principal is RegisteredUserPrincipal) {
21 | principal.identifier
22 | } else null
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/frontend/src/components/PrivateRoute.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {
3 | Redirect,
4 | Route,
5 | RouteProps,
6 | } from 'react-router-dom';
7 | import {useAppSelector} from "../app/hooks";
8 | import {selectIsAuthenticated, selectIsRegistered} from "../features/auth/authSlice";
9 |
10 | export interface CustomRouteProps {
11 | allowUnregistered?: boolean;
12 | }
13 |
14 | function PrivateRoute(props: RouteProps & CustomRouteProps) {
15 | const isAuthenticated = useAppSelector(selectIsAuthenticated);
16 | const isRegistered = useAppSelector(selectIsRegistered);
17 | const allowUnregistered = props.allowUnregistered ?? false;
18 |
19 | const {component: Component, ...rest} = props;
20 | if (!Component) return null;
21 |
22 | return (
23 | (
26 | isAuthenticated && (isRegistered || allowUnregistered) ? :
27 | )}
28 | />
29 | )
30 | }
31 |
32 | export default PrivateRoute
--------------------------------------------------------------------------------
/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | html {
2 | height: 100%;
3 | }
4 |
5 | @keyframes AnimateHighlighting {
6 | 0%{background:#FFFFFF}
7 | 50%{background:#ebe834}
8 | 100%{background:#FFFFFF}
9 | }
10 |
11 | body {
12 | margin: 0;
13 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
14 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
15 | sans-serif;
16 | -webkit-font-smoothing: antialiased;
17 | -moz-osx-font-smoothing: grayscale;
18 | background-color: lightgrey;
19 | background: url(./images/keycloak-bg.png) no-repeat center center fixed;
20 | background-size: cover;
21 | height: 100%;
22 | }
23 |
24 | #root {
25 | height: 100%;
26 | }
27 |
28 | code {
29 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
30 | monospace;
31 | }
32 |
33 | main {
34 | display: flex;
35 | flex-direction: column;
36 | align-items: center;
37 | }
38 |
39 | .row {
40 | display: flex;
41 | flex-direction: row;
42 | flex-wrap: wrap;
43 | gap: 16px;
44 | align-content: flex-start;
45 | }
46 |
--------------------------------------------------------------------------------
/backend/services/project/application/src/main/resources/application.yaml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: project-service
4 | datasource:
5 | url: jdbc:postgresql://localhost:5432/projectdb
6 | username: projectapp
7 | password: axon
8 | flyway:
9 | locations: classpath:db/migration/postgres
10 | jackson:
11 | serialization:
12 | fail-on-empty-beans: false
13 | jpa:
14 | hibernate:
15 | ddl-auto: validate
16 | properties:
17 | hibernate:
18 | dialect: org.hibernate.dialect.PostgreSQL10Dialect
19 | open-in-view: false
20 | sql:
21 | init:
22 | mode: always
23 | platform: postgres
24 |
25 | logging:
26 | level:
27 | ROOT: warn
28 | # io.jaegertracing.internal.reporters.LoggingReporter: info
29 | org:
30 | axonframework.eventhandling.pooled.Coordinator: info
31 | flywaydb.core.internal.command: info
32 | springframework.boot: info
33 |
34 | axon:
35 | serializer:
36 | events: jackson
37 | eventhandling:
38 | processors:
39 | participant-unique-key-projector:
40 | mode: subscribing
41 |
--------------------------------------------------------------------------------
/backend/services/user/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | maven("https://repo.spring.io/milestone")
5 | }
6 | includeBuild("../../build-logic")
7 |
8 | val springBootVersion: String by settings
9 | val kotlinVersion: String by settings
10 | val springDependencyManagementPluginVersion: String by settings
11 | plugins {
12 | id("io.spring.dependency-management") version springDependencyManagementPluginVersion
13 | id("org.springframework.boot") version springBootVersion
14 | kotlin("jvm") version kotlinVersion
15 | kotlin("plugin.jpa") version kotlinVersion
16 | kotlin("plugin.spring") version kotlinVersion
17 | }
18 | }
19 |
20 | dependencyResolutionManagement {
21 | repositories {
22 | mavenCentral()
23 | maven("https://repo.spring.io/milestone")
24 | maven("https://repo.spring.io/snapshot")
25 | }
26 | }
27 |
28 | includeBuild("../../platforms")
29 | includeBuild("../common")
30 |
31 | rootProject.name = "user"
32 | include("api")
33 | include("application")
--------------------------------------------------------------------------------
/frontend/src/components/TableToolbar.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {IconButton, Toolbar, Tooltip, Typography} from "@mui/material";
3 | import AddIcon from "@mui/icons-material/Add";
4 |
5 | interface TableToolbarProps {
6 | title: string;
7 | tooltip: string;
8 | onClick: (event: React.MouseEvent) => void;
9 | }
10 |
11 | export function TableToolbar(props: TableToolbarProps) {
12 | return (
13 |
19 |
25 | {props.title}
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | );
34 | };
--------------------------------------------------------------------------------
/backend/apis/rest/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.novatecgmbh.commons-kotlin")
3 | id("io.spring.dependency-management")
4 | kotlin("plugin.spring")
5 | }
6 |
7 | group = "${group}.apis"
8 |
9 | dependencies {
10 | implementation("com.novatecgmbh.eventsourcing.axon.apis:common")
11 |
12 | implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
13 | implementation("io.opentracing.contrib:opentracing-spring-jaeger-cloud-starter")
14 | implementation("io.projectreactor:reactor-core")
15 | implementation("org.axonframework:axon-spring-boot-starter")
16 | implementation("org.axonframework.extensions.kotlin:axon-kotlin")
17 | implementation("org.axonframework.extensions.tracing:axon-tracing-spring-boot-starter")
18 | implementation("org.springframework.boot:spring-boot-starter-security")
19 | implementation("org.springframework.boot:spring-boot-starter-web")
20 | implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
21 |
22 | testImplementation("org.axonframework:axon-test")
23 | testImplementation("org.springframework.boot:spring-boot-starter-test")
24 | }
25 |
--------------------------------------------------------------------------------
/backend/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "Building Service - User API ..."
4 | ./gradlew :user:api:build
5 | echo "Building Service - User Application ..."
6 | ./gradlew :user:application:build
7 |
8 | echo "Building Service - Company API ..."
9 | ./gradlew :company:api:build
10 | echo "Building Service - Company Application ..."
11 | ./gradlew :company:application:build
12 |
13 | echo "Building Service - Project API ..."
14 | ./gradlew :project:api:build
15 | echo "Building Service - Project Application ..."
16 | ./gradlew :project:application:build
17 |
18 | echo "Building Data Importer ..."
19 | ./gradlew :data-import:initial:build
20 |
21 | echo "Building API Common ..."
22 | ./gradlew :apis:common:build
23 | echo "Building API GraphQL ..."
24 | ./gradlew :apis:graphql:build
25 | echo "Building API gRPC"
26 | ./gradlew :apis:grpc:build
27 | echo "Building API REST ..."
28 | ./gradlew :apis:rest:build
29 | echo "Building API RSocket ..."
30 | ./gradlew :apis:rsocket:build
31 |
32 | echo "Building Demo Client GRPC ..."
33 | ./gradlew :clients:grpc:build
34 | echo "Building Demo Client RSocket ..."
35 | ./gradlew :clients:rsocket:build
36 |
--------------------------------------------------------------------------------
/backend/services/common/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | maven("https://repo.spring.io/milestone")
5 | }
6 | includeBuild("../../build-logic")
7 |
8 | val springBootVersion: String by settings
9 | val kotlinVersion: String by settings
10 | val springDependencyManagementPluginVersion: String by settings
11 | plugins {
12 | id("io.spring.dependency-management") version springDependencyManagementPluginVersion
13 | id("org.springframework.boot") version springBootVersion
14 | kotlin("jvm") version kotlinVersion
15 | kotlin("plugin.jpa") version kotlinVersion
16 | kotlin("plugin.spring") version kotlinVersion
17 | }
18 | }
19 |
20 | dependencyResolutionManagement {
21 | repositories {
22 | mavenCentral()
23 | maven("https://repo.spring.io/milestone")
24 | maven("https://repo.spring.io/snapshot")
25 | }
26 | }
27 |
28 | includeBuild("../../platforms")
29 | includeBuild("../user")
30 |
31 | rootProject.name = "common"
32 | include("api")
33 | include("auditing")
34 | include("application")
35 |
--------------------------------------------------------------------------------
/backend/services/project/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | maven("https://repo.spring.io/milestone")
5 | }
6 | includeBuild("../../build-logic")
7 |
8 | val springBootVersion: String by settings
9 | val kotlinVersion: String by settings
10 | val springDependencyManagementPluginVersion: String by settings
11 | plugins {
12 | id("io.spring.dependency-management") version springDependencyManagementPluginVersion
13 | id("org.springframework.boot") version springBootVersion
14 | kotlin("jvm") version kotlinVersion
15 | kotlin("plugin.jpa") version kotlinVersion
16 | kotlin("plugin.spring") version kotlinVersion
17 | }
18 | }
19 |
20 | dependencyResolutionManagement {
21 | repositories {
22 | mavenCentral()
23 | maven("https://repo.spring.io/milestone")
24 | maven("https://repo.spring.io/snapshot")
25 | }
26 | }
27 |
28 | includeBuild("../../platforms")
29 | includeBuild("../user")
30 | includeBuild("../company")
31 |
32 | rootProject.name = "project"
33 | include("api")
34 | include("application")
35 |
--------------------------------------------------------------------------------
/backend/apis/graphql/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.novatecgmbh.commons-kotlin")
3 | id("io.spring.dependency-management")
4 | kotlin("plugin.spring")
5 | }
6 |
7 | group = "${group}.apis"
8 |
9 | dependencies {
10 | implementation("com.novatecgmbh.eventsourcing.axon.apis:common")
11 |
12 | implementation("io.opentracing.contrib:opentracing-spring-jaeger-cloud-starter")
13 | implementation("io.projectreactor.kotlin:reactor-kotlin-extensions:1.1.5")
14 | implementation("org.axonframework:axon-spring-boot-starter")
15 | implementation("org.axonframework.extensions.kotlin:axon-kotlin")
16 | implementation("org.axonframework.extensions.tracing:axon-tracing-spring-boot-starter")
17 | implementation("org.springframework.boot:spring-boot-starter-graphql")
18 | implementation("org.springframework.boot:spring-boot-starter-security")
19 | implementation("org.springframework.boot:spring-boot-starter-web")
20 | implementation("org.springframework.boot:spring-boot-starter-websocket")
21 | implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
22 |
23 | testImplementation("org.springframework.boot:spring-boot-starter-test")
24 | }
--------------------------------------------------------------------------------
/backend/apis/rsocket/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/application/config/TemporaryWorkaroundConfig.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.application.config
2 |
3 | import java.util.concurrent.CancellationException
4 | import org.slf4j.Logger
5 | import org.slf4j.LoggerFactory
6 | import org.springframework.beans.factory.annotation.Autowired
7 | import org.springframework.context.annotation.Configuration
8 | import reactor.core.publisher.Hooks
9 |
10 | @Configuration
11 | class TemporaryWorkaroundConfig {
12 | // source: https://github.com/rsocket/rsocket-java/issues/1018#issuecomment-954459392
13 | // workaround for: https://github.com/rsocket/rsocket-java/issues/1018
14 | @Autowired
15 | fun configureHooks() {
16 | Hooks.onErrorDropped {
17 | if (it is CancellationException || it.cause is CancellationException) {
18 | LOGGER.trace("Operator called default onErrorDropped", it)
19 | } else {
20 | LOGGER.error("Operator called default onErrorDropped", it)
21 | }
22 | }
23 | }
24 |
25 | companion object {
26 | val LOGGER: Logger = LoggerFactory.getLogger(TemporaryWorkaroundConfig::class.java)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/backend/apis/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | maven("https://repo.spring.io/milestone")
5 | maven("https://repo.spring.io/snapshot")
6 | }
7 | includeBuild("../build-logic")
8 |
9 | val springBootVersion: String by settings
10 | val kotlinVersion: String by settings
11 | val springDependencyManagementPluginVersion: String by settings
12 | plugins {
13 | id("io.spring.dependency-management") version springDependencyManagementPluginVersion
14 | id("org.springframework.boot") version springBootVersion
15 | kotlin("jvm") version kotlinVersion
16 | kotlin("plugin.jpa") version kotlinVersion
17 | kotlin("plugin.spring") version kotlinVersion
18 | }
19 | }
20 |
21 | dependencyResolutionManagement {
22 | repositories {
23 | mavenCentral()
24 | maven ("https://repo.spring.io/milestone")
25 | maven("https://repo.spring.io/snapshot")
26 | }
27 | }
28 |
29 | includeBuild("../platforms")
30 | includeBuild("../services")
31 |
32 | rootProject.name = "apis"
33 | include("common")
34 | include("graphql")
35 | include("grpc")
36 | include("grpc-lib")
37 | include("rest")
38 | include("rsocket")
39 |
--------------------------------------------------------------------------------
/backend/services/company/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | maven("https://repo.spring.io/milestone")
5 | }
6 | includeBuild("../../build-logic")
7 |
8 | val springBootVersion: String by settings
9 | val kotlinVersion: String by settings
10 | val springDependencyManagementPluginVersion: String by settings
11 | plugins {
12 | id("io.spring.dependency-management") version springDependencyManagementPluginVersion
13 | id("org.springframework.boot") version springBootVersion
14 | kotlin("jvm") version kotlinVersion
15 | kotlin("plugin.jpa") version kotlinVersion
16 | kotlin("plugin.spring") version kotlinVersion
17 | }
18 | }
19 |
20 | dependencyResolutionManagement {
21 | repositories {
22 | mavenCentral()
23 | maven("https://repo.spring.io/milestone")
24 | maven("https://repo.spring.io/snapshot")
25 | }
26 | }
27 |
28 | includeBuild("../../platforms")
29 | includeBuild("../common")
30 | includeBuild("../project")
31 | includeBuild("../user")
32 |
33 | rootProject.name = "company"
34 | include("api")
35 | include("application")
36 |
37 |
--------------------------------------------------------------------------------
/backend/apis/rest/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/application/config/CorsConfig.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.application.config
2 |
3 | import java.util.*
4 | import org.springframework.beans.factory.annotation.Value
5 | import org.springframework.context.annotation.Bean
6 | import org.springframework.context.annotation.Configuration
7 | import org.springframework.web.cors.CorsConfiguration
8 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource
9 | import org.springframework.web.filter.CorsFilter
10 |
11 | @Configuration
12 | class CorsConfig {
13 | @Bean
14 | fun corsFilter(@Value("\${app.cors.allowed-origins}") allowedOrigins: List?) =
15 | CorsFilter(
16 | UrlBasedCorsConfigurationSource().apply {
17 | this.registerCorsConfiguration(
18 | "/**",
19 | CorsConfiguration().apply {
20 | this.allowCredentials = true
21 | this.allowedOrigins = allowedOrigins
22 | this.allowedMethods = Collections.singletonList("*")
23 | this.allowedHeaders = Collections.singletonList("*")
24 | })
25 | })
26 | }
27 |
--------------------------------------------------------------------------------
/backend/apis/graphql/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/application/config/CorsConfig.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.application.config
2 |
3 | import java.util.*
4 | import org.springframework.beans.factory.annotation.Value
5 | import org.springframework.context.annotation.Bean
6 | import org.springframework.context.annotation.Configuration
7 | import org.springframework.web.cors.CorsConfiguration
8 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource
9 | import org.springframework.web.filter.CorsFilter
10 |
11 | @Configuration
12 | class CorsConfig {
13 |
14 | @Bean
15 | fun corsFilter(@Value("\${app.cors.allowed-origins}") allowedOrigins: List?) =
16 | CorsFilter(
17 | UrlBasedCorsConfigurationSource().apply {
18 | this.registerCorsConfiguration(
19 | "/**",
20 | CorsConfiguration().apply {
21 | this.allowCredentials = true
22 | this.allowedOrigins = allowedOrigins
23 | this.allowedMethods = Collections.singletonList("*")
24 | this.allowedHeaders = Collections.singletonList("*")
25 | })
26 | })
27 | }
28 |
--------------------------------------------------------------------------------
/backend/services/project/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/task/api/Queries.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.task.api
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.participant.api.ParticipantId
4 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
5 | import java.time.LocalDate
6 |
7 | data class TasksByProjectQuery(val projectId: ProjectId)
8 |
9 | data class TasksByMultipleProjectsQuery(
10 | val projectIds: Set,
11 | val from: LocalDate? = null,
12 | val to: LocalDate? = null
13 | )
14 |
15 | data class TaskQuery(val taskId: TaskId)
16 |
17 | data class TaskQueryResult(
18 | val identifier: TaskId,
19 | val projectId: ProjectId,
20 | val name: String,
21 | val description: String?,
22 | val startDate: LocalDate,
23 | val endDate: LocalDate,
24 | val status: TaskStatusEnum,
25 | val participantId: ParticipantId?,
26 | val assigneeFirstName: String?,
27 | val assigneeLastName: String?,
28 | val assigneeCompanyName: String?,
29 | val todos: List,
30 | )
31 |
32 | data class TodoQueryResult(val todoId: TodoId, val description: String, var isDone: Boolean)
33 |
--------------------------------------------------------------------------------
/backend/services/company/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/employee/query/EmployeeQueryHandler.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.employee.query
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.employee.api.*
4 | import java.util.*
5 | import org.axonframework.queryhandling.QueryHandler
6 | import org.springframework.stereotype.Component
7 |
8 | @Component
9 | class EmployeeQueryHandler(val repository: EmployeeProjectionRepository) {
10 |
11 | @QueryHandler
12 | fun handle(query: EmployeeQuery): Optional =
13 | repository.findById(query.employeeId).map { it.toQueryResult() }
14 |
15 | @QueryHandler
16 | fun handle(query: EmployeesByCompanyQuery): Iterable =
17 | repository.findAllByCompanyId(query.companyId).map { it.toQueryResult() }
18 |
19 | @QueryHandler
20 | fun handle(query: EmployeesByMultipleCompaniesQuery): Iterable =
21 | repository.findAllByCompanyIdIn(query.companyIds).map { it.toQueryResult() }
22 |
23 | @QueryHandler
24 | fun handle(query: AllEmployeesQuery): Iterable =
25 | repository.findAll().map { it.toQueryResult() }
26 | }
27 |
--------------------------------------------------------------------------------
/backend/services/user/application/src/main/resources/db/migration/postgres/V1__AXON_TABLES.sql:
--------------------------------------------------------------------------------
1 | create sequence hibernate_sequence start 1 increment 1;
2 |
3 | create table association_value_entry
4 | (
5 | id int8 not null,
6 | association_key varchar(255) not null,
7 | association_value varchar(255),
8 | saga_id varchar(255) not null,
9 | saga_type varchar(255),
10 | primary key (id)
11 | );
12 |
13 | create table saga_entry
14 | (
15 | saga_id varchar(255) not null,
16 | revision varchar(255),
17 | saga_type varchar(255),
18 | serialized_saga oid,
19 | primary key (saga_id)
20 | );
21 |
22 | create table token_entry
23 | (
24 | processor_name varchar(255) not null,
25 | segment int4 not null,
26 | owner varchar(255),
27 | timestamp varchar(255) not null,
28 | token oid,
29 | token_type varchar(255),
30 | primary key (processor_name, segment)
31 | );
32 | create index IDXk45eqnxkgd8hpdn6xixn8sgft on association_value_entry (saga_type, association_key, association_value);
33 | create index IDXgv5k1v2mh6frxuy5c0hgbau94 on association_value_entry (saga_id, saga_type);
34 |
--------------------------------------------------------------------------------
/backend/services/company/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/employee/api/Events.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.employee.api
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
5 |
6 | abstract class EmployeeEvent(open val aggregateIdentifier: EmployeeId)
7 |
8 | data class EmployeeCreatedEvent(
9 | override val aggregateIdentifier: EmployeeId,
10 | val companyId: CompanyId,
11 | val userId: UserId,
12 | ) : EmployeeEvent(aggregateIdentifier)
13 |
14 | data class AdminPermissionGrantedForEmployeeEvent(
15 | override val aggregateIdentifier: EmployeeId,
16 | ) : EmployeeEvent(aggregateIdentifier)
17 |
18 | data class AdminPermissionRemovedFromEmployeeEvent(
19 | override val aggregateIdentifier: EmployeeId,
20 | ) : EmployeeEvent(aggregateIdentifier)
21 |
22 | data class ProjectManagerPermissionGrantedForEmployeeEvent(
23 | override val aggregateIdentifier: EmployeeId,
24 | ) : EmployeeEvent(aggregateIdentifier)
25 |
26 | data class ProjectManagerPermissionRemovedFromEmployeeEvent(
27 | override val aggregateIdentifier: EmployeeId,
28 | ) : EmployeeEvent(aggregateIdentifier)
29 |
--------------------------------------------------------------------------------
/backend/services/company/application/src/main/resources/db/migration/postgres/V1__AXON_TABLES.sql:
--------------------------------------------------------------------------------
1 | create sequence hibernate_sequence start 1 increment 1;
2 |
3 | create table association_value_entry
4 | (
5 | id int8 not null,
6 | association_key varchar(255) not null,
7 | association_value varchar(255),
8 | saga_id varchar(255) not null,
9 | saga_type varchar(255),
10 | primary key (id)
11 | );
12 |
13 | create table saga_entry
14 | (
15 | saga_id varchar(255) not null,
16 | revision varchar(255),
17 | saga_type varchar(255),
18 | serialized_saga oid,
19 | primary key (saga_id)
20 | );
21 |
22 | create table token_entry
23 | (
24 | processor_name varchar(255) not null,
25 | segment int4 not null,
26 | owner varchar(255),
27 | timestamp varchar(255) not null,
28 | token oid,
29 | token_type varchar(255),
30 | primary key (processor_name, segment)
31 | );
32 | create index IDXk45eqnxkgd8hpdn6xixn8sgft on association_value_entry (saga_type, association_key, association_value);
33 | create index IDXgv5k1v2mh6frxuy5c0hgbau94 on association_value_entry (saga_id, saga_type);
34 |
--------------------------------------------------------------------------------
/backend/services/project/application/src/main/resources/db/migration/postgres/V1__AXON_TABLES.sql:
--------------------------------------------------------------------------------
1 | create sequence hibernate_sequence start 1 increment 1;
2 |
3 | create table association_value_entry
4 | (
5 | id int8 not null,
6 | association_key varchar(255) not null,
7 | association_value varchar(255),
8 | saga_id varchar(255) not null,
9 | saga_type varchar(255),
10 | primary key (id)
11 | );
12 |
13 | create table saga_entry
14 | (
15 | saga_id varchar(255) not null,
16 | revision varchar(255),
17 | saga_type varchar(255),
18 | serialized_saga oid,
19 | primary key (saga_id)
20 | );
21 |
22 | create table token_entry
23 | (
24 | processor_name varchar(255) not null,
25 | segment int4 not null,
26 | owner varchar(255),
27 | timestamp varchar(255) not null,
28 | token oid,
29 | token_type varchar(255),
30 | primary key (processor_name, segment)
31 | );
32 | create index IDXk45eqnxkgd8hpdn6xixn8sgft on association_value_entry (saga_type, association_key, association_value);
33 | create index IDXgv5k1v2mh6frxuy5c0hgbau94 on association_value_entry (saga_id, saga_type);
34 |
--------------------------------------------------------------------------------
/backend/apis/grpc/src/main/resources/certs/server.csr:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE REQUEST-----
2 | MIIDDjCCAfYCAQAwcDELMAkGA1UEBhMCREUxEjAQBgNVBAgMCVRodXJpbmdpYTEP
3 | MA0GA1UEBwwGRXJmdXJ0MRMwEQYDVQQKDApBbGljZSBDb3JwMREwDwYDVQQLDAhU
4 | ZWFtIEZvbzEUMBIGA1UEAwwLZ3JwYy1zZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUA
5 | A4IBDwAwggEKAoIBAQCtQiC9MUZ6GQVhJ1x6YpGxA9rCem7eQt31MMCjlIdul0vF
6 | prkmyH7sSFmeMylychtE1RrZEn9xJaO7CattU4boARHqn5wayNAeUwZ0h+2kAPZ1
7 | kavAkgv9wYW3/Qk1cCL7fPcvLcjWV44imZbw1yWnBxNTIh1uW0GW6GxonuHXTDxQ
8 | lqVKa3I0SsflM5zDUhtGA7ALMGov23FxmWmm1TmWNB4DQaDbWC+xp6K2ox4874Zh
9 | iaERVjdbxQS+KSGQel5lWd0QETI/3QkV3jznn8B16Wiza3ejc6aKx6lETKPY2gJq
10 | UHwR6SXp1Lgty6ng87V0WFT0+n2XRoh4Gn1LXeWlAgMBAAGgWTBXBgkqhkiG9w0B
11 | CQ4xSjBIMA4GA1UdDwEB/wQEAwIBtjATBgNVHSUEDDAKBggrBgEFBQcDATAhBgNV
12 | HREEGjAYggtncnBjLXNlcnZlcoIJbG9jYWxob3N0MA0GCSqGSIb3DQEBCwUAA4IB
13 | AQBHcMK0xfdMqLBDSnFcykxNsqez22WwWxjLARuzrFiSUo53MOIU7rZWnt8vtNmJ
14 | ouzfT2bNDAT3DucWlAIbCR+Xyz7u8f1k8aEkgmwmx7mFNOyaEwmbOQFRb96p6ria
15 | Hb85qO3VJWhfEjXqQby3xixuB4MAH2igbK1VP5VoNgZB4SUP4MZc/deVtxCwNzro
16 | TRdTCG4EZxkBOVoLISJoif4TLMEpkQfxLgnnDZRwuQRvv4rWujCPtmsXSEQK6R4V
17 | 7svwNfM5f7s2hPGHpS3VXdnoCND3/NeYEVMCZqOZ398lQIGYLrFHwbJZgKY9R/Jz
18 | hpGovJ3f+cKybsYYQev++M6U
19 | -----END CERTIFICATE REQUEST-----
20 |
--------------------------------------------------------------------------------
/backend/data-import/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal() // if pluginManagement.repositories looks like this, it can be omitted as this is the default
4 | maven("https://repo.spring.io/milestone")
5 | maven("https://repo.spring.io/snapshot")
6 | }
7 | includeBuild("../build-logic")
8 |
9 | val springBootVersion: String by settings
10 | val kotlinVersion: String by settings
11 | val springDependencyManagementPluginVersion: String by settings
12 | plugins {
13 | id("io.spring.dependency-management") version springDependencyManagementPluginVersion
14 | id("org.springframework.boot") version springBootVersion
15 | kotlin("jvm") version kotlinVersion
16 | kotlin("plugin.jpa") version kotlinVersion
17 | kotlin("plugin.spring") version kotlinVersion
18 | }
19 | }
20 |
21 | dependencyResolutionManagement {
22 | repositories {
23 | mavenCentral()
24 | maven("https://repo.spring.io/milestone")
25 | maven("https://repo.spring.io/snapshot")
26 | }
27 | }
28 |
29 | includeBuild("../platforms")
30 | includeBuild("../services")
31 |
32 | rootProject.name = "data-import"
33 | include("initial")
34 |
--------------------------------------------------------------------------------
/backend/services/common/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/common/references/RootContextIdMapping.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.common.references
2 |
3 | import java.io.Serializable
4 | import javax.persistence.*
5 |
6 | @Entity
7 | @Table(name = "root_context_id_mapping")
8 | class RootContextIdMapping(@EmbeddedId val key: RootContextIdMappingKey)
9 |
10 | @Embeddable
11 | class RootContextIdMappingKey(
12 | var aggregateType: String,
13 | var aggregateIdentifier: String,
14 | var rootContextId: String
15 | ) : Serializable {
16 | override fun equals(other: Any?): Boolean {
17 | if (this === other) return true
18 | if (javaClass != other?.javaClass) return false
19 |
20 | other as RootContextIdMappingKey
21 |
22 | if (aggregateType != other.aggregateType) return false
23 | if (aggregateIdentifier != other.aggregateIdentifier) return false
24 | if (rootContextId != other.rootContextId) return false
25 |
26 | return true
27 | }
28 |
29 | override fun hashCode(): Int {
30 | var result = aggregateType.hashCode()
31 | result = 31 * result + aggregateIdentifier.hashCode()
32 | result = 31 * result + rootContextId.hashCode()
33 | return result
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/backend/data-import/initial/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.novatecgmbh.commons-kotlin")
3 | id("io.spring.dependency-management")
4 | kotlin("plugin.spring")
5 | }
6 |
7 | group = "${group}.initial-data-import"
8 |
9 | dependencies {
10 | implementation("com.novatecgmbh.eventsourcing.axon.common:auditing")
11 | implementation("com.novatecgmbh.eventsourcing.axon.common:api")
12 | implementation("com.novatecgmbh.eventsourcing.axon.company:api")
13 | implementation("com.novatecgmbh.eventsourcing.axon.project:api")
14 | implementation("com.novatecgmbh.eventsourcing.axon.user:api")
15 |
16 | implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
17 | implementation("io.opentracing.contrib:opentracing-spring-jaeger-cloud-starter")
18 | implementation("io.projectreactor:reactor-core")
19 | implementation("org.axonframework:axon-spring-boot-starter")
20 | implementation("org.axonframework.extensions.kotlin:axon-kotlin")
21 | implementation("org.axonframework.extensions.tracing:axon-tracing-spring-boot-starter")
22 | implementation("org.springframework.boot:spring-boot-starter")
23 |
24 | testImplementation("org.axonframework:axon-test")
25 | testImplementation("org.springframework.boot:spring-boot-starter-test")
26 | }
27 |
--------------------------------------------------------------------------------
/backend/services/project/api/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/project/api/Queries.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.project.api
2 |
3 | import com.novatecgmbh.eventsourcing.axon.common.api.AggregateReference
4 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
5 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
6 | import java.time.LocalDate
7 |
8 | class MyProjectsQuery(val userId: UserId)
9 |
10 | data class ProjectQuery(val projectId: ProjectId)
11 |
12 | data class ProjectQueryResult(
13 | val identifier: ProjectId,
14 | val version: Long,
15 | val name: String,
16 | val startDate: LocalDate,
17 | val deadline: LocalDate,
18 | val companyReference: AggregateReference,
19 | val status: ProjectStatus,
20 | var actualEndDate: LocalDate? = null
21 | )
22 |
23 | data class ProjectDetailsQuery(val projectId: ProjectId)
24 |
25 | data class ProjectDetailsQueryResult(
26 | val identifier: ProjectId,
27 | val version: Long,
28 | val name: String,
29 | val startDate: LocalDate,
30 | val deadline: LocalDate,
31 | val allTasksCount: Long,
32 | val plannedTasksCount: Long,
33 | val startedTasksCount: Long,
34 | val completedTasksCount: Long,
35 | )
36 |
--------------------------------------------------------------------------------
/backend/apis/common/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/application/security/CustomUserDetailsService.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.application.security
2 |
3 | import com.novatecgmbh.eventsourcing.axon.user.api.FindUserByExternalUserIdQuery
4 | import com.novatecgmbh.eventsourcing.axon.user.api.UserQueryResult
5 | import kotlin.jvm.Throws
6 | import org.axonframework.extensions.kotlin.queryOptional
7 | import org.axonframework.queryhandling.QueryGateway
8 | import org.springframework.security.core.userdetails.UserDetails
9 | import org.springframework.security.core.userdetails.UserDetailsService
10 | import org.springframework.stereotype.Service
11 |
12 | @Service
13 | class CustomUserDetailsService(val queryGateway: QueryGateway) : UserDetailsService {
14 | @Throws(Exception::class)
15 | override fun loadUserByUsername(username: String): UserDetails {
16 | SecurityContextHelper.setAuthentication("system") // TODO ?
17 | return queryGateway
18 | .queryOptional(
19 | FindUserByExternalUserIdQuery(username))
20 | .join()
21 | .get()
22 | .toRegisteredUser()
23 | }
24 | }
25 |
26 | fun UserQueryResult.toRegisteredUser() =
27 | RegisteredUserPrincipal(identifier, externalUserId, firstname, lastname)
28 |
--------------------------------------------------------------------------------
/backend/build-logic/commons-kotlin/src/main/kotlin/com.novatecgmbh.commons-kotlin.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("java-library")
3 | kotlin("jvm")
4 | }
5 |
6 | group = "com.novatecgmbh.eventsourcing.axon"
7 |
8 | java {
9 | sourceCompatibility = JavaVersion.VERSION_17
10 | targetCompatibility = JavaVersion.VERSION_17
11 | }
12 |
13 | dependencies {
14 | implementation(platform("com.novatecgmbh.platform:product-platform"))
15 | testImplementation(platform("com.novatecgmbh.platform:test-platform"))
16 |
17 | implementation(kotlin("reflect"))
18 | implementation(kotlin("stdlib-jdk8"))
19 |
20 | testImplementation("com.tngtech.archunit:archunit-junit5")
21 | testImplementation("io.mockk:mockk")
22 | testImplementation("org.apache.commons:commons-lang3")
23 | testImplementation("org.junit.jupiter:junit-jupiter-api")
24 |
25 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
26 | }
27 |
28 | tasks.withType { useJUnitPlatform() }
29 |
30 | tasks.withType().configureEach {
31 | kotlinOptions {
32 | freeCompilerArgs = listOf("-Xjsr305=strict")
33 | jvmTarget = "17"
34 | }
35 | }
36 |
37 | tasks.withType { duplicatesStrategy = DuplicatesStrategy.WARN }
38 |
39 | tasks.withType { duplicatesStrategy = DuplicatesStrategy.WARN }
40 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/backend/services/project/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/participant/command/views/ParticipantUniqueKeyProjection.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.participant.command.views
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.project.participant.api.ParticipantId
5 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
6 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
7 | import javax.persistence.*
8 |
9 | @Entity
10 | @Table(
11 | name = "participant_unique_key",
12 | uniqueConstraints =
13 | [
14 | UniqueConstraint(
15 | name = "participant_unique_key_constraint",
16 | columnNames = ["projectId", "companyId", "userId"])])
17 | class ParticipantUniqueKeyProjection(
18 | @EmbeddedId var identifier: ParticipantId,
19 | @Embedded
20 | @AttributeOverride(name = "identifier", column = Column(name = "projectId", nullable = false))
21 | var projectId: ProjectId,
22 | @Embedded
23 | @AttributeOverride(name = "identifier", column = Column(name = "companyId", nullable = false))
24 | var companyId: CompanyId,
25 | @Embedded
26 | @AttributeOverride(name = "identifier", column = Column(name = "userId", nullable = false))
27 | var userId: UserId,
28 | )
29 |
--------------------------------------------------------------------------------
/backend/services/company/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/company/references/RootContextIdMappingRepository.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.company.references
2 |
3 | import com.novatecgmbh.eventsourcing.axon.common.references.RootContextIdMapping
4 | import com.novatecgmbh.eventsourcing.axon.common.references.RootContextIdMappingKey
5 | import org.springframework.data.jpa.repository.JpaRepository
6 | import org.springframework.data.jpa.repository.Query
7 | import org.springframework.data.repository.query.Param
8 | import org.springframework.stereotype.Repository
9 |
10 | @Repository
11 | interface RootContextIdMappingRepository :
12 | JpaRepository {
13 |
14 | @Query(
15 | "select case when (count(r) > 0) then true else false end from RootContextIdMapping r " +
16 | "where r.key.aggregateType " +
17 | "= 'COMPANY' " +
18 | "and r.key.aggregateIdentifier = :companyId ")
19 | fun existsCompanyById(@Param("companyId") companyId: String): Boolean
20 |
21 | @Query(
22 | "select case when (count(r) > 0) then true else false end from RootContextIdMapping r " +
23 | "where r.key.aggregateType " +
24 | "= 'USER' " +
25 | "and r.key.aggregateIdentifier = :userId ")
26 | fun existsUserById(@Param("userId") userId: String): Boolean
27 | }
28 |
--------------------------------------------------------------------------------
/backend/services/project/application/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/authorization/ProjectAuthorizationService.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.authorization
2 |
3 | import com.novatecgmbh.eventsourcing.axon.project.authorization.acl.ProjectAclRepository
4 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
5 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
6 | import org.springframework.stereotype.Service
7 |
8 | @Service
9 | class ProjectAuthorizationService(private val aclRepository: ProjectAclRepository) {
10 |
11 | fun runWhenAuthorizedForProject(userId: UserId, projectId: ProjectId, callable: () -> T): T =
12 | if (aclRepository.hasAccessToProject(userId, projectId.toString())) {
13 | callable.invoke()
14 | } else {
15 | throw IllegalAccessException("User has no access to this project")
16 | }
17 |
18 | fun runWhenAuthorizedForAllProjects(
19 | userId: UserId,
20 | projectIds: Set,
21 | callable: () -> T
22 | ): T =
23 | if (aclRepository.filterProjectsWithAccess(
24 | userId, projectIds.map(ProjectId::toString).toSet())
25 | .size == projectIds.size) {
26 | callable.invoke()
27 | } else {
28 | throw IllegalAccessException("User does not have access to all requested projects")
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/backend/apis/graphql/src/main/kotlin/com/novatecgmbh/eventsourcing/axon/project/participant/graphql/ParticipantMutationsController.kt:
--------------------------------------------------------------------------------
1 | package com.novatecgmbh.eventsourcing.axon.project.participant.graphql
2 |
3 | import com.novatecgmbh.eventsourcing.axon.company.company.api.CompanyId
4 | import com.novatecgmbh.eventsourcing.axon.project.participant.api.CreateParticipantCommand
5 | import com.novatecgmbh.eventsourcing.axon.project.participant.api.ParticipantId
6 | import com.novatecgmbh.eventsourcing.axon.project.project.api.ProjectId
7 | import com.novatecgmbh.eventsourcing.axon.user.api.UserId
8 | import java.util.concurrent.CompletableFuture
9 | import org.axonframework.commandhandling.gateway.CommandGateway
10 | import org.springframework.graphql.data.method.annotation.Argument
11 | import org.springframework.graphql.data.method.annotation.MutationMapping
12 | import org.springframework.stereotype.Controller
13 |
14 | @Controller
15 | class ParticipantMutationsController(val commandGateway: CommandGateway) {
16 |
17 | @MutationMapping
18 | fun createParticipant(
19 | @Argument projectIdentifier: ProjectId,
20 | @Argument companyIdentifier: CompanyId,
21 | @Argument userIdentifier: UserId
22 | ): CompletableFuture =
23 | commandGateway.send(
24 | CreateParticipantCommand(
25 | ParticipantId(), projectIdentifier, companyIdentifier, userIdentifier))
26 | }
27 |
--------------------------------------------------------------------------------