├── .circleci
└── config.yml
├── .gitignore
├── .scalafmt.conf
├── README.md
├── api-client
└── src
│ ├── main
│ └── scala
│ │ └── com
│ │ └── github
│ │ └── j5ik2o
│ │ └── threadWeaver
│ │ └── api
│ │ ├── APIException.scala
│ │ ├── ClientSettings.scala
│ │ ├── HttpClient.scala
│ │ ├── ThreadWeaverClient.scala
│ │ ├── ThreadWeaverCommandClient.scala
│ │ └── ThreadWeaverQueryClient.scala
│ └── test
│ └── scala
│ └── com
│ └── github
│ └── j5ik2o
│ └── threadWeaver
│ └── api
│ └── ThreadWeaverCommandClientSpec.scala
├── api-server
└── src
│ └── main
│ ├── resources
│ ├── application.conf
│ ├── common
│ │ ├── akka.conf
│ │ ├── k8s_probe.conf
│ │ ├── kamon.conf
│ │ └── thread-weaver.conf
│ ├── local-cluster.conf
│ ├── local-cluster
│ │ └── akka.conf
│ ├── local-machine
│ │ └── akka.conf
│ ├── logback.xml
│ ├── production.conf
│ └── production
│ │ └── akka.conf
│ └── scala
│ └── com
│ └── github
│ └── j5ik2o
│ └── threadWeaver
│ └── api
│ ├── ClusterWatcher.scala
│ ├── HealthCheck.scala
│ └── Main.scala
├── build.sbt
├── charts
├── dynamodb
│ ├── Chart.yaml
│ ├── environments
│ │ ├── local-values.yaml
│ │ └── prod-values.yaml
│ ├── templates
│ │ ├── _helpers.tpl
│ │ ├── pod.yaml
│ │ └── service.yaml
│ └── values.yaml
├── mysql
│ ├── Chart.yaml
│ ├── environments
│ │ ├── local-values.yaml
│ │ └── prod-values.yaml
│ ├── templates
│ │ ├── _helpers.tpl
│ │ ├── pod.yaml
│ │ └── service.yaml
│ └── values.yaml
├── thread-weaver-api-server
│ ├── .helmignore
│ ├── Chart.yaml
│ ├── environments
│ │ ├── .gitignore
│ │ ├── local-values.yaml
│ │ └── prod-values.yaml
│ ├── templates
│ │ ├── _helpers.tpl
│ │ ├── deployment.yml
│ │ ├── rbac.yml
│ │ └── service.yml
│ └── values.yaml
└── thread-weaver-flyway
│ ├── .helmignore
│ ├── Chart.yaml
│ ├── environments
│ ├── .gitignore
│ ├── local-values.yaml
│ └── prod-values.yaml.tpl
│ ├── templates
│ ├── _helpers.tpl
│ ├── job.yaml
│ └── secrets.yaml
│ └── values.yaml
├── contracts
├── contract-grpc-proto-interface
│ └── src
│ │ └── main
│ │ └── protobuf
│ │ └── thread
│ │ ├── command.proto
│ │ ├── model.proto
│ │ └── query.proto
├── contract-http-proto-interface
│ └── src
│ │ └── main
│ │ └── scala
│ │ └── com
│ │ └── github
│ │ └── j5ik2o
│ │ └── threadWeaver
│ │ └── adaptor
│ │ └── http
│ │ └── json
│ │ ├── AddMessagesRequestJson.scala
│ │ ├── AddMessagesResponseJson.scala
│ │ ├── CreateThreadRequestJson.scala
│ │ ├── CreateThreadResponseJson.scala
│ │ ├── DestroyThreadRequestJson.scala
│ │ ├── DestroyThreadResponseJson.scala
│ │ ├── ErrorsResponseJson.scala
│ │ ├── GetThreadAdministratorIdsResponseJson.scala
│ │ ├── GetThreadMemberIdsResponseJson.scala
│ │ ├── GetThreadMessagesResponseJson.scala
│ │ ├── GetThreadResponseJson.scala
│ │ ├── GetThreadsResponseJson.scala
│ │ ├── JoinAdministratorIdsRequestJson.scala
│ │ ├── JoinAdministratorIdsResponseJson.scala
│ │ ├── JoinMemberIdsRequestJson.scala
│ │ ├── JoinMemberIdsResponseJson.scala
│ │ ├── LeaveAdministratorIdsRequestJson.scala
│ │ ├── LeaveAdministratorIdsResponseJson.scala
│ │ ├── LeaveMemberIdsRequestJson.scala
│ │ ├── LeaveMemberIdsResponseJson.scala
│ │ ├── RemoveMessagesRequestJson.scala
│ │ ├── RemoveMessagesResponseJson.scala
│ │ ├── ResponseJson.scala
│ │ ├── TextMessage.scala
│ │ ├── ThreadJson.scala
│ │ └── ThreadMessageJson.scala
├── contract-interface
│ └── src
│ │ └── main
│ │ └── scala
│ │ └── com
│ │ └── github
│ │ └── j5ik2o
│ │ └── threadWeaver
│ │ └── adaptor
│ │ ├── aggregates
│ │ ├── BaseCommandRequest.scala
│ │ ├── ThreadCommonProtocol.scala
│ │ ├── typed
│ │ │ └── ThreadProtocol.scala
│ │ └── untyped
│ │ │ └── ThreadProtocol.scala
│ │ ├── dao
│ │ ├── ThreadAdministratorIdsRecord.scala
│ │ ├── ThreadMemberIdsRecord.scala
│ │ ├── ThreadMessageRecord.scala
│ │ └── ThreadRecord.scala
│ │ ├── grpc
│ │ └── presenter
│ │ │ ├── AddMessagesPresenter.scala
│ │ │ ├── CreateThreadPresenter.scala
│ │ │ ├── DestroyThreadPresenter.scala
│ │ │ ├── JoinAdministratorIdsPresenter.scala
│ │ │ ├── JoinMemberIdsPresenter.scala
│ │ │ ├── LeaveAdministratorIdsPresenter.scala
│ │ │ ├── LeaveMemberIdsPresenter.scala
│ │ │ └── RemoveMessagesPresenter.scala
│ │ ├── http
│ │ ├── controller
│ │ │ ├── ThreadCommandController.scala
│ │ │ └── ThreadQueryController.scala
│ │ ├── presenter
│ │ │ ├── AddMessagesPresenter.scala
│ │ │ ├── CreateThreadPresenter.scala
│ │ │ ├── DestroyThreadPresenter.scala
│ │ │ ├── JoinAdministratorIdsPresenter.scala
│ │ │ ├── JoinMemberIdsPresenter.scala
│ │ │ ├── LeaveAdministratorIdsPresenter.scala
│ │ │ ├── LeaveMemberIdsPresenter.scala
│ │ │ ├── RemoveMessagesPresenter.scala
│ │ │ ├── ThreadMessagePresenter.scala
│ │ │ └── ThreadPresenter.scala
│ │ └── routes
│ │ │ └── RouteNames.scala
│ │ ├── presenter
│ │ └── Presenter.scala
│ │ └── readModelUpdater
│ │ ├── ThreadReadModelUpdaterProtocol.scala
│ │ ├── ThreadReadModelUpdaterSettings.scala
│ │ └── ThreadTag.scala
└── contract-use-case
│ └── src
│ └── main
│ └── scala
│ └── com
│ └── github
│ └── j5ik2o
│ └── threadWeaver
│ └── useCase
│ ├── AddMessagesUseCase.scala
│ ├── CreateThreadUseCase.scala
│ ├── DestroyThreadUseCase.scala
│ ├── JoinAdministratorIdsUseCase.scala
│ ├── JoinMemberIdsUseCase.scala
│ ├── LeaveAdministratorIdsUseCase.scala
│ ├── LeaveMemberIdsUseCase.scala
│ ├── RemoveMessagesUseCase.scala
│ ├── ThreadWeaverProtocol.scala
│ └── ThreadWeaverUseCase.scala
├── modules
├── domain
│ └── src
│ │ ├── main
│ │ └── scala
│ │ │ └── com
│ │ │ └── github
│ │ │ └── j5ik2o
│ │ │ └── threadWeaver
│ │ │ └── domain
│ │ │ └── model
│ │ │ ├── accounts
│ │ │ ├── Account.scala
│ │ │ ├── AccountId.scala
│ │ │ └── AccountName.scala
│ │ │ └── threads
│ │ │ ├── AdministratorIds.scala
│ │ │ ├── MemberIds.scala
│ │ │ ├── Message.scala
│ │ │ ├── MessageId.scala
│ │ │ ├── MessageIds.scala
│ │ │ ├── Messages.scala
│ │ │ ├── Text.scala
│ │ │ ├── Thread.scala
│ │ │ ├── ThreadId.scala
│ │ │ ├── ThreadRemarks.scala
│ │ │ ├── ThreadTitle.scala
│ │ │ └── ToAccountIds.scala
│ │ └── test
│ │ └── scala
│ │ └── com
│ │ └── github
│ │ └── j5ik2o
│ │ └── threadWeave
│ │ └── domain
│ │ └── model
│ │ └── accounts
│ │ └── AccountNameSpec.scala
├── infrastructure
│ └── src
│ │ └── main
│ │ └── scala
│ │ └── com
│ │ └── github
│ │ └── j5ik2o
│ │ └── threadWeaver
│ │ └── infrastructure
│ │ └── ulid
│ │ └── ULID.scala
├── interface
│ ├── native-libs
│ │ ├── libsqlite4java-linux-amd64-1.0.392.so
│ │ ├── libsqlite4java-linux-i386-1.0.392.so
│ │ ├── libsqlite4java-osx-1.0.392.dylib
│ │ ├── sqlite4java-win32-x64-1.0.392.dll
│ │ └── sqlite4java-win32-x86-1.0.392.dll
│ ├── src
│ │ ├── main
│ │ │ ├── resources
│ │ │ │ ├── reference.conf
│ │ │ │ └── swagger
│ │ │ │ │ ├── favicon-16x16.png
│ │ │ │ │ ├── favicon-32x32.png
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── oauth2-redirect.html
│ │ │ │ │ ├── swagger-ui-bundle.js
│ │ │ │ │ ├── swagger-ui-bundle.js.map
│ │ │ │ │ ├── swagger-ui-standalone-preset.js
│ │ │ │ │ ├── swagger-ui-standalone-preset.js.map
│ │ │ │ │ ├── swagger-ui.css
│ │ │ │ │ ├── swagger-ui.css.map
│ │ │ │ │ ├── swagger-ui.js
│ │ │ │ │ └── swagger-ui.js.map
│ │ │ └── scala
│ │ │ │ └── com
│ │ │ │ └── github
│ │ │ │ └── j5ik2o
│ │ │ │ └── threadWeaver
│ │ │ │ └── adaptor
│ │ │ │ ├── DISettings.scala
│ │ │ │ ├── aggregates
│ │ │ │ ├── typed
│ │ │ │ │ ├── PersistentThreadAggregate.scala
│ │ │ │ │ ├── ShardedThreadAggregates.scala
│ │ │ │ │ ├── ShardedThreadAggregatesProxy.scala
│ │ │ │ │ ├── ThreadAggregate.scala
│ │ │ │ │ └── ThreadAggregates.scala
│ │ │ │ └── untyped
│ │ │ │ │ ├── ChildActorLookup.scala
│ │ │ │ │ ├── PersistentThreadAggregate.scala
│ │ │ │ │ ├── Settings.scala
│ │ │ │ │ ├── ShardedThreadAggregates.scala
│ │ │ │ │ ├── ShardedThreadAggregatesRegion.scala
│ │ │ │ │ ├── ThreadAggregate.scala
│ │ │ │ │ └── ThreadAggregates.scala
│ │ │ │ ├── dao
│ │ │ │ └── jdbc
│ │ │ │ │ ├── SlickDaoSupport.scala
│ │ │ │ │ ├── Thread.scala
│ │ │ │ │ ├── ThreadAdministratorIds.scala
│ │ │ │ │ ├── ThreadMemberIds.scala
│ │ │ │ │ └── ThreadMessage.scala
│ │ │ │ ├── das
│ │ │ │ └── ThreadDas.scala
│ │ │ │ ├── error
│ │ │ │ └── InterfaceError.scala
│ │ │ │ ├── grpc
│ │ │ │ ├── presenter
│ │ │ │ │ ├── AddMessagesPresenterImpl.scala
│ │ │ │ │ ├── CreateThreadPresenterImpl.scala
│ │ │ │ │ ├── DestroyThreadPresenterImpl.scala
│ │ │ │ │ ├── JoinAdministratorIdsPresenterImpl.scala
│ │ │ │ │ ├── JoinMemberIdsPresenterImpl.scala
│ │ │ │ │ ├── LeaveAdministratorIdsPresenterImpl.scala
│ │ │ │ │ ├── LeaveMemberIdsPresenterImpl.scala
│ │ │ │ │ └── RemoveMessagesPresenterImpl.scala
│ │ │ │ ├── service
│ │ │ │ │ ├── ThreadCommandServiceImpl.scala
│ │ │ │ │ └── ThreadQueryServiceImpl.scala
│ │ │ │ └── validator
│ │ │ │ │ └── ThreadValidatorSupport.scala
│ │ │ │ ├── http
│ │ │ │ ├── controller
│ │ │ │ │ ├── ThreadCommandControllerImpl.scala
│ │ │ │ │ └── ThreadQueryControllerImpl.scala
│ │ │ │ ├── directives
│ │ │ │ │ ├── MetricsDirective.scala
│ │ │ │ │ ├── ThreadValidateDirectives.scala
│ │ │ │ │ └── ValidationsRejection.scala
│ │ │ │ ├── presenter
│ │ │ │ │ ├── AddMessagesPresenterImpl.scala
│ │ │ │ │ ├── CreateThreadPresenterImpl.scala
│ │ │ │ │ ├── DestroyThreadPresenterImpl.scala
│ │ │ │ │ ├── JoinAdministratorIdsPresenterImpl.scala
│ │ │ │ │ ├── JoinMemberIdsPresenterImpl.scala
│ │ │ │ │ ├── LeaveAdministratorIdsPresenterImpl.scala
│ │ │ │ │ ├── LeaveMemberIdsPresenterImpl.scala
│ │ │ │ │ ├── RemoveMessagesPresenterImpl.scala
│ │ │ │ │ ├── ThreadMessagePresenterImpl.scala
│ │ │ │ │ └── ThreadPresenterImpl.scala
│ │ │ │ ├── rejections
│ │ │ │ │ ├── NotFoundRejection.scala
│ │ │ │ │ ├── RejectionHandlers.scala
│ │ │ │ │ └── ThreadWeaverRejection.scala
│ │ │ │ └── routes
│ │ │ │ │ ├── DefaultRequestLoggingFormatter.scala
│ │ │ │ │ ├── RequestFormatter.scala
│ │ │ │ │ ├── RequestResultFormatter.scala
│ │ │ │ │ ├── RouteLogging.scala
│ │ │ │ │ └── Routes.scala
│ │ │ │ ├── metrics
│ │ │ │ └── KamonMetricsReporter.scala
│ │ │ │ ├── readModelUpdater
│ │ │ │ ├── ShardedThreadReadModelUpdaters.scala
│ │ │ │ ├── ShardedThreadReadModelUpdatersRegion.scala
│ │ │ │ ├── ThreadReadModelUpdater.scala
│ │ │ │ └── ThreadReadModelUpdaters.scala
│ │ │ │ ├── routing
│ │ │ │ └── ThreadToRMURouter.scala
│ │ │ │ ├── serialization
│ │ │ │ ├── CirceJsonSerialization.scala
│ │ │ │ ├── JournalEventTagPartitioner.scala
│ │ │ │ ├── ThreadEventJSONSerializer.scala
│ │ │ │ ├── ThreadMessageJSONSerializer.scala
│ │ │ │ ├── ThreadTaggingEventAdaptor.scala
│ │ │ │ └── json
│ │ │ │ │ ├── CreateThreadFailedJson.scala
│ │ │ │ │ ├── CreateThreadJson.scala
│ │ │ │ │ ├── CreateThreadSucceededJson.scala
│ │ │ │ │ └── ThreadCreatedJson.scala
│ │ │ │ ├── swagger
│ │ │ │ └── SwaggerDocService.scala
│ │ │ │ ├── util
│ │ │ │ └── PingPong.scala
│ │ │ │ └── validator
│ │ │ │ ├── ValidateUtils.scala
│ │ │ │ ├── Validator.scala
│ │ │ │ └── package.scala
│ │ ├── multi-jvm
│ │ │ ├── resources
│ │ │ │ ├── logback-test.xml
│ │ │ │ └── reference.conf
│ │ │ └── scala
│ │ │ │ └── com
│ │ │ │ └── github
│ │ │ │ └── j5ik2o
│ │ │ │ └── threadWeaver
│ │ │ │ └── adaptor
│ │ │ │ └── aggregates
│ │ │ │ ├── DynamoDbConfig.scala
│ │ │ │ ├── DynamoDbSpecSupport.scala
│ │ │ │ ├── LevelDbConfig.scala
│ │ │ │ ├── LevelDbSpecSupport.scala
│ │ │ │ ├── typed
│ │ │ │ ├── ShardedThreadAggregateOnDynamoDbSpec.scala
│ │ │ │ └── ShardedThreadAggregateOnLevelDbSpec.scala
│ │ │ │ └── untyped
│ │ │ │ ├── ShardedThreadAggregateOnDynamoDbSpec.scala
│ │ │ │ └── ShardedThreadAggregateOnLevelDbSpec.scala
│ │ └── test
│ │ │ ├── resources
│ │ │ ├── logback-test.xml
│ │ │ └── reference.conf
│ │ │ └── scala
│ │ │ └── com
│ │ │ └── github
│ │ │ └── j5ik2o
│ │ │ └── threadWeaver
│ │ │ └── adaptor
│ │ │ ├── DITestSettings.scala
│ │ │ ├── aggregates
│ │ │ ├── PersistenceCleanup.scala
│ │ │ ├── typed
│ │ │ │ ├── PersistentThreadAggregateOnDynamoDBSpec.scala
│ │ │ │ ├── PersistentThreadAggregateOnLevelDBSpec.scala
│ │ │ │ ├── ShardedThreadAggregatesSpec.scala
│ │ │ │ ├── ThreadAggregateSpec.scala
│ │ │ │ ├── ThreadAggregatesSpec.scala
│ │ │ │ └── TypedActorSpecSupport.scala
│ │ │ └── untyped
│ │ │ │ ├── ActorSpecSupport.scala
│ │ │ │ ├── AkkaSpec.scala
│ │ │ │ ├── PersistentThreadAggregateOnDynamoDBSpec.scala
│ │ │ │ ├── PersistentThreadAggregateOnLevelDBSpec.scala
│ │ │ │ ├── ShardedThreadAggregatesSpec.scala
│ │ │ │ ├── ThreadAggregateSpec.scala
│ │ │ │ └── ThreadAggregatesSpec.scala
│ │ │ ├── grpc
│ │ │ └── service
│ │ │ │ ├── ServiceSpec.scala
│ │ │ │ └── ThreadServiceImplSpec.scala
│ │ │ ├── http
│ │ │ └── controller
│ │ │ │ ├── RouteSpec.scala
│ │ │ │ └── ThreadControllerImplSpec.scala
│ │ │ ├── readModelUpdater
│ │ │ ├── ThreadReadModelUpdaterOnDynamoDBSpec.scala
│ │ │ └── ThreadReadModelUpdaterOnLevelDBSpec.scala
│ │ │ └── util
│ │ │ ├── DynamoDBSpecSupport.scala
│ │ │ ├── FlywayWithMySQLSpecSupport.scala
│ │ │ ├── JdbcSpecSupport.scala
│ │ │ ├── RandomPortSupport.scala
│ │ │ ├── ScalaCheckSupport.scala
│ │ │ ├── ScalaFuturesSpecSupport.scala
│ │ │ └── Slick3SpecSupport.scala
│ └── templates
│ │ └── template.ftl
└── use-case
│ └── src
│ └── main
│ └── scala
│ └── com
│ └── github
│ └── j5ik2o
│ └── threadWeaver
│ └── useCase
│ ├── DISettings.scala
│ ├── typed
│ ├── AddMessagesUseCaseTypeImpl.scala
│ ├── CreateThreadUseCaseTypeImpl.scala
│ ├── DestroyThreadUseCaseTypeImpl.scala
│ ├── JoinAdministratorIdsUseCaseTypeImpl.scala
│ ├── JoinMemberIdsUseCaseTypeImpl.scala
│ ├── LeaveAdministratorIdsUseCaseTypeImpl.scala
│ ├── LeaveMemberIdsUseCaseTypeImpl.scala
│ └── RemoveMessagesUseCaseTypeImpl.scala
│ └── untyped
│ ├── AddMessagesUseCaseUntypeImpl.scala
│ ├── CreateThreadUseCaseUntypeImpl.scala
│ ├── DestroyThreadUseCaseUntypeImpl.scala
│ ├── JoinAdministratorIdsUseCaseUntypeImpl.scala
│ ├── JoinMemberIdsUseCaseUntypeImpl.scala
│ ├── LeaveAdministratorIdsUseCaseUntypeImpl.scala
│ ├── LeaveMemberIdsUseCaseUntypeImpl.scala
│ ├── RemoveMessagesUseCaseUntypeImpl.scala
│ └── UseCaseSupport.scala
├── project
├── Dependencies.scala
├── Settings.scala
├── Utils.scala
├── build.properties
└── plugins.sbt
├── read-model-updater
└── src
│ └── main
│ ├── resources
│ ├── application.conf
│ ├── common
│ │ ├── akka.conf
│ │ ├── k8s_probe.conf
│ │ ├── kamon.conf
│ │ └── thread-weaver.conf
│ ├── local-cluster.conf
│ ├── local-cluster
│ │ └── akka.conf
│ ├── local-machine
│ │ └── akka.conf
│ ├── logback.xml
│ ├── production.conf
│ └── production
│ │ └── akka.conf
│ └── scala
│ └── com
│ └── github
│ └── j5ik2o
│ └── threadWeaver
│ └── rmu
│ ├── ClusterWatcher.scala
│ ├── HealthCheck.scala
│ └── Main.scala
├── scalastyle-config.xml
├── slide
├── .gitignore
├── domain-models.puml
├── images
│ ├── K0001062281.jpg
│ ├── actor-tree.svg
│ ├── akka-event-sourcing.svg
│ ├── arete.html
│ ├── arete_files
│ │ ├── cb=gapi.loaded_0
│ │ ├── celebrating-50-years-of-pride-6537357791592448.3-s.png
│ │ ├── free_transration_online.html
│ │ ├── id414706506
│ │ ├── m=NBZ7u,TxZWcc,WgDvvc,Z1Gqqd,aa,aam1T,abd,async,bgd,dBHdve,dvl,ecm,exdc,fEVMic,foot,itm,lr,lu,m,mUpTid,mpck,mu,qik19b,sb_wiz,sf,sonic,spch,tl,tobs,vs,xpd,xz7cCd,zMMxKd
│ │ ├── m=Uuupec,r36a9c
│ │ ├── rs=AA2YrTvOuLZIEz8epvzJpWYCzzvc74aqcw
│ │ ├── rs=ACT90oFtVfH8HGapCMs4PqIfJEKufvZoPQ
│ │ ├── saved_resource
│ │ ├── saved_resource(1)
│ │ ├── saved_resource(2)
│ │ ├── saved_resource(3)
│ │ ├── saved_resource(4)
│ │ ├── saved_resource(5)
│ │ ├── saved_resource(6)
│ │ ├── search
│ │ └── 翻訳
│ ├── clean-architecture.jpeg
│ ├── cluster-image.svg
│ ├── domain-models.svg
│ ├── event-stream.png
│ ├── helm.png
│ ├── logo-hz.png
│ ├── logo-vt.png
│ ├── modules.svg
│ ├── real-events.png
│ ├── same-node-layout.svg
│ ├── self-prof.png
│ ├── separate-node-layout.svg
│ ├── system-diagram.svg
│ └── thread-aggregate-tree.svg
├── modules.puml
├── pdf
│ └── presentation.pdf
├── presentation.md
├── template
│ ├── index.html
│ ├── remark.min.js
│ └── style.scss
└── thread-aggregates.puml
└── tools
├── deploy
├── deploy-app.sh
├── deploy-flyway.sh
├── eks
│ ├── .gitignore
│ ├── dd-agent-values.yaml.default
│ ├── deploy-dd-agent.sh
│ ├── push-image-to-ecr.sh
│ ├── secrets.yaml.default
│ ├── test-application.sh
│ └── test-management.sh
├── k8s-d4m
│ ├── build-image.sh
│ ├── deploy-local-db.sh
│ ├── migrate-local-db.sh
│ ├── secrets.yaml
│ ├── test-application.sh
│ └── test-management.sh
├── k8s-setup.sh
└── rbac-config.yaml
├── dynamodb
├── create-table.sh
└── table.json
├── flyway
├── Dockerfile
├── Makefile
├── config.env
├── deploy.env
├── src
│ └── test
│ │ └── resources
│ │ └── db-migration
│ │ └── V1__Create_Tables.sql
└── version.sh
├── gatling-aggregate-runner
└── src
│ └── main
│ ├── resources
│ ├── application.conf
│ └── logback.xml
│ └── scala
│ └── com
│ └── github
│ └── j5ik2o
│ └── gatling
│ └── runner
│ └── Runner.scala
├── gatling-runner
├── docker-compose.yml
└── src
│ └── main
│ ├── resources
│ ├── application.conf
│ └── logback.xml
│ └── scala
│ └── com
│ └── github
│ └── j5ik2o
│ └── gatling
│ └── runner
│ └── Runner.scala
├── gatling-s3-reporter
├── Dockerfile
├── Makefile
├── config.env
├── deploy.env
├── docker-compose.yml
├── generate-report.sh
└── version.sh
├── gatling-test
└── src
│ └── it
│ ├── resources
│ ├── gatling.conf
│ ├── logback.xml
│ └── reference.conf
│ └── scala
│ └── com
│ └── github
│ └── j5ik2o
│ └── gatling
│ └── ThreadSimulation.scala
├── local-dynamodb
├── native-libs
│ ├── libsqlite4java-linux-amd64-1.0.392.so
│ ├── libsqlite4java-linux-i386-1.0.392.so
│ ├── libsqlite4java-osx-1.0.392.dylib
│ ├── sqlite4java-win32-x64-1.0.392.dll
│ └── sqlite4java-win32-x86-1.0.392.dll
└── src
│ └── main
│ └── scala
│ └── com
│ └── github
│ └── j5ik2o
│ └── threadWeaver
│ └── dynmodb
│ └── Main.scala
├── migrate-dynamodb
└── src
│ └── main
│ └── scala
│ └── com
│ └── github
│ └── j5ik2o
│ └── threadWeaver
│ └── dynmodb
│ ├── DynamoDBCreator.scala
│ └── Main.scala
└── terraform
├── .gitignore
├── dynamodb
├── main.tf
├── output.tf
└── variables.tf
├── ecr
├── main.tf
├── output.tf
└── variables.tf
├── eks-terraform-apply.sh
├── eks-terraform-destroy.sh
├── eks-terraform-env.sh
├── eks-terraform-env.sh.default
├── eks-terraform-init.sh
├── eks-terraform-plan.sh
├── eks.tfvars.default
├── gatling
├── ecr.tf
├── main.tf
└── variables.tf
├── main.tf
├── output.tf
└── variables.tf
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build:
4 | environment:
5 | - TEST_TIME_FACTOR: "2"
6 | - JAVA_OPTS: "-XX:ReservedCodeCacheSize=256M -Xms1g -Xmx3g -Xss2m"
7 | - AWS_REGION: "ap-northeast-1"
8 | machine: true
9 | steps:
10 | - run:
11 | command: |
12 | sudo apt-get update -qq && sudo apt-get install -y libaio1 libevent-dev
13 | sudo apt-get install -y software-properties-common
14 | sudo apt-get install -y openjdk-8-jdk
15 | sudo apt-get update -qq
16 | sudo apt-get install -y maven git
17 | echo "deb https://dl.bintray.com/sbt/debian /" | sudo tee -a /etc/apt/sources.list.d/sbt.list
18 | sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2EE0EA64E40A89B84B2DF73499E82A75642AC823
19 | sudo apt-get update -qq
20 | sudo apt-get install sbt
21 | - checkout
22 | - restore_cache:
23 | key: scala-library-dependencies-{{ checksum "build.sbt" }}
24 | - run: sbt test
25 | - save_cache:
26 | paths: [ "~/.sbt/boot", "~/.ivy2", "~/.wixMySQL" ]
27 | key: scala-library-dependencies-{{ checksum "build.sbt" }}
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .classpath
2 | .project
3 | .cache-main
4 | .cache-tests
5 | .settings/
6 | target/
7 | project/target
8 | .cache
9 | .idea/
10 | .cache-main
11 | .envrc
12 | bin/
13 | native
14 | *.pyc
15 | *.pem
16 | *.stackdump
17 | *.tfvars
18 | *.tfstate
19 | *.tfstate.backup
20 | *.deb
21 | *.tgz
22 | *.log
23 | .terraform/
24 | node_modules/
25 | dump.rdb
26 |
27 | .DS_Store
28 | .credentials
29 | env.sh
30 | cluster.yaml
31 | eksctl-env.sh
32 |
--------------------------------------------------------------------------------
/.scalafmt.conf:
--------------------------------------------------------------------------------
1 | version = 2.0.0-RC5
2 | style = defaultWithAlign
3 | danglingParentheses = true
4 | indentOperator = spray
5 | includeCurlyBraceInSelectChains = true
6 | maxColumn = 120
7 | rewrite.rules = [RedundantParens, SortImports, PreferCurlyFors]
8 | spaces.inImportCurlyBraces = true
9 | binPack.literalArgumentLists = false
10 | unindentTopLevelOperators = true
11 | optIn.breaksInsideChains = true
12 | newlines.alwaysBeforeTopLevelStatements = true
--------------------------------------------------------------------------------
/api-client/src/main/scala/com/github/j5ik2o/threadWeaver/api/APIException.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.api
2 |
3 | class APIException(errorMessages: Seq[String]) extends Exception(errorMessages.mkString(", "))
4 |
--------------------------------------------------------------------------------
/api-client/src/main/scala/com/github/j5ik2o/threadWeaver/api/ClientSettings.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.api
2 |
3 | import akka.http.scaladsl.model.Uri
4 |
5 | final case class ClientSettings(
6 | scheme: String,
7 | host: String,
8 | port: Option[Int] = None,
9 | version: String = "v1",
10 | queueSize: Int,
11 | https: Boolean = false
12 | ) {
13 | def urlString: String = s"$scheme://$host${port.fold("")(p => s":$p")}"
14 | def uri: Uri = Uri.from(scheme = scheme, host = host, port = port.getOrElse(0))
15 | }
16 |
--------------------------------------------------------------------------------
/api-client/src/main/scala/com/github/j5ik2o/threadWeaver/api/ThreadWeaverClient.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.api
2 |
3 | import akka.http.scaladsl.model.HttpResponse
4 | import akka.http.scaladsl.unmarshalling.Unmarshal
5 | import akka.stream.Materializer
6 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.ErrorsResponseJson
7 | import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport
8 |
9 | import scala.concurrent.{ ExecutionContext, Future }
10 |
11 | trait ThreadWeaverClient extends FailFastCirceSupport {
12 | import io.circe.generic.auto._
13 | protected def handleResponse[A](
14 | response: HttpResponse
15 | )(f: Unmarshal[HttpResponse] => Future[A])(implicit ec: ExecutionContext, mat: Materializer): Future[A] = {
16 | val unmarshal = Unmarshal(response)
17 | if (response.status.isSuccess())
18 | f(unmarshal)
19 | else
20 | unmarshal.to[ErrorsResponseJson].flatMap { e =>
21 | Future.failed(new APIException(e.error_messages))
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/api-client/src/test/scala/com/github/j5ik2o/threadWeaver/api/ThreadWeaverCommandClientSpec.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.api
2 |
3 | import org.scalatest.FreeSpec
4 |
5 | class ThreadWeaverCommandClientSpec extends FreeSpec {}
6 |
--------------------------------------------------------------------------------
/api-server/src/main/resources/application.conf:
--------------------------------------------------------------------------------
1 | include classpath("common/akka.conf")
2 | include classpath("local-machine/akka.conf")
3 | include classpath("common/kamon.conf")
4 | include classpath("common/thread-weaver.conf")
5 | include classpath("common/k8s_probe.conf")
6 | include classpath("common/kamon.conf")
7 |
8 |
--------------------------------------------------------------------------------
/api-server/src/main/resources/common/k8s_probe.conf:
--------------------------------------------------------------------------------
1 | k8s_probe {
2 | host = "localhost"
3 | port = "18080"
4 | port = ${?THREAD_WEAVER_API_SERVER_HTTP_PORT}
5 | path {
6 | liveness = "/live"
7 | readiness = "/ready"
8 | }
9 | }
--------------------------------------------------------------------------------
/api-server/src/main/resources/common/thread-weaver.conf:
--------------------------------------------------------------------------------
1 | thread-weaver {
2 | api-server {
3 | host = "0.0.0.0"
4 | http {
5 | port = 8080
6 | port = ${?THREAD_WEAVER_API_SERVER_HTTP_PORT}
7 | }
8 | terminate.duration = 3s
9 | nr-of-shards = 30
10 | nr-of-shards = ${?THREAD_WEAVER_API_NR_OF_SHARDS}
11 | }
12 |
13 | read-model-updater.sql-batch-count = 1
14 | read-model-updater.sql-batch-count = ${?THREAD_WEAVER_READ_MODEL_UPDATER_SQL_BATCH_COUNT}
15 | read-model-updater.thread {
16 | shard-name = "thread"
17 | category = "thread"
18 | num-partition = 1
19 | num-partition = ${?THREAD_WEAVER_READ_MODEL_UPDATER_ROOM_NUM_PARTITION}
20 | }
21 |
22 | }
23 |
24 | slick {
25 | profile = "slick.jdbc.MySQLProfile$"
26 | db {
27 | driver = "com.mysql.jdbc.Driver"
28 | url = "jdbc:mysql://localhost:3306/tw?useSSL=false"
29 | url = ${?THREAD_WEAVER_SLICK_URL}
30 | user = "tw"
31 | user = ${?THREAD_WEAVER_SLICK_USER}
32 | password = "passwd"
33 | password = ${?THREAD_WEAVER_SLICK_PASSWORD}
34 | connectionPool = "HikariCP"
35 | keepAliveConnection = true
36 | properties = {
37 | maximumPoolSize = 64
38 | maximumPoolSize = ${?THREAD_WEAVER_SLICK_MAX_POOL_SIZE}
39 | minimumIdle = 64
40 | minimumIdle = ${?THREAD_WEAVER_SLICK_MIN_IDLE_SIZE}
41 | connectionTimeout = 30
42 | connectionTimeout = ${?THREAD_WEAVER_SLICK_CONNECTION_TIMEOUT}
43 | idleTimeout = 30
44 | idleTimeout = ${?THREAD_WEAVER_SLICK_IDLE_TIMEOUT}
45 | }
46 | poolName = "slick-pool"
47 | poolName = ${?THREAD_WEAVER_SLICK_POOL_NAME}
48 | numThreads = 64
49 | numThreads = ${?THREAD_WEAVER_SLICK_NUM_THREADS}
50 | queueSize = 1000
51 | queueSize = ${?THREAD_WEAVER_SLICK_QUEUE_SIZE}
52 | registerMbeans=true
53 | }
54 | }
--------------------------------------------------------------------------------
/api-server/src/main/resources/local-cluster.conf:
--------------------------------------------------------------------------------
1 | include classpath("common/akka.conf")
2 | include classpath("local-cluster/akka.conf")
3 | include classpath("common/kamon.conf")
4 | include classpath("common/thread-weaver.conf")
5 | include classpath("common/k8s_probe.conf")
6 | include classpath("common/kamon.conf")
7 |
--------------------------------------------------------------------------------
/api-server/src/main/resources/production.conf:
--------------------------------------------------------------------------------
1 | include classpath("common/akka.conf")
2 | include classpath("production/akka.conf")
3 | include classpath("common/kamon.conf")
4 | include classpath("common/thread-weaver.conf")
5 | include classpath("common/k8s_probe.conf")
6 | include classpath("common/kamon.conf")
7 |
--------------------------------------------------------------------------------
/api-server/src/main/scala/com/github/j5ik2o/threadWeaver/api/ClusterWatcher.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.api
2 |
3 | import akka.actor.{ Actor, ActorLogging }
4 | import akka.cluster.Cluster
5 |
6 | class ClusterWatcher extends Actor with ActorLogging {
7 | private val cluster = Cluster(context.system)
8 |
9 | override def receive: PartialFunction[Any, Unit] = {
10 | case msg => log.info(s"Cluster ${cluster.selfAddress} >>> " + msg)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/api-server/src/main/scala/com/github/j5ik2o/threadWeaver/api/HealthCheck.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.api
2 |
3 | import akka.actor.ActorSystem
4 | import akka.cluster.{ Cluster, MemberStatus }
5 | import cats.syntax.validated._
6 | import com.github.everpeace.healthchecks._
7 |
8 | import scala.concurrent.Future
9 |
10 | object HealthCheck {
11 |
12 | def akka(host: String, port: Int)(implicit system: ActorSystem): HealthCheck = {
13 | import system.dispatcher
14 | asyncHealthCheck("akka-cluster") {
15 | Future {
16 | val cluster = Cluster(system)
17 | val status = cluster.selfMember.status
18 | val result = status == MemberStatus.Up || status == MemberStatus.WeaklyUp
19 | if (result)
20 | healthy
21 | else
22 | "Not Found".invalidNel
23 |
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/charts/dynamodb/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | description: dynamodb
3 | name: dynamodb
4 | version: 0.1.0
--------------------------------------------------------------------------------
/charts/dynamodb/environments/local-values.yaml:
--------------------------------------------------------------------------------
1 | # Default values for astraea-elasticmq.
2 | # This is a YAML-formatted file.
3 | # Declare variables to be passed into your templates.
4 | replicaCount: 1
5 | image:
6 | repository: amazon/dynamodb-local
7 | tag: 1.11.475
8 | pullPolicy: IfNotPresent
9 | service:
10 | name: dynamodb
11 | type: NodePort
12 | externalPort: 8000
13 | externalNodePort: 32000
14 | internalPort: 8000
15 | resources:
16 | # We usually recommend not to specify default resources and to leave this as a conscious
17 | # choice for the user. This also increases chances charts run on environments with little
18 | # resources, such as Minikube. If you do want to specify resources, uncomment the following
19 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
20 | requests: {}
21 | # cpu: 500m
22 | # memory: 128Mi
23 | # limits:
24 | # cpu: 500m
25 | # memory: 512Mi
--------------------------------------------------------------------------------
/charts/dynamodb/environments/prod-values.yaml:
--------------------------------------------------------------------------------
1 | # Default values for astraea-elasticmq.
2 | # This is a YAML-formatted file.
3 | # Declare variables to be passed into your templates.
4 | replicaCount: 1
5 | image:
6 | repository: amazon/dynamodb-local
7 | tag: 1.11.475
8 | pullPolicy: IfNotPresent
9 | service:
10 | name: dynamodb
11 | type: LoadBalancer
12 | externalPort: 8000
13 | externalNodePort: 32000
14 | internalPort: 8000
15 | resources:
16 | # We usually recommend not to specify default resources and to leave this as a conscious
17 | # choice for the user. This also increases chances charts run on environments with little
18 | # resources, such as Minikube. If you do want to specify resources, uncomment the following
19 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
20 | requests: {}
21 | # cpu: 500m
22 | # memory: 128Mi
23 | # limits:
24 | # cpu: 500m
25 | # memory: 512Mi
--------------------------------------------------------------------------------
/charts/dynamodb/templates/_helpers.tpl:
--------------------------------------------------------------------------------
1 | {{/* vim: set filetype=mustache: */}}
2 | {{/*
3 | Expand the name of the chart.
4 | */}}
5 | {{- define "name" -}}
6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
7 | {{- end -}}
8 |
9 | {{/*
10 | Create a default fully qualified app name.
11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
12 | */}}
13 | {{- define "fullname" -}}
14 | {{- $name := default .Chart.Name .Values.nameOverride -}}
15 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
16 | {{- end -}}
--------------------------------------------------------------------------------
/charts/dynamodb/templates/pod.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: {{ template "name" . }}
5 | labels:
6 | app: {{ template "name" . }}
7 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
8 | release: {{ .Release.Name }}
9 | heritage: {{ .Release.Service }}
10 | spec:
11 | containers:
12 | - name: {{ template "name" . }}
13 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
14 | ports:
15 | - name: internal-port
16 | containerPort: {{ .Values.service.internalPort }}
17 |
--------------------------------------------------------------------------------
/charts/dynamodb/templates/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: {{ template "name" . }}
5 | labels:
6 | app: {{ template "name" . }}
7 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
8 | release: {{ .Release.Name }}
9 | heritage: {{ .Release.Service }}
10 | spec:
11 | type: {{ .Values.service.type }}
12 | ports:
13 | - port: {{ .Values.service.externalPort }}
14 | targetPort: {{ .Values.service.internalPort }}
15 | {{ if eq .Values.service.type "NodePort" }}nodePort: {{ .Values.service.externalNodePort }}{{ end }}
16 | protocol: TCP
17 | name: {{ .Values.service.name }}
18 | selector:
19 | app: {{ template "name" . }}
20 | release: {{ .Release.Name }}
--------------------------------------------------------------------------------
/charts/dynamodb/values.yaml:
--------------------------------------------------------------------------------
1 | # Default values for astraea-elasticmq.
2 | # This is a YAML-formatted file.
3 | # Declare variables to be passed into your templates.
4 | replicaCount: 1
5 | image:
6 | repository: amazon/dynamodb-local
7 | tag: 1.11.475
8 | pullPolicy: IfNotPresent
9 | service:
10 | name: dynamodb
11 | type: LoadBalancer
12 | externalPort: 8000
13 | externalNodePort: 32000
14 | internalPort: 8000
15 | resources:
16 | # We usually recommend not to specify default resources and to leave this as a conscious
17 | # choice for the user. This also increases chances charts run on environments with little
18 | # resources, such as Minikube. If you do want to specify resources, uncomment the following
19 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
20 | requests: {}
21 | # cpu: 500m
22 | # memory: 128Mi
23 | # limits:
24 | # cpu: 500m
25 | # memory: 512Mi
--------------------------------------------------------------------------------
/charts/mysql/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | description: mysql
3 | name: mysql
4 | version: 0.1.0
--------------------------------------------------------------------------------
/charts/mysql/environments/local-values.yaml:
--------------------------------------------------------------------------------
1 | # Default values for astraea-elasticmq.
2 | # This is a YAML-formatted file.
3 | # Declare variables to be passed into your templates.
4 | replicaCount: 1
5 | image:
6 | repository: mysql
7 | tag: 5.6
8 | pullPolicy: IfNotPresent
9 | service:
10 | name: mysql
11 | type: NodePort
12 | externalPort: 3306
13 | externalNodePort: 30306
14 | internalPort: 3306
15 | resources:
16 | # We usually recommend not to specify default resources and to leave this as a conscious
17 | # choice for the user. This also increases chances charts run on environments with little
18 | # resources, such as Minikube. If you do want to specify resources, uncomment the following
19 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
20 | requests: {}
21 | # cpu: 500m
22 | # memory: 128Mi
23 | # limits:
24 | # cpu: 500m
25 | # memory: 512Mi
--------------------------------------------------------------------------------
/charts/mysql/environments/prod-values.yaml:
--------------------------------------------------------------------------------
1 | # Default values for astraea-elasticmq.
2 | # This is a YAML-formatted file.
3 | # Declare variables to be passed into your templates.
4 | replicaCount: 1
5 | image:
6 | repository: mysql
7 | tag: 5.6
8 | pullPolicy: IfNotPresent
9 | service:
10 | name: mysql
11 | type: LoadBalancer
12 | externalPort: 3306
13 | externalNodePort: 30306
14 | internalPort: 3306
15 | resources:
16 | # We usually recommend not to specify default resources and to leave this as a conscious
17 | # choice for the user. This also increases chances charts run on environments with little
18 | # resources, such as Minikube. If you do want to specify resources, uncomment the following
19 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
20 | requests: {}
21 | # cpu: 500m
22 | # memory: 128Mi
23 | # limits:
24 | # cpu: 500m
25 | # memory: 512Mi
--------------------------------------------------------------------------------
/charts/mysql/templates/_helpers.tpl:
--------------------------------------------------------------------------------
1 | {{/* vim: set filetype=mustache: */}}
2 | {{/*
3 | Expand the name of the chart.
4 | */}}
5 | {{- define "name" -}}
6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
7 | {{- end -}}
8 |
9 | {{/*
10 | Create a default fully qualified app name.
11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
12 | */}}
13 | {{- define "fullname" -}}
14 | {{- $name := default .Chart.Name .Values.nameOverride -}}
15 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
16 | {{- end -}}
--------------------------------------------------------------------------------
/charts/mysql/templates/pod.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: {{ template "name" . }}
5 | labels:
6 | app: {{ template "name" . }}
7 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
8 | release: {{ .Release.Name }}
9 | heritage: {{ .Release.Service }}
10 | spec:
11 | containers:
12 | - name: {{ template "name" . }}
13 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
14 | ports:
15 | - name: internal-port
16 | containerPort: {{ .Values.service.internalPort }}
17 | args: ["--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci"]
18 | env:
19 | - name: "MAX_SERVERS"
20 | value: "32"
21 | - name: "ENSEMBLE_NAME"
22 | value: "mysql"
23 | - name: "MYSQL_DATABASE"
24 | value: "tw"
25 | - name: "MYSQL_USER"
26 | value: "tw"
27 | - name: "MYSQL_PASSWORD"
28 | value: "passwd"
29 | - name: "MYSQL_ROOT_PASSWORD"
30 | value: "root"
--------------------------------------------------------------------------------
/charts/mysql/templates/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: {{ template "name" . }}
5 | labels:
6 | app: {{ template "name" . }}
7 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
8 | release: {{ .Release.Name }}
9 | heritage: {{ .Release.Service }}
10 | spec:
11 | type: {{ .Values.service.type }}
12 | ports:
13 | - port: {{ .Values.service.externalPort }}
14 | targetPort: {{ .Values.service.internalPort }}
15 | {{ if eq .Values.service.type "NodePort" }}nodePort: {{ .Values.service.externalNodePort }}{{ end }}
16 | protocol: TCP
17 | name: {{ .Values.service.name }}
18 | selector:
19 | app: {{ template "name" . }}
20 | release: {{ .Release.Name }}
--------------------------------------------------------------------------------
/charts/mysql/values.yaml:
--------------------------------------------------------------------------------
1 | # Default values for astraea-elasticmq.
2 | # This is a YAML-formatted file.
3 | # Declare variables to be passed into your templates.
4 | replicaCount: 1
5 | image:
6 | repository: mysql
7 | tag: 5.6
8 | pullPolicy: IfNotPresent
9 | service:
10 | name: mysql
11 | type: LoadBalancer
12 | externalPort: 3306
13 | externalNodePort: 30306
14 | internalPort: 3306
15 | resources:
16 | # We usually recommend not to specify default resources and to leave this as a conscious
17 | # choice for the user. This also increases chances charts run on environments with little
18 | # resources, such as Minikube. If you do want to specify resources, uncomment the following
19 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
20 | requests: {}
21 | # cpu: 500m
22 | # memory: 128Mi
23 | # limits:
24 | # cpu: 500m
25 | # memory: 512Mi
--------------------------------------------------------------------------------
/charts/thread-weaver-api-server/.helmignore:
--------------------------------------------------------------------------------
1 | # Patterns to ignore when building packages.
2 | # This supports shell glob matching, relative path matching, and
3 | # negation (prefixed with !). Only one pattern per line.
4 | .DS_Store
5 | # Common VCS dirs
6 | .git/
7 | .gitignore
8 | .bzr/
9 | .bzrignore
10 | .hg/
11 | .hgignore
12 | .svn/
13 | # Common backup files
14 | *.swp
15 | *.bak
16 | *.tmp
17 | *~
18 | # Various IDEs
19 | .project
20 | .idea/
21 | *.tmproj
22 | environments/
--------------------------------------------------------------------------------
/charts/thread-weaver-api-server/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | description: thread-weaver api-server
3 | name: thread-weaver-api-server
4 | version: 1.0.0
--------------------------------------------------------------------------------
/charts/thread-weaver-api-server/environments/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 |
--------------------------------------------------------------------------------
/charts/thread-weaver-api-server/environments/local-values.yaml:
--------------------------------------------------------------------------------
1 | envName: development
2 | prodEnabled: false
3 | configResource: local-cluster.conf
4 | jvmHeapMin: 1500m
5 | jvmHeapMax: 1500m
6 | jvmMetaMax: 500m
7 | replicaCount: 3
8 | image:
9 | repository: j5ik2o/thread-weaver-api-server
10 | tag: 1.0.0-SNAPSHOT
11 | pullPolicy: IfNotPresent
12 | service:
13 | type: NodePort
14 | api:
15 | name: api
16 | externalPort: 18080
17 | externalNodePort: 30080
18 | internalPort: 8080
19 | management:
20 | name: management
21 | externalPort: 18558
22 | externalNodePort: 30081
23 | internalPort: 8558
24 | resources:
25 | requests:
26 | cpu: 2
27 | memory: 4Gi
28 | limits:
29 | cpu: 2
30 | memory: 4Gi
31 | slick:
32 | url: jdbc:mysql://mysql:3306/tw?useSSL=false
33 | user: tw
34 | maxPoolSize: 10
35 | minIdleSize: 10
36 | numThreads: 10
37 | queueSize: 1000
38 | connectionTimeout: 30
39 | idleTimeout: 30
40 | dynamodb:
41 | journalTableName: Journal
42 | snapshotTableName: Snapshot
43 | readJournalTableName : Journal
--------------------------------------------------------------------------------
/charts/thread-weaver-api-server/environments/prod-values.yaml:
--------------------------------------------------------------------------------
1 | envName: production
2 | prodEnabled: true
3 | configResource: production.conf
4 | jvmHeapMin: 1500m
5 | jvmHeapMax: 1500m
6 | jvmMetaMax: 500m
7 | replicaCount: 3
8 | image:
9 | repository: 738575627980.dkr.ecr.ap-northeast-1.amazonaws.com/j5ik2o/thread-weaver-api-server
10 | tag: latest
11 | pullPolicy: Always
12 | service:
13 | type: LoadBalancer
14 | api:
15 | name: api
16 | externalPort: 8080
17 | internalPort: 8080
18 | management:
19 | name: management
20 | externalPort: 8558
21 | internalPort: 8558
22 | resources:
23 | requests:
24 | cpu: 3
25 | memory: 4Gi
26 | limits:
27 | cpu: 3
28 | memory: 4Gi
29 | slick:
30 | url: jdbc:mysql://j5ik2o-rds-cluster-aurora.cluster-ctywrcabnmgr.ap-northeast-1.rds.amazonaws.com:3306/thread_weaver?useSSL=false
31 | user: root
32 | maxPoolSize: 64
33 | minIdleSize: 32
34 | numThreads: 64
35 | queueSize: 1000
36 | connectionTimeout: 30
37 | idleTimeout: 30
38 | dynamodb:
39 | journalTableName: j5ik2o-thread-weaver-journal
40 | snapshotTableName: j5ik2o-thread-weaver-snapshot
41 | readJournalTableName : j5ik2o-thread-weaver-journal
--------------------------------------------------------------------------------
/charts/thread-weaver-api-server/templates/_helpers.tpl:
--------------------------------------------------------------------------------
1 | {{/* vim: set filetype=mustache: */}}
2 | {{/*
3 | Expand the name of the chart.
4 | */}}
5 | {{- define "name" -}}
6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
7 | {{- end -}}
8 |
9 | {{/*
10 | Create a default fully qualified app name.
11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
12 | */}}
13 | {{- define "fullname" -}}
14 | {{- $name := default .Chart.Name .Values.nameOverride -}}
15 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
16 | {{- end -}}
--------------------------------------------------------------------------------
/charts/thread-weaver-api-server/templates/rbac.yml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: Role
3 | metadata:
4 | name: thread-weaver-api-server
5 | namespace: thread-weaver
6 | rules:
7 | - apiGroups: [""]
8 | resources: ["pods"]
9 | verbs: ["get", "watch", "list"]
10 | ---
11 | apiVersion: rbac.authorization.k8s.io/v1
12 | kind: RoleBinding
13 | metadata:
14 | name: thread-weaver-api-server
15 | namespace: thread-weaver
16 | subjects:
17 | - kind: ServiceAccount
18 | name: thread-weaver
19 | namespace: thread-weaver
20 | roleRef:
21 | kind: Role
22 | name: thread-weaver-api-server
23 | apiGroup: rbac.authorization.k8s.io
24 |
--------------------------------------------------------------------------------
/charts/thread-weaver-api-server/templates/service.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: {{ template "name" . }}
5 | namespace: thread-weaver
6 | annotations:
7 | service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
8 | labels:
9 | app: {{ template "name" . }}
10 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
11 | release: {{ .Release.Name }}
12 | heritage: {{ .Release.Service }}
13 | spec:
14 | selector:
15 | app: {{ template "name" . }}
16 | release: {{ .Release.Name }}
17 | type: {{ .Values.service.type }}
18 | ports:
19 | - name: api
20 | protocol: TCP
21 | port: {{ .Values.service.api.externalPort }}
22 | targetPort: api
23 | {{ if (and (eq .Values.service.type "NodePort") (not (empty .Values.service.api.externalNodePort))) }}
24 | nodePort: {{ .Values.service.api.externalNodePort }}
25 | {{ end }}
26 | - name: management
27 | protocol: TCP
28 | port: {{ .Values.service.management.externalPort }}
29 | targetPort: management
30 | {{ if (and (eq .Values.service.type "NodePort") (not (empty .Values.service.management.externalNodePort))) }}
31 | nodePort: {{ .Values.service.management.externalNodePort }}
32 | {{ end }}
33 |
--------------------------------------------------------------------------------
/charts/thread-weaver-api-server/values.yaml:
--------------------------------------------------------------------------------
1 | # Default values for astraea-elasticmq.
2 | # This is a YAML-formatted file.
3 | # Declare variables to be passed into your templates.
4 | envName: development
5 | prodEnabled: false
6 | configResource: application.conf
7 | jvmHeapMin: 1500m
8 | jvmHeapMax: 1500m
9 | jvmMetaMax: 500m
10 | replicaCount: 3
11 | image:
12 | repository: j5ik2o/thread-weaver-api-server
13 | tag: 1.0.0-SNAPSHOT
14 | pullPolicy: IfNotPresent
15 | service:
16 | type: NodePort
17 | api:
18 | name: api
19 | externalPort: 18080
20 | externalNodePort: 30080
21 | internalPort: 8080
22 | management:
23 | name: management
24 | externalPort: 18558
25 | externalNodePort: 30081
26 | internalPort: 8558
27 | slick:
28 | url: jdbc:mysql://mysql:3306/tw?useSSL=false
29 | user: tw
30 | maxPoolSize: 64
31 | minIdleSize: 64
32 | numThreads: 16
33 | queueSize: 1000
34 | connectionTimeout: 30
35 | idleTimeout: 30
36 | dynamodb:
37 | journalTableName: thread_weaver_journal
38 | snapshotTableName: thread_weaver_snapshot
39 | readJournalTableName: thread_weaver_journal
40 | resources:
41 | # We usually recommend not to specify default resources and to leave this as a conscious
42 | # choice for the user. This also increases chances charts run on environments with little
43 | # resources, such as Minikube. If you do want to specify resources, uncomment the following
44 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
45 | requests: {}
46 | # cpu: 500m
47 | # memory: 128Mi
48 | # limits:
49 | # cpu: 500m
50 | # memory: 512Mi
--------------------------------------------------------------------------------
/charts/thread-weaver-flyway/.helmignore:
--------------------------------------------------------------------------------
1 | # Patterns to ignore when building packages.
2 | # This supports shell glob matching, relative path matching, and
3 | # negation (prefixed with !). Only one pattern per line.
4 | .DS_Store
5 | # Common VCS dirs
6 | .git/
7 | .gitignore
8 | .bzr/
9 | .bzrignore
10 | .hg/
11 | .hgignore
12 | .svn/
13 | # Common backup files
14 | *.swp
15 | *.bak
16 | *.tmp
17 | *~
18 | # Various IDEs
19 | .project
20 | .idea/
21 | *.tmproj
22 | environments/
--------------------------------------------------------------------------------
/charts/thread-weaver-flyway/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | description: flyway job for thread-weaver
3 | name: thread-weaver-flyway
4 | version: 1.0.0
5 |
--------------------------------------------------------------------------------
/charts/thread-weaver-flyway/environments/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | prod-values.yaml
--------------------------------------------------------------------------------
/charts/thread-weaver-flyway/environments/local-values.yaml:
--------------------------------------------------------------------------------
1 | image:
2 | repository: j5ik2o/thread-weaver-flyway
3 | tag: latest
4 | pullPolicy: IfNotPresent
5 | secrets:
6 | flyway: |
7 | flyway.driver=com.mysql.jdbc.Driver
8 | flyway.url=jdbc:mysql://mysql:3306/tw?useSSL=false
9 | flyway.user=tw
10 | flyway.password=passwd
11 |
--------------------------------------------------------------------------------
/charts/thread-weaver-flyway/environments/prod-values.yaml.tpl:
--------------------------------------------------------------------------------
1 | image:
2 | repository: ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/j5ik2o/thread-weaver-flyway
3 | tag: latest
4 | pullPolicy: Always
5 | secrets:
6 | flyway: |
7 | flyway.driver=com.mysql.jdbc.Driver
8 | flyway.url=jdbc:mysql://${FLYWAY_HOST}:${FLYWAY_PORT}/${FLYWAY_DB}?useSSL=false
9 | flyway.user=${FLYWAY_USER}
10 | flyway.password=${FLYWAY_PASSWORD}
11 |
--------------------------------------------------------------------------------
/charts/thread-weaver-flyway/templates/_helpers.tpl:
--------------------------------------------------------------------------------
1 | {{/* vim: set filetype=mustache: */}}
2 | {{/*
3 | Expand the name of the chart.
4 | */}}
5 | {{- define "name" -}}
6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
7 | {{- end -}}
8 |
9 | {{/*
10 | Create a default fully qualified app name.
11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
12 | */}}
13 | {{- define "fullname" -}}
14 | {{- $name := default .Chart.Name .Values.nameOverride -}}
15 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
16 | {{- end -}}
17 |
--------------------------------------------------------------------------------
/charts/thread-weaver-flyway/templates/job.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: batch/v1
2 | kind: Job
3 | metadata:
4 | name: {{ template "name" . }}
5 | namespace: thread-weaver
6 | labels:
7 | app: {{ template "name" . }}
8 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
9 | release: {{ .Release.Name }}
10 | heritage: {{ .Release.Service }}
11 | spec:
12 | completions: 1
13 | parallelism: 1
14 | backoffLimit: 1
15 | template:
16 | spec:
17 | containers:
18 | - name: {{ template "name" . }}
19 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
20 | imagePullPolicy: {{ .Values.image.pullPolicy }}
21 | workingDir: /flyway
22 | command: ["/flyway/flyway"]
23 | args: ["migrate"]
24 | volumeMounts:
25 | - name: {{ template "name" . }}
26 | mountPath: "/flyway/conf/"
27 | readOnly: true
28 | restartPolicy: Never
29 | volumes:
30 | - name: {{ template "name" . }}
31 | secret:
32 | secretName: {{ template "name" . }}
33 | items:
34 | - key: flyway.conf
35 | path: flyway.conf
--------------------------------------------------------------------------------
/charts/thread-weaver-flyway/templates/secrets.yaml:
--------------------------------------------------------------------------------
1 | {{- $root := . -}}
2 | {{- $secret := .Values.secrets.flyway -}}
3 | apiVersion: v1
4 | kind: Secret
5 | metadata:
6 | name: {{ template "name" . }}
7 | namespace: thread-weaver
8 | labels:
9 | app: {{ template "name" . }}
10 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
11 | release: {{ .Release.Name }}
12 | heritage: {{ .Release.Service }}
13 | type: Opaque
14 | data:
15 | flyway.conf: {{ tpl (print $secret) $root | b64enc | quote }}
16 |
--------------------------------------------------------------------------------
/charts/thread-weaver-flyway/values.yaml:
--------------------------------------------------------------------------------
1 | image:
2 | repository: XXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/j5ik2o/thread-weaver-flyway
3 | tag: latest
4 | pullPolicy: Always
5 | secrets:
6 | flyway: |
7 | flyway.driver=com.mysql.jdbc.Driver
8 | flyway.url=jdbc:mysql://j5ik2o-rds-cluster-aurora.cluster-ctywrcabnmgr.ap-northeast-1.rds.amazonaws.com:3306/thread_weaver?useSSL=false
9 | flyway.user=root
10 | flyway.password=
--------------------------------------------------------------------------------
/contracts/contract-grpc-proto-interface/src/main/protobuf/thread/command.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | option java_multiple_files = true;
4 | option java_package = "com.github.j5ik2o.threadWeaver.adaptor.grpc.service";
5 | option java_outer_classname = "ThreadCommandProto";
6 |
7 | package thread;
8 |
9 | import "thread/model.proto";
10 |
11 | service ThreadCommandService {
12 | rpc CreateThread (CreateThreadRequest) returns (CreateThreadResponse) {
13 | }
14 | rpc DestroyThread (DestroyThreadRequest) returns (DestroyThreadResponse) {
15 | }
16 | rpc JoinAdministratorIds (JoinAdministratorIdsRequest) returns (JoinAdministratorIdsResponse) {
17 | }
18 | rpc LeaveAdministratorIds (LeaveAdministratorIdsRequest) returns (LeaveAdministratorIdsResponse) {
19 | }
20 | rpc JoinMemberIds (JoinMemberIdsRequest) returns (JoinMemberIdsResponse) {
21 | }
22 | rpc LeaveMemberIds (LeaveMemberIdsRequest) returns (LeaveMemberIdsResponse) {
23 | }
24 | rpc AddMessages (AddMessagesRequest) returns (AddMessagesResponse) {
25 | }
26 | rpc RemoveMessages (RemoveMessagesRequest) returns (RemoveMessagesResponse) {
27 | }
28 | }
29 |
30 |
31 |
--------------------------------------------------------------------------------
/contracts/contract-grpc-proto-interface/src/main/protobuf/thread/query.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | option java_multiple_files = true;
4 | option java_package = "com.github.j5ik2o.threadWeaver.adaptor.grpc.service";
5 | option java_outer_classname = "ThreadQueryProto";
6 |
7 | package thread;
8 |
9 | import "thread/model.proto";
10 |
11 | service ThreadQueryService {
12 | rpc GetThread (GetThreadRequest) returns (GetThreadResponse) {}
13 | rpc GetThreads (GetThreadsRequest) returns (GetThreadsResponse) {}
14 | rpc GetMessages (GetMessagesRequest) returns (GetMessagesResponse) {}
15 | }
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/AddMessagesRequestJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class AddMessagesRequestJson(accountId: String, messages: Seq[TextMessage], createAt: Long)
4 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/AddMessagesResponseJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class AddMessagesResponseJson(messageIds: Seq[String], error_messages: Seq[String] = Seq.empty)
4 | extends ResponseJson
5 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/CreateThreadRequestJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class CreateThreadRequestJson(
4 | accountId: String,
5 | parentThreadId: Option[String],
6 | title: String,
7 | remarks: Option[String],
8 | administratorIds: Seq[String],
9 | memberIds: Seq[String],
10 | createAt: Long
11 | )
12 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/CreateThreadResponseJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class CreateThreadResponseJson(thread_id: Option[String], error_messages: Seq[String] = Seq.empty)
4 | extends ResponseJson
5 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/DestroyThreadRequestJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class DestroyThreadRequestJson(accountId: String, createAt: Long)
4 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/DestroyThreadResponseJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class DestroyThreadResponseJson(threadId: Option[String], error_messages: Seq[String] = Seq.empty)
4 | extends ResponseJson
5 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/ErrorsResponseJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class ErrorsResponseJson(error_messages: Seq[String]) extends ResponseJson
4 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/GetThreadAdministratorIdsResponseJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class GetThreadAdministratorIdsResponseJson(results: Seq[String], error_messages: Seq[String] = Seq.empty)
4 | extends ResponseJson
5 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/GetThreadMemberIdsResponseJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class GetThreadMemberIdsResponseJson(results: Seq[String], error_messages: Seq[String] = Seq.empty)
4 | extends ResponseJson
5 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/GetThreadMessagesResponseJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class GetThreadMessagesResponseJson(result: Seq[ThreadMessageJson], error_messages: Seq[String] = Seq.empty)
4 | extends ResponseJson
5 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/GetThreadResponseJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class GetThreadResponseJson(result: ThreadJson, error_messages: Seq[String] = Seq.empty) extends ResponseJson
4 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/GetThreadsResponseJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class GetThreadsResponseJson(results: Seq[ThreadJson], error_messages: Seq[String] = Seq.empty)
4 | extends ResponseJson
5 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/JoinAdministratorIdsRequestJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class JoinAdministratorIdsRequestJson(accountId: String, accountIds: Seq[String], createAt: Long)
4 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/JoinAdministratorIdsResponseJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class JoinAdministratorIdsResponseJson(threadId: Option[String], error_messages: Seq[String] = Seq.empty)
4 | extends ResponseJson
5 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/JoinMemberIdsRequestJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class JoinMemberIdsRequestJson(accountId: String, accountIds: Seq[String], createAt: Long)
4 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/JoinMemberIdsResponseJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class JoinMemberIdsResponseJson(threadId: Option[String], error_messages: Seq[String] = Seq.empty)
4 | extends ResponseJson
5 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/LeaveAdministratorIdsRequestJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class LeaveAdministratorIdsRequestJson(accountId: String, administratorIds: Seq[String], createAt: Long)
4 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/LeaveAdministratorIdsResponseJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class LeaveAdministratorIdsResponseJson(threadId: Option[String], error_messages: Seq[String] = Seq.empty)
4 | extends ResponseJson
5 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/LeaveMemberIdsRequestJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class LeaveMemberIdsRequestJson(accountId: String, accountIds: Seq[String], createAt: Long)
4 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/LeaveMemberIdsResponseJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class LeaveMemberIdsResponseJson(threadId: Option[String], error_messages: Seq[String] = Seq.empty)
4 | extends ResponseJson
5 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/RemoveMessagesRequestJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class RemoveMessagesRequestJson(accountId: String, messageIds: Seq[String], createAt: Long)
4 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/RemoveMessagesResponseJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class RemoveMessagesResponseJson(messageIds: Seq[String], error_messages: Seq[String] = Seq.empty)
4 | extends ResponseJson
5 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/ResponseJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | trait ResponseJson {
4 | def error_messages: Seq[String]
5 | def isSuccessful: Boolean = error_messages.isEmpty
6 | }
7 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/TextMessage.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class TextMessage(replyMessageId: Option[String], toAccountIds: Seq[String], text: String)
4 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/ThreadJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class ThreadJson(
4 | id: String,
5 | creatorId: String,
6 | parentThreadId: Option[String],
7 | title: String,
8 | remarks: Option[String],
9 | createdAt: Long,
10 | updatedAt: Long
11 | )
12 |
--------------------------------------------------------------------------------
/contracts/contract-http-proto-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/json/ThreadMessageJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.json
2 |
3 | final case class ThreadMessageJson(
4 | id: String,
5 | threadId: String,
6 | senderId: String,
7 | `type`: String,
8 | body: String,
9 | createdAt: Long,
10 | updatedAt: Long
11 | )
12 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/aggregates/BaseCommandRequest.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.aggregates
2 |
3 | trait BaseCommandRequest
4 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/aggregates/ThreadCommonProtocol.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.aggregates
2 |
3 | import java.time.Instant
4 |
5 | import com.github.j5ik2o.threadWeaver.domain.model.threads.ThreadId
6 | import com.github.j5ik2o.threadWeaver.infrastructure.ulid.ULID
7 |
8 | object ThreadCommonProtocol {
9 | trait Message
10 |
11 | trait Event extends Message {
12 | def id: ULID
13 | def threadId: ThreadId
14 | def createdAt: Instant
15 | }
16 |
17 | case class Started(id: ULID, threadId: ThreadId, createdAt: Instant) extends Event
18 |
19 | case class Stopped(id: ULID, threadId: ThreadId, createdAt: Instant) extends Event
20 | }
21 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/dao/ThreadAdministratorIdsRecord.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.dao
2 |
3 | trait ThreadAdministratorIdsRecord {
4 | def id: String
5 | def threadId: String
6 | def accountId: String
7 | def adderId: String
8 | def createdAt: java.time.Instant
9 | def updatedAt: java.time.Instant
10 | }
11 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/dao/ThreadMemberIdsRecord.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.dao
2 |
3 | trait ThreadMemberIdsRecord {
4 | def id: String
5 | def threadId: String
6 | def accountId: String
7 | def adderId: String
8 | def createdAt: java.time.Instant
9 | def updatedAt: java.time.Instant
10 | }
11 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/dao/ThreadMessageRecord.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.dao
2 |
3 | trait ThreadMessageRecord {
4 | def id: String
5 | def deleted: Boolean
6 | def threadId: String
7 | def senderId: String
8 | def `type`: String
9 | def body: String
10 | def createdAt: java.time.Instant
11 | def updatedAt: java.time.Instant
12 | }
13 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/dao/ThreadRecord.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.dao
2 |
3 | trait ThreadRecord {
4 | def id: String
5 | def deleted: Boolean
6 | def sequenceNr: Long
7 | def creatorId: String
8 | def parentId: Option[String]
9 | def title: String
10 | def remarks: Option[String]
11 | def createdAt: java.time.Instant
12 | def updatedAt: java.time.Instant
13 | def removedAt: Option[java.time.Instant]
14 | }
15 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/grpc/presenter/AddMessagesPresenter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.grpc.presenter
2 |
3 | import com.github.j5ik2o.threadWeaver.adaptor.presenter.Presenter
4 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.AddMessagesResponse
5 | import com.github.j5ik2o.threadWeaver.adaptor.grpc.model.{ AddMessagesResponse => AddMessagesGrpcResponse }
6 |
7 | trait AddMessagesPresenter extends Presenter[AddMessagesResponse, AddMessagesGrpcResponse]
8 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/grpc/presenter/CreateThreadPresenter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.grpc.presenter
2 |
3 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.CreateThreadResponse
4 | import com.github.j5ik2o.threadWeaver.adaptor.grpc.model.{ CreateThreadResponse => CreateThreadGrpcResponse }
5 | import com.github.j5ik2o.threadWeaver.adaptor.presenter.Presenter
6 |
7 | trait CreateThreadPresenter extends Presenter[CreateThreadResponse, CreateThreadGrpcResponse]
8 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/grpc/presenter/DestroyThreadPresenter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.grpc.presenter
2 |
3 | import com.github.j5ik2o.threadWeaver.adaptor.grpc.model.{ DestroyThreadResponse => DestroyThreadGrpcResponse }
4 | import com.github.j5ik2o.threadWeaver.adaptor.presenter.Presenter
5 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.DestroyThreadResponse
6 |
7 | trait DestroyThreadPresenter extends Presenter[DestroyThreadResponse, DestroyThreadGrpcResponse]
8 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/grpc/presenter/JoinAdministratorIdsPresenter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.grpc.presenter
2 |
3 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.JoinAdministratorIdsResponse
4 | import com.github.j5ik2o.threadWeaver.adaptor.grpc.model.{
5 | JoinAdministratorIdsResponse => JoinAdministratorIdsGrpcResponse
6 | }
7 | import com.github.j5ik2o.threadWeaver.adaptor.presenter.Presenter
8 |
9 | trait JoinAdministratorIdsPresenter extends Presenter[JoinAdministratorIdsResponse, JoinAdministratorIdsGrpcResponse]
10 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/grpc/presenter/JoinMemberIdsPresenter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.grpc.presenter
2 |
3 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.JoinMemberIdsResponse
4 | import com.github.j5ik2o.threadWeaver.adaptor.grpc.model.{ JoinMemberIdsResponse => JoinMemberIdsGrpcResponse }
5 | import com.github.j5ik2o.threadWeaver.adaptor.presenter.Presenter
6 |
7 | trait JoinMemberIdsPresenter extends Presenter[JoinMemberIdsResponse, JoinMemberIdsGrpcResponse]
8 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/grpc/presenter/LeaveAdministratorIdsPresenter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.grpc.presenter
2 |
3 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.LeaveAdministratorIdsResponse
4 | import com.github.j5ik2o.threadWeaver.adaptor.grpc.model.{
5 | LeaveAdministratorIdsResponse => LeaveAdministratorIdsGrpcResponse
6 | }
7 | import com.github.j5ik2o.threadWeaver.adaptor.presenter.Presenter
8 |
9 | trait LeaveAdministratorIdsPresenter extends Presenter[LeaveAdministratorIdsResponse, LeaveAdministratorIdsGrpcResponse]
10 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/grpc/presenter/LeaveMemberIdsPresenter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.grpc.presenter
2 |
3 | import com.github.j5ik2o.threadWeaver.adaptor.grpc.model.{ LeaveMemberIdsResponse => LeaveMemberIdsGrpcResponse }
4 | import com.github.j5ik2o.threadWeaver.adaptor.presenter.Presenter
5 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.LeaveMemberIdsResponse
6 |
7 | trait LeaveMemberIdsPresenter extends Presenter[LeaveMemberIdsResponse, LeaveMemberIdsGrpcResponse]
8 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/grpc/presenter/RemoveMessagesPresenter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.grpc.presenter
2 |
3 | import com.github.j5ik2o.threadWeaver.adaptor.grpc.model.{ RemoveMessagesResponse => RemoveMessagesGrpcResponse }
4 | import com.github.j5ik2o.threadWeaver.adaptor.presenter.Presenter
5 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.RemoveMessagesResponse
6 |
7 | trait RemoveMessagesPresenter extends Presenter[RemoveMessagesResponse, RemoveMessagesGrpcResponse]
8 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/AddMessagesPresenter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 |
3 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.AddMessagesResponseJson
4 | import com.github.j5ik2o.threadWeaver.adaptor.presenter.Presenter
5 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.AddMessagesResponse
6 |
7 | trait AddMessagesPresenter extends Presenter[AddMessagesResponse, AddMessagesResponseJson]
8 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/CreateThreadPresenter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 |
3 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.CreateThreadResponseJson
4 | import com.github.j5ik2o.threadWeaver.adaptor.presenter.Presenter
5 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.CreateThreadResponse
6 |
7 | trait CreateThreadPresenter extends Presenter[CreateThreadResponse, CreateThreadResponseJson]
8 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/DestroyThreadPresenter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 |
3 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.DestroyThreadResponseJson
4 | import com.github.j5ik2o.threadWeaver.adaptor.presenter.Presenter
5 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.DestroyThreadResponse
6 |
7 | trait DestroyThreadPresenter extends Presenter[DestroyThreadResponse, DestroyThreadResponseJson]
8 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/JoinAdministratorIdsPresenter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 |
3 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.JoinAdministratorIdsResponseJson
4 | import com.github.j5ik2o.threadWeaver.adaptor.presenter.Presenter
5 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.JoinAdministratorIdsResponse
6 |
7 | trait JoinAdministratorIdsPresenter extends Presenter[JoinAdministratorIdsResponse, JoinAdministratorIdsResponseJson]
8 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/JoinMemberIdsPresenter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 |
3 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.JoinMemberIdsResponseJson
4 | import com.github.j5ik2o.threadWeaver.adaptor.presenter.Presenter
5 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.JoinMemberIdsResponse
6 |
7 | trait JoinMemberIdsPresenter extends Presenter[JoinMemberIdsResponse, JoinMemberIdsResponseJson]
8 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/LeaveAdministratorIdsPresenter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 |
3 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.LeaveAdministratorIdsResponseJson
4 | import com.github.j5ik2o.threadWeaver.adaptor.presenter.Presenter
5 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.LeaveAdministratorIdsResponse
6 |
7 | trait LeaveAdministratorIdsPresenter extends Presenter[LeaveAdministratorIdsResponse, LeaveAdministratorIdsResponseJson]
8 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/LeaveMemberIdsPresenter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 |
3 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.LeaveMemberIdsResponseJson
4 | import com.github.j5ik2o.threadWeaver.adaptor.presenter.Presenter
5 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.LeaveMemberIdsResponse
6 |
7 | trait LeaveMemberIdsPresenter extends Presenter[LeaveMemberIdsResponse, LeaveMemberIdsResponseJson]
8 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/RemoveMessagesPresenter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 |
3 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.RemoveMessagesResponseJson
4 | import com.github.j5ik2o.threadWeaver.adaptor.presenter.Presenter
5 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.RemoveMessagesResponse
6 |
7 | trait RemoveMessagesPresenter extends Presenter[RemoveMessagesResponse, RemoveMessagesResponseJson]
8 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/ThreadMessagePresenter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 |
3 | import com.github.j5ik2o.threadWeaver.adaptor.dao.ThreadMessageRecord
4 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.ThreadMessageJson
5 | import com.github.j5ik2o.threadWeaver.adaptor.presenter.Presenter
6 |
7 | trait ThreadMessagePresenter extends Presenter[ThreadMessageRecord, ThreadMessageJson]
8 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/ThreadPresenter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 |
3 | import com.github.j5ik2o.threadWeaver.adaptor.dao.ThreadRecord
4 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.ThreadJson
5 | import com.github.j5ik2o.threadWeaver.adaptor.presenter.Presenter
6 |
7 | trait ThreadPresenter extends Presenter[ThreadRecord, ThreadJson]
8 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/routes/RouteNames.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.routes
2 |
3 | object RouteNames {
4 | final val Base: String = "threads"
5 | final val CreateThread: String = s"/$Base/create"
6 | final def DestroyThread(threadId: String): String = s"/$Base/$threadId/destroy"
7 | final def JoinAdministratorIds(threadId: String, accountId: String): String =
8 | s"/$Base/$threadId/administrator-ids/join"
9 | final def LeaveAdministratorIds(threadId: String): String = s"/$Base/$threadId/administrator-ids/leave"
10 |
11 | final def JoinMemberIds(threadId: String): String = s"/$Base/$threadId/member-ids/join"
12 | final def LeaveMemberIds(threadId: String): String = s"/$Base/$threadId/member-ids/leave"
13 |
14 | final def AddMessages(threadId: String): String = s"/$Base/$threadId/messages/add"
15 | final def RemoveMessages(threadId: String): String = s"/$Base/$threadId/messages/remove"
16 |
17 | final def GetAdministratorIds(threadId: String, accountId: String): String =
18 | s"/$Base/$threadId/administrator-ids?account_id=$accountId"
19 | final def GetMemberIds(threadId: String, accountId: String): String =
20 | s"/$Base/$threadId/member-ids?account_id=$accountId"
21 |
22 | final def GetMessages(threadId: String, accountId: String): String =
23 | s"/$Base/$threadId/messages?account_id=$accountId"
24 | final def GetThreads(accountId: String): String = s"/$Base?account_id=$accountId"
25 | final def GetThread(threadId: String, accountId: String): String = s"/$Base/$threadId?account_id=$accountId"
26 | }
27 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/presenter/Presenter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.presenter
2 |
3 | import akka.NotUsed
4 | import akka.stream.scaladsl.Flow
5 |
6 | trait Presenter[Res, ResRepr] {
7 | def response: Flow[Res, ResRepr, NotUsed]
8 | }
9 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/readModelUpdater/ThreadReadModelUpdaterProtocol.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.readModelUpdater
2 |
3 | import java.time.Instant
4 |
5 | import com.github.j5ik2o.threadWeaver.infrastructure.ulid.ULID
6 |
7 | object ThreadReadModelUpdaterProtocol {
8 | sealed trait Message
9 |
10 | sealed trait CommandMessage extends Message {
11 | def id: ULID
12 | def createAt: Instant
13 | }
14 | sealed trait CommandRequest extends CommandMessage {
15 | def threadTag: ThreadTag
16 | }
17 |
18 | sealed trait CommandResponse extends CommandMessage {
19 | def requestId: ULID
20 | }
21 |
22 | final case class Start(id: ULID, threadTag: ThreadTag, createAt: Instant) extends CommandRequest
23 | final case class Stop(id: ULID, threadTag: ThreadTag, createAt: Instant) extends CommandRequest
24 |
25 | case object Idle extends CommandRequest {
26 | override def id: ULID = throw new UnsupportedOperationException
27 | override def threadTag: ThreadTag = throw new UnsupportedOperationException
28 | override def createAt: Instant = throw new UnsupportedOperationException
29 | }
30 |
31 | case object Stop extends CommandRequest {
32 | override def id: ULID = throw new UnsupportedOperationException
33 | override def threadTag: ThreadTag = throw new UnsupportedOperationException
34 | override def createAt: Instant = throw new UnsupportedOperationException
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/readModelUpdater/ThreadReadModelUpdaterSettings.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.readModelUpdater
2 |
3 | import com.typesafe.config.Config
4 |
5 | case class ThreadReadModelUpdaterSettings(category: String, numPartition: Int, shardName: String)
6 |
7 | object ThreadReadModelUpdaterSettings {
8 |
9 | def fromConfig(config: Config): ThreadReadModelUpdaterSettings = new ThreadReadModelUpdaterSettings(
10 | category = config.getString("thread-weaver.read-model-updater.thread.category"),
11 | numPartition = config.getInt("thread-weaver.read-model-updater.thread.num-partition"),
12 | shardName = config.getString("thread-weaver.read-model-updater.thread.shard-name")
13 | )
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/contracts/contract-interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/readModelUpdater/ThreadTag.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.readModelUpdater
2 |
3 | import com.github.j5ik2o.threadWeaver.domain.model.threads.ThreadId
4 | import com.typesafe.config.Config
5 |
6 | import scala.util.hashing.MurmurHash3
7 |
8 | case class ThreadTag(value: String)
9 |
10 | object ThreadTag {
11 |
12 | def fromThreadId(threadId: ThreadId)(implicit config: Config): ThreadTag = {
13 | val settings = ThreadReadModelUpdaterSettings.fromConfig(config)
14 | val num = Math.abs(MurmurHash3.stringHash(threadId.value.asString)) % settings.numPartition
15 | ThreadTag(s"thread-$num")
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/contracts/contract-use-case/src/main/scala/com/github/j5ik2o/threadWeaver/useCase/AddMessagesUseCase.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.useCase
2 |
3 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.{ AddMessages, AddMessagesResponse }
4 |
5 | trait AddMessagesUseCase extends ThreadWeaverUseCase[AddMessages, AddMessagesResponse]
6 |
--------------------------------------------------------------------------------
/contracts/contract-use-case/src/main/scala/com/github/j5ik2o/threadWeaver/useCase/CreateThreadUseCase.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.useCase
2 |
3 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.{ CreateThread, CreateThreadResponse }
4 |
5 | trait CreateThreadUseCase extends ThreadWeaverUseCase[CreateThread, CreateThreadResponse]
6 |
--------------------------------------------------------------------------------
/contracts/contract-use-case/src/main/scala/com/github/j5ik2o/threadWeaver/useCase/DestroyThreadUseCase.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.useCase
2 |
3 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.{ DestroyThread, DestroyThreadResponse }
4 |
5 | trait DestroyThreadUseCase extends ThreadWeaverUseCase[DestroyThread, DestroyThreadResponse]
6 |
--------------------------------------------------------------------------------
/contracts/contract-use-case/src/main/scala/com/github/j5ik2o/threadWeaver/useCase/JoinAdministratorIdsUseCase.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.useCase
2 |
3 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.{
4 | JoinAdministratorIds,
5 | JoinAdministratorIdsResponse
6 | }
7 |
8 | trait JoinAdministratorIdsUseCase extends ThreadWeaverUseCase[JoinAdministratorIds, JoinAdministratorIdsResponse]
9 |
--------------------------------------------------------------------------------
/contracts/contract-use-case/src/main/scala/com/github/j5ik2o/threadWeaver/useCase/JoinMemberIdsUseCase.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.useCase
2 |
3 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.{ JoinMemberIds, JoinMemberIdsResponse }
4 |
5 | trait JoinMemberIdsUseCase extends ThreadWeaverUseCase[JoinMemberIds, JoinMemberIdsResponse]
6 |
--------------------------------------------------------------------------------
/contracts/contract-use-case/src/main/scala/com/github/j5ik2o/threadWeaver/useCase/LeaveAdministratorIdsUseCase.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.useCase
2 |
3 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.{
4 | LeaveAdministratorIds,
5 | LeaveAdministratorIdsResponse
6 | }
7 |
8 | trait LeaveAdministratorIdsUseCase extends ThreadWeaverUseCase[LeaveAdministratorIds, LeaveAdministratorIdsResponse]
9 |
--------------------------------------------------------------------------------
/contracts/contract-use-case/src/main/scala/com/github/j5ik2o/threadWeaver/useCase/LeaveMemberIdsUseCase.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.useCase
2 |
3 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.{ LeaveMemberIds, LeaveMemberIdsResponse }
4 |
5 | trait LeaveMemberIdsUseCase extends ThreadWeaverUseCase[LeaveMemberIds, LeaveMemberIdsResponse]
6 |
--------------------------------------------------------------------------------
/contracts/contract-use-case/src/main/scala/com/github/j5ik2o/threadWeaver/useCase/RemoveMessagesUseCase.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.useCase
2 |
3 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.{ RemoveMessages, RemoveMessagesResponse }
4 |
5 | trait RemoveMessagesUseCase extends ThreadWeaverUseCase[RemoveMessages, RemoveMessagesResponse]
6 |
--------------------------------------------------------------------------------
/contracts/contract-use-case/src/main/scala/com/github/j5ik2o/threadWeaver/useCase/ThreadWeaverUseCase.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.useCase
2 |
3 | import akka.NotUsed
4 | import akka.stream.scaladsl.Flow
5 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.{ ThreadWeaverRequest, ThreadWeaverResponse }
6 |
7 | trait ThreadWeaverUseCase[Req <: ThreadWeaverRequest, Res <: ThreadWeaverResponse] {
8 | def execute: Flow[Req, Res, NotUsed]
9 | }
10 |
--------------------------------------------------------------------------------
/modules/domain/src/main/scala/com/github/j5ik2o/threadWeaver/domain/model/accounts/Account.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.domain.model.accounts
2 |
3 | import java.time.Instant
4 |
5 | final case class Account(id: AccountId, name: AccountName, createdAt: Instant, updatedAt: Instant) {
6 |
7 | def withName(value: AccountName): Account = copy(name = value)
8 |
9 | def withUpdatedAt(value: Instant): Account = copy(updatedAt = value)
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/modules/domain/src/main/scala/com/github/j5ik2o/threadWeaver/domain/model/accounts/AccountId.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.domain.model.accounts
2 |
3 | import com.github.j5ik2o.threadWeaver.infrastructure.ulid.ULID
4 |
5 | final case class AccountId(value: ULID = ULID())
6 |
--------------------------------------------------------------------------------
/modules/domain/src/main/scala/com/github/j5ik2o/threadWeaver/domain/model/accounts/AccountName.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.domain.model.accounts
2 |
3 | import cats.implicits._
4 | import com.github.j5ik2o.threadWeaver.domain.model.accounts.AccountName.{ DomainError, FormatError }
5 | import eu.timepit.refined.W
6 | import eu.timepit.refined.api.RefType.applyRef
7 | import eu.timepit.refined.api.Refined
8 | import eu.timepit.refined.boolean.And
9 | import eu.timepit.refined.collection.Size
10 | import eu.timepit.refined.numeric.Interval
11 | import eu.timepit.refined.string.MatchesRegex
12 |
13 | object AccountName {
14 |
15 | type AsString =
16 | String Refined And[MatchesRegex[W.`"[a-z][a-zA-Z0-9]+"`.T], Size[Interval.Closed[W.`1`.T, W.`255`.T]]]
17 |
18 | sealed trait DomainError
19 |
20 | final case class FormatError(message: String) extends DomainError
21 |
22 | }
23 |
24 | final case class AccountName(breachEncapsulationOfValue: AccountName.AsString) {
25 |
26 | def withSuffix(suffix: String): Either[DomainError, AccountName] = {
27 | applyRef[AccountName.AsString](breachEncapsulationOfValue + suffix)
28 | .leftMap[DomainError](FormatError).map(new AccountName(_))
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/modules/domain/src/main/scala/com/github/j5ik2o/threadWeaver/domain/model/threads/AdministratorIds.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.domain.model.threads
2 |
3 | import cats.Semigroup
4 | import cats.data.NonEmptyList
5 | import com.github.j5ik2o.threadWeaver.domain.model.accounts.AccountId
6 |
7 | final case class AdministratorIds(breachEncapsulationOfValues: NonEmptyList[AccountId]) {
8 |
9 | def contains(value: AccountId): Boolean =
10 | breachEncapsulationOfValues.toList.contains(value)
11 |
12 | def filterNot(other: AdministratorIds): Either[Exception, AdministratorIds] = {
13 | val list = breachEncapsulationOfValues.filterNot(p => other.contains(p))
14 | if (list.isEmpty)
15 | Left(new IllegalArgumentException("Administrators can not be empty."))
16 | else
17 | Right(AdministratorIds(list.head, list.tail: _*))
18 | }
19 |
20 | def valuesAsString: Seq[String] = breachEncapsulationOfValues.toList.map(_.value.asString)
21 |
22 | }
23 |
24 | object AdministratorIds {
25 |
26 | def apply(head: AccountId, tail: List[AccountId]): AdministratorIds =
27 | new AdministratorIds(NonEmptyList.of(head, tail: _*))
28 |
29 | def apply(head: AccountId, tail: AccountId*): AdministratorIds =
30 | new AdministratorIds(NonEmptyList.of(head, tail: _*))
31 |
32 | implicit object MessagesSemigroup extends Semigroup[AdministratorIds] {
33 |
34 | override def combine(x: AdministratorIds, y: AdministratorIds): AdministratorIds =
35 | AdministratorIds(x.breachEncapsulationOfValues ::: y.breachEncapsulationOfValues)
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/modules/domain/src/main/scala/com/github/j5ik2o/threadWeaver/domain/model/threads/MemberIds.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.domain.model.threads
2 |
3 | import cats.Monoid
4 | import com.github.j5ik2o.threadWeaver.domain.model.accounts.AccountId
5 |
6 | final case class MemberIds(breachEncapsulationOfValues: Vector[AccountId]) {
7 |
8 | def contains(value: AccountId): Boolean =
9 | breachEncapsulationOfValues.contains(value)
10 |
11 | def filterNot(other: MemberIds): MemberIds = {
12 | val list = breachEncapsulationOfValues.filterNot(p => other.contains(p))
13 | MemberIds(list: _*)
14 | }
15 |
16 | def valuesAsString: Seq[String] = breachEncapsulationOfValues.map(_.value.asString)
17 |
18 | }
19 |
20 | object MemberIds {
21 |
22 | def apply(values: AccountId*): MemberIds = new MemberIds(Vector(values: _*))
23 |
24 | val empty = MemberIds(Vector.empty)
25 |
26 | implicit val memberIdsMonoid: Monoid[MemberIds] = new Monoid[MemberIds] {
27 | override def empty: MemberIds = MemberIds.empty
28 |
29 | override def combine(x: MemberIds, y: MemberIds): MemberIds =
30 | MemberIds(x.breachEncapsulationOfValues ++ y.breachEncapsulationOfValues)
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/modules/domain/src/main/scala/com/github/j5ik2o/threadWeaver/domain/model/threads/Message.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.domain.model.threads
2 |
3 | import java.time.Instant
4 |
5 | import com.github.j5ik2o.threadWeaver.domain.model.accounts.AccountId
6 |
7 | sealed trait Message[A] {
8 | def id: MessageId
9 |
10 | def `type`: String
11 |
12 | def body: A
13 |
14 | def senderId: AccountId
15 |
16 | def createdAt: Instant
17 |
18 | def updatedAt: Instant
19 | }
20 |
21 | final case class TextMessage(
22 | id: MessageId,
23 | replyMessageId: Option[MessageId],
24 | toAccountIds: ToAccountIds,
25 | body: Text,
26 | senderId: AccountId,
27 | createdAt: Instant,
28 | updatedAt: Instant
29 | ) extends Message[Text] {
30 | override def `type`: String = "text"
31 | }
32 |
--------------------------------------------------------------------------------
/modules/domain/src/main/scala/com/github/j5ik2o/threadWeaver/domain/model/threads/MessageId.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.domain.model.threads
2 |
3 | import com.github.j5ik2o.threadWeaver.infrastructure.ulid.ULID
4 |
5 | final case class MessageId(value: ULID = ULID())
6 |
--------------------------------------------------------------------------------
/modules/domain/src/main/scala/com/github/j5ik2o/threadWeaver/domain/model/threads/MessageIds.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.domain.model.threads
2 |
3 | import cats.Monoid
4 |
5 | final case class MessageIds(breachEncapsulationOfValues: Vector[MessageId]) {
6 |
7 | def contains(value: MessageId): Boolean =
8 | breachEncapsulationOfValues.contains(value)
9 |
10 | def diff(other: MessageIds): MessageIds =
11 | MessageIds(breachEncapsulationOfValues.diff(other.breachEncapsulationOfValues))
12 |
13 | def valuesAsString: Vector[String] = breachEncapsulationOfValues.map(_.value.asString)
14 |
15 | }
16 |
17 | object MessageIds {
18 |
19 | def apply(values: MessageId*): MessageIds = new MessageIds(Vector(values: _*))
20 |
21 | val empty = MessageIds(Vector.empty)
22 |
23 | implicit val memberIdsMonoid: Monoid[MessageIds] = new Monoid[MessageIds] {
24 | override def empty: MessageIds = MessageIds.empty
25 |
26 | override def combine(x: MessageIds, y: MessageIds): MessageIds =
27 | MessageIds(x.breachEncapsulationOfValues ++ y.breachEncapsulationOfValues)
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/modules/domain/src/main/scala/com/github/j5ik2o/threadWeaver/domain/model/threads/Messages.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.domain.model.threads
2 |
3 | import cats.Monoid
4 |
5 | final case class Messages(breachEncapsulationOfValues: Vector[Message[_]]) {
6 |
7 | def size: Int = breachEncapsulationOfValues.size
8 |
9 | def filter(messageIds: MessageIds): Messages =
10 | copy(
11 | breachEncapsulationOfValues = breachEncapsulationOfValues.filter(v => messageIds.contains(v.id))
12 | )
13 |
14 | def forall(condition: Message[_] => Boolean): Boolean =
15 | breachEncapsulationOfValues.forall(condition)
16 |
17 | def filterNot(messageIds: MessageIds): Messages = {
18 | copy(
19 | breachEncapsulationOfValues = breachEncapsulationOfValues.filterNot(v => messageIds.contains(v.id))
20 | )
21 | }
22 |
23 | def toMessageIds: MessageIds = MessageIds(breachEncapsulationOfValues.map(_.id): _*)
24 |
25 | }
26 |
27 | object Messages {
28 |
29 | def apply(values: Message[_]*): Messages = new Messages(Vector(values: _*))
30 |
31 | val empty = Messages(Vector.empty)
32 |
33 | implicit val messagesMonoid: Monoid[Messages] = new Monoid[Messages] {
34 | override def empty: Messages = Messages.empty
35 |
36 | override def combine(x: Messages, y: Messages): Messages =
37 | Messages(x.breachEncapsulationOfValues ++ y.breachEncapsulationOfValues)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/modules/domain/src/main/scala/com/github/j5ik2o/threadWeaver/domain/model/threads/Text.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.domain.model.threads
2 |
3 | final case class Text(value: String) {
4 | override def toString: String = value
5 | }
6 |
7 | object Text {
8 |
9 | def parseFrom(text: String): Either[Exception, Text] =
10 | if (text.size > 255)
11 | Left(new IllegalArgumentException("too long text"))
12 | else
13 | Right(Text(text))
14 | }
15 |
--------------------------------------------------------------------------------
/modules/domain/src/main/scala/com/github/j5ik2o/threadWeaver/domain/model/threads/ThreadId.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.domain.model.threads
2 |
3 | import com.github.j5ik2o.threadWeaver.infrastructure.ulid.ULID
4 |
5 | final case class ThreadId(value: ULID = ULID())
6 |
--------------------------------------------------------------------------------
/modules/domain/src/main/scala/com/github/j5ik2o/threadWeaver/domain/model/threads/ThreadRemarks.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.domain.model.threads
2 |
3 | final case class ThreadRemarks(value: String)
4 |
--------------------------------------------------------------------------------
/modules/domain/src/main/scala/com/github/j5ik2o/threadWeaver/domain/model/threads/ThreadTitle.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.domain.model.threads
2 |
3 | case class ThreadTitle(value: String)
4 |
--------------------------------------------------------------------------------
/modules/domain/src/main/scala/com/github/j5ik2o/threadWeaver/domain/model/threads/ToAccountIds.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.domain.model.threads
2 |
3 | import cats.Monoid
4 | import com.github.j5ik2o.threadWeaver.domain.model.accounts.AccountId
5 |
6 | final case class ToAccountIds(breachEncapsulationOfValues: Vector[AccountId])
7 |
8 | object ToAccountIds {
9 |
10 | def apply(values: AccountId*): ToAccountIds = new ToAccountIds(Vector(values: _*))
11 |
12 | val empty = ToAccountIds(Vector.empty)
13 |
14 | implicit val toAccountIdsMonoid: Monoid[ToAccountIds] = new Monoid[ToAccountIds] {
15 | override def empty: ToAccountIds = ToAccountIds.empty
16 |
17 | override def combine(x: ToAccountIds, y: ToAccountIds): ToAccountIds =
18 | ToAccountIds(x.breachEncapsulationOfValues ++ y.breachEncapsulationOfValues)
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/modules/domain/src/test/scala/com/github/j5ik2o/threadWeave/domain/model/accounts/AccountNameSpec.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeave.domain.model.accounts
2 |
3 | import com.github.j5ik2o.threadWeaver.domain.model.accounts.AccountName
4 | import eu.timepit.refined.auto._
5 | import org.scalatest.FreeSpec
6 |
7 | class AccountNameSpec extends FreeSpec {
8 | "AccountName" - {
9 | "check" in {
10 | val result = AccountName("abcdef").withSuffix("_2")
11 | println(result)
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/modules/infrastructure/src/main/scala/com/github/j5ik2o/threadWeaver/infrastructure/ulid/ULID.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.infrastructure.ulid
2 |
3 | import de.huxhorn.sulky.ulid.ULID.Value
4 | import de.huxhorn.sulky.ulid.{ ULID => ULIDGen }
5 | import eu.timepit.refined.api.{ Refined, Validate }
6 |
7 | import scala.util.Try
8 |
9 | object ULID {
10 |
11 | private val gen = new ULIDGen()
12 |
13 | def parseFromString(text: String): Try[ULID] = Try {
14 | apply(ULIDGen.parseULID(text))
15 | }
16 |
17 | case class ULIDType()
18 |
19 | def apply(value: Value = ULID.gen.nextValue()): ULID = new ULID(value)
20 |
21 | type ULIDStringType = String Refined ULIDType
22 |
23 | implicit def ulidValidate: Validate.Plain[String, ULIDType] =
24 | Validate.fromPartial(
25 | s => require(parseFromString(s).isSuccess),
26 | "ULID",
27 | ULIDType()
28 | )
29 | }
30 |
31 | final case class ULID private (private val value: Value) extends Ordered[ULID] {
32 |
33 | def timestamp: Long = value.timestamp()
34 | def increment: ULID = new ULID(value.increment())
35 |
36 | def mostSignificantBits: Long = value.getMostSignificantBits
37 | def leastSignificantBits: Long = value.getLeastSignificantBits
38 |
39 | def asString: String = value.toString
40 | def asBytes: Array[Byte] = value.toBytes
41 |
42 | override def compare(that: ULID): Int =
43 | value.compareTo(that.value)
44 |
45 | override def equals(other: Any): Boolean = other match {
46 | case that: ULID => value == that.value
47 | case _ => false
48 | }
49 |
50 | override def hashCode(): Int = {
51 | val state = Seq(value)
52 | state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b)
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/modules/interface/native-libs/libsqlite4java-linux-amd64-1.0.392.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/modules/interface/native-libs/libsqlite4java-linux-amd64-1.0.392.so
--------------------------------------------------------------------------------
/modules/interface/native-libs/libsqlite4java-linux-i386-1.0.392.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/modules/interface/native-libs/libsqlite4java-linux-i386-1.0.392.so
--------------------------------------------------------------------------------
/modules/interface/native-libs/libsqlite4java-osx-1.0.392.dylib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/modules/interface/native-libs/libsqlite4java-osx-1.0.392.dylib
--------------------------------------------------------------------------------
/modules/interface/native-libs/sqlite4java-win32-x64-1.0.392.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/modules/interface/native-libs/sqlite4java-win32-x64-1.0.392.dll
--------------------------------------------------------------------------------
/modules/interface/native-libs/sqlite4java-win32-x86-1.0.392.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/modules/interface/native-libs/sqlite4java-win32-x86-1.0.392.dll
--------------------------------------------------------------------------------
/modules/interface/src/main/resources/reference.conf:
--------------------------------------------------------------------------------
1 | akka {
2 | loglevel = DEBUG
3 | loggers = ["akka.event.slf4j.Slf4jLogger"]
4 | logging-filter = "akka.event.slf4j.Slf4jLoggingFilter"
5 | http.server.preview.enable-http2 = on
6 | }
7 |
8 | passivate-timeout = 60 seconds
9 |
10 |
--------------------------------------------------------------------------------
/modules/interface/src/main/resources/swagger/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/modules/interface/src/main/resources/swagger/favicon-16x16.png
--------------------------------------------------------------------------------
/modules/interface/src/main/resources/swagger/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/modules/interface/src/main/resources/swagger/favicon-32x32.png
--------------------------------------------------------------------------------
/modules/interface/src/main/resources/swagger/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Swagger UI
7 |
8 |
9 |
10 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/modules/interface/src/main/resources/swagger/swagger-ui.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"swagger-ui.css","sourceRoot":""}
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/aggregates/typed/ShardedThreadAggregatesProxy.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.aggregates.typed
2 |
3 | import akka.actor.typed.scaladsl.Behaviors
4 | import akka.actor.typed.{ ActorRef, Behavior }
5 | import akka.cluster.sharding.typed.ShardingEnvelope
6 | import akka.cluster.sharding.typed.scaladsl.ClusterSharding
7 | import com.github.j5ik2o.threadWeaver.adaptor.aggregates.ThreadCommonProtocol
8 | import com.github.j5ik2o.threadWeaver.adaptor.aggregates.typed.ThreadProtocol.CommandRequest
9 |
10 | import scala.concurrent.duration.FiniteDuration
11 |
12 | object ShardedThreadAggregatesProxy {
13 |
14 | def behavior(
15 | clusterSharding: ClusterSharding,
16 | receiveTimeout: FiniteDuration,
17 | subscribers: Seq[ActorRef[ThreadCommonProtocol.Message]]
18 | ): Behavior[CommandRequest] =
19 | Behaviors.setup[CommandRequest] { _ =>
20 | val actorRef = ShardedThreadAggregates.initEntityActor(clusterSharding, receiveTimeout, subscribers)
21 | Behaviors.receiveMessagePartial[CommandRequest] {
22 | case msg =>
23 | actorRef ! ShardingEnvelope(msg.threadId.value.asString, msg)
24 | Behaviors.same
25 | }
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/aggregates/typed/ThreadAggregates.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.aggregates.typed
2 |
3 | import akka.actor.typed.scaladsl.Behaviors
4 | import akka.actor.typed.{ ActorRef, Behavior }
5 | import com.github.j5ik2o.threadWeaver.adaptor.aggregates.ThreadCommonProtocol
6 | import com.github.j5ik2o.threadWeaver.adaptor.aggregates.typed.ThreadProtocol.CommandRequest
7 | import com.github.j5ik2o.threadWeaver.domain.model.threads.ThreadId
8 |
9 | object ThreadAggregates {
10 |
11 | val name = "threads"
12 |
13 | def behavior(
14 | subscribers: Seq[ActorRef[ThreadCommonProtocol.Message]],
15 | name: ThreadId => String
16 | )(
17 | behaviorF: (ThreadId, Seq[ActorRef[ThreadCommonProtocol.Message]]) => Behavior[CommandRequest]
18 | ): Behavior[CommandRequest] = {
19 | Behaviors.setup { ctx =>
20 | def createAndSend(threadId: ThreadId): ActorRef[CommandRequest] = {
21 | ctx.child(name(threadId)) match {
22 | case None => ctx.spawn(behaviorF(threadId, subscribers), name = name(threadId))
23 | case Some(ref) => ref.asInstanceOf[ActorRef[CommandRequest]]
24 | }
25 | }
26 | Behaviors.receiveMessage[CommandRequest] { msg =>
27 | createAndSend(msg.threadId) ! msg
28 | Behaviors.same
29 | }
30 | }
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/aggregates/untyped/ChildActorLookup.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.aggregates.untyped
2 |
3 | import akka.actor.{ Actor, ActorContext, ActorLogging, ActorRef, Props }
4 | import com.github.j5ik2o.threadWeaver.adaptor.aggregates.BaseCommandRequest
5 |
6 | trait ChildActorLookup extends ActorLogging { this: Actor =>
7 |
8 | implicit def context: ActorContext
9 |
10 | type ID
11 |
12 | protected def childName(childId: ID): String
13 | protected def childProps(childId: ID): Props
14 | protected def toChildId(commandRequest: BaseCommandRequest): ID
15 |
16 | protected def forwardToActor: Actor.Receive = {
17 | case cmd: BaseCommandRequest =>
18 | context
19 | .child(childName(toChildId(cmd)))
20 | .fold(createAndForward(cmd, toChildId(cmd)))(forwardCommand(cmd))
21 | }
22 |
23 | protected def forwardCommand(cmd: BaseCommandRequest)(childRef: ActorRef): Unit =
24 | childRef forward cmd
25 |
26 | protected def createAndForward(cmd: BaseCommandRequest, childId: ID): Unit = {
27 | createActor(childId) forward cmd
28 | }
29 |
30 | protected def createActor(childId: ID): ActorRef =
31 | context.actorOf(childProps(childId), childName(childId))
32 | }
33 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/aggregates/untyped/Settings.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.aggregates.untyped
2 |
3 | import scala.concurrent.duration._
4 |
5 | import akka.actor._
6 |
7 | import com.typesafe.config.Config
8 |
9 | object Settings extends ExtensionId[Settings] with ExtensionIdProvider {
10 | override def createExtension(system: ExtendedActorSystem): Settings = new Settings(system.settings.config)
11 |
12 | override def lookup(): ExtensionId[_ <: Extension] = Settings
13 | }
14 |
15 | class Settings(config: Config) extends Extension {
16 | def this(system: ExtendedActorSystem) = this(system.settings.config)
17 |
18 | val passivateTimeout = Duration(config.getString("passivate-timeout"))
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/aggregates/untyped/ShardedThreadAggregatesRegion.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.aggregates.untyped
2 |
3 | import akka.actor.{ ActorRef, ActorSystem }
4 | import akka.cluster.sharding.{ ClusterSharding, ClusterShardingSettings }
5 |
6 | object ShardedThreadAggregatesRegion {
7 |
8 | def startClusterSharding(nrOfShards: Int, subscribers: Seq[ActorRef])(
9 | implicit system: ActorSystem
10 | ): ActorRef =
11 | ClusterSharding(system).start(
12 | ShardedThreadAggregates.shardName,
13 | ShardedThreadAggregates.props(subscribers, PersistentThreadAggregate.props),
14 | ClusterShardingSettings(system),
15 | ShardedThreadAggregates.extractEntityId,
16 | ShardedThreadAggregates.extractShardId(nrOfShards)
17 | )
18 |
19 | def shardRegion(implicit system: ActorSystem): ActorRef =
20 | ClusterSharding(system).shardRegion(ShardedThreadAggregates.shardName)
21 | }
22 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/aggregates/untyped/ThreadAggregates.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.aggregates.untyped
2 |
3 | import akka.actor.{ Actor, ActorLogging, ActorRef, Props }
4 | import com.github.j5ik2o.threadWeaver.adaptor.aggregates.BaseCommandRequest
5 | import com.github.j5ik2o.threadWeaver.domain.model.threads.ThreadId
6 |
7 | object ThreadAggregates {
8 |
9 | val name = "threads"
10 |
11 | def props(subscribers: Seq[ActorRef], propsF: ThreadId => Seq[ActorRef] => Props): Props =
12 | Props(new ThreadAggregates(subscribers, propsF))
13 |
14 | }
15 |
16 | class ThreadAggregates(subscribers: Seq[ActorRef], propsF: ThreadId => Seq[ActorRef] => Props)
17 | extends Actor
18 | with ActorLogging
19 | with ChildActorLookup {
20 | override def receive: Receive = forwardToActor
21 |
22 | override type ID = ThreadId
23 |
24 | override protected def childName(childId: ThreadId): String = childId.value.asString
25 |
26 | override protected def childProps(childId: ThreadId): Props = propsF(childId)(subscribers)
27 |
28 | override protected def toChildId(commandRequest: BaseCommandRequest): ThreadId =
29 | commandRequest.asInstanceOf[ThreadProtocol.CommandRequest].threadId
30 | }
31 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/dao/jdbc/SlickDaoSupport.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.dao.jdbc
2 |
3 | import java.time.Instant
4 |
5 | import slick.ast.BaseTypedType
6 | import slick.jdbc.JdbcType
7 |
8 | trait SlickDaoSupport {
9 |
10 | val profile: slick.jdbc.JdbcProfile
11 |
12 | import profile.api._
13 |
14 | implicit def instantColumnType: JdbcType[Instant] with BaseTypedType[Instant] =
15 | MappedColumnType.base[Instant, java.sql.Timestamp](
16 | { instant =>
17 | new java.sql.Timestamp(instant.toEpochMilli)
18 | }, { ts =>
19 | Instant.ofEpochMilli(ts.getTime)
20 | }
21 | )
22 |
23 | trait Record
24 |
25 | trait SoftDeletableRecord extends Record {
26 | val deleted: Boolean
27 | }
28 |
29 | abstract class TableBase[T](_tableTag: Tag, _tableName: String, _schemaName: Option[String] = None)
30 | extends Table[T](_tableTag, _schemaName, _tableName)
31 |
32 | trait SoftDeletableTableSupport[T] { this: TableBase[T] =>
33 | def deleted: Rep[Boolean]
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/dao/jdbc/ThreadAdministratorIds.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.dao.jdbc
2 |
3 | import slick.lifted.ProvenShape
4 | import slick.lifted.PrimaryKey
5 | import com.github.j5ik2o.threadWeaver.adaptor.dao._
6 |
7 | trait ThreadAdministratorIdsComponent extends SlickDaoSupport {
8 |
9 | import profile.api._
10 |
11 | case class ThreadAdministratorIdsRecordImpl(
12 | id: String,
13 | threadId: String,
14 | accountId: String,
15 | adderId: String,
16 | createdAt: java.time.Instant,
17 | updatedAt: java.time.Instant
18 | ) extends Record
19 | with ThreadAdministratorIdsRecord
20 |
21 | case class ThreadAdministratorIdss(tag: Tag)
22 | extends TableBase[ThreadAdministratorIdsRecordImpl](tag, "thread_administrator_ids") {
23 | def id: Rep[String] = column[String]("id")
24 | def threadId: Rep[String] = column[String]("thread_id")
25 | def accountId: Rep[String] = column[String]("account_id")
26 | def adderId: Rep[String] = column[String]("adder_id")
27 | def createdAt: Rep[java.time.Instant] = column[java.time.Instant]("created_at")
28 | def updatedAt: Rep[java.time.Instant] = column[java.time.Instant]("updated_at")
29 | def pk: PrimaryKey = primaryKey("pk", (id))
30 | override def * : ProvenShape[ThreadAdministratorIdsRecordImpl] =
31 | (id, threadId, accountId, adderId, createdAt, updatedAt) <> (ThreadAdministratorIdsRecordImpl.tupled, ThreadAdministratorIdsRecordImpl.unapply)
32 | }
33 |
34 | object ThreadAdministratorIdsDao extends TableQuery(ThreadAdministratorIdss)
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/dao/jdbc/ThreadMemberIds.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.dao.jdbc
2 |
3 | import slick.lifted.ProvenShape
4 | import slick.lifted.PrimaryKey
5 | import com.github.j5ik2o.threadWeaver.adaptor.dao._
6 |
7 | trait ThreadMemberIdsComponent extends SlickDaoSupport {
8 |
9 | import profile.api._
10 |
11 | case class ThreadMemberIdsRecordImpl(
12 | id: String,
13 | threadId: String,
14 | accountId: String,
15 | adderId: String,
16 | createdAt: java.time.Instant,
17 | updatedAt: java.time.Instant
18 | ) extends Record
19 | with ThreadMemberIdsRecord
20 |
21 | case class ThreadMemberIdss(tag: Tag) extends TableBase[ThreadMemberIdsRecordImpl](tag, "thread_member_ids") {
22 | def id: Rep[String] = column[String]("id")
23 | def threadId: Rep[String] = column[String]("thread_id")
24 | def accountId: Rep[String] = column[String]("account_id")
25 | def adderId: Rep[String] = column[String]("adder_id")
26 | def createdAt: Rep[java.time.Instant] = column[java.time.Instant]("created_at")
27 | def updatedAt: Rep[java.time.Instant] = column[java.time.Instant]("updated_at")
28 | def pk: PrimaryKey = primaryKey("pk", (id))
29 | override def * : ProvenShape[ThreadMemberIdsRecordImpl] =
30 | (id, threadId, accountId, adderId, createdAt, updatedAt) <> (ThreadMemberIdsRecordImpl.tupled, ThreadMemberIdsRecordImpl.unapply)
31 | }
32 |
33 | object ThreadMemberIdsDao extends TableQuery(ThreadMemberIdss)
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/dao/jdbc/ThreadMessage.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.dao.jdbc
2 |
3 | import slick.lifted.ProvenShape
4 | import slick.lifted.PrimaryKey
5 | import com.github.j5ik2o.threadWeaver.adaptor.dao._
6 |
7 | trait ThreadMessageComponent extends SlickDaoSupport {
8 |
9 | import profile.api._
10 |
11 | case class ThreadMessageRecordImpl(
12 | id: String,
13 | deleted: Boolean,
14 | threadId: String,
15 | senderId: String,
16 | `type`: String,
17 | body: String,
18 | createdAt: java.time.Instant,
19 | updatedAt: java.time.Instant
20 | ) extends SoftDeletableRecord
21 | with ThreadMessageRecord
22 |
23 | case class ThreadMessages(tag: Tag)
24 | extends TableBase[ThreadMessageRecordImpl](tag, "thread_message")
25 | with SoftDeletableTableSupport[ThreadMessageRecordImpl] {
26 | def id: Rep[String] = column[String]("id")
27 | def deleted: Rep[Boolean] = column[Boolean]("deleted")
28 | def threadId: Rep[String] = column[String]("thread_id")
29 | def senderId: Rep[String] = column[String]("sender_id")
30 | def `type`: Rep[String] = column[String]("type")
31 | def body: Rep[String] = column[String]("body")
32 | def createdAt: Rep[java.time.Instant] = column[java.time.Instant]("created_at")
33 | def updatedAt: Rep[java.time.Instant] = column[java.time.Instant]("updated_at")
34 | def pk: PrimaryKey = primaryKey("pk", (id))
35 | override def * : ProvenShape[ThreadMessageRecordImpl] =
36 | (id, deleted, threadId, senderId, `type`, body, createdAt, updatedAt) <> (ThreadMessageRecordImpl.tupled, ThreadMessageRecordImpl.unapply)
37 | }
38 |
39 | object ThreadMessageDao extends TableQuery(ThreadMessages)
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/error/InterfaceError.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.error
2 |
3 | sealed trait InterfaceError {
4 | val message: String
5 | val cause: Option[Throwable]
6 | }
7 |
8 | case class ULIDFormatError(message: String, cause: Option[Throwable] = None) extends InterfaceError
9 | case class ThreadIdFormatError(message: String, cause: Option[Throwable] = None) extends InterfaceError
10 | case class ThreadTitleFormatError(message: String, cause: Option[Throwable] = None) extends InterfaceError
11 | case class ThreadRemarksFormatError(message: String, cause: Option[Throwable] = None) extends InterfaceError
12 | case class AdministratorIdsError(message: String, cause: Option[Throwable] = None) extends InterfaceError
13 | case class AccountIdFormatError(message: String, cause: Option[Throwable] = None) extends InterfaceError
14 | case class InstantFormatError(message: String, cause: Option[Throwable] = None) extends InterfaceError
15 | case class TextMessageFormatError(message: String, cause: Option[Throwable] = None) extends InterfaceError
16 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/grpc/presenter/AddMessagesPresenterImpl.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.grpc.presenter
2 | import akka.NotUsed
3 | import akka.stream.scaladsl.Flow
4 | import com.github.j5ik2o.threadWeaver.adaptor.grpc.model.AddMessagesResponse
5 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol
6 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.{ AddMessagesFailed, AddMessagesSucceeded }
7 |
8 | class AddMessagesPresenterImpl extends AddMessagesPresenter {
9 | override def response: Flow[ThreadWeaverProtocol.AddMessagesResponse, AddMessagesResponse, NotUsed] =
10 | Flow[ThreadWeaverProtocol.AddMessagesResponse].map {
11 | case s: AddMessagesSucceeded =>
12 | AddMessagesResponse(isSuccessful = true, s.messageIds.valuesAsString, Seq.empty)
13 | case f: AddMessagesFailed =>
14 | AddMessagesResponse(isSuccessful = false, Seq.empty, Seq(f.message))
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/grpc/presenter/CreateThreadPresenterImpl.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.grpc.presenter
2 |
3 | import akka.NotUsed
4 | import akka.stream.scaladsl.Flow
5 | import com.github.j5ik2o.threadWeaver.adaptor.grpc.model.CreateThreadResponse
6 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol
7 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.{ CreateThreadFailed, CreateThreadSucceeded }
8 |
9 | class CreateThreadPresenterImpl extends CreateThreadPresenter {
10 | override def response: Flow[ThreadWeaverProtocol.CreateThreadResponse, CreateThreadResponse, NotUsed] =
11 | Flow[ThreadWeaverProtocol.CreateThreadResponse].map {
12 | case s: CreateThreadSucceeded =>
13 | CreateThreadResponse(isSuccessful = true, s.threadId.value.asString, Seq.empty)
14 | case f: CreateThreadFailed =>
15 | CreateThreadResponse(isSuccessful = false, "", Seq(f.message))
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/grpc/presenter/DestroyThreadPresenterImpl.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.grpc.presenter
2 | import akka.NotUsed
3 | import akka.stream.scaladsl.Flow
4 | import com.github.j5ik2o.threadWeaver.adaptor.grpc.model.DestroyThreadResponse
5 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol
6 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.{ DestroyThreadFailed, DestroyThreadSucceeded }
7 |
8 | class DestroyThreadPresenterImpl extends DestroyThreadPresenter {
9 | override def response: Flow[ThreadWeaverProtocol.DestroyThreadResponse, DestroyThreadResponse, NotUsed] =
10 | Flow[ThreadWeaverProtocol.DestroyThreadResponse].map {
11 | case s: DestroyThreadSucceeded =>
12 | DestroyThreadResponse(isSuccessful = true, s.threadId.value.asString, Seq.empty)
13 | case f: DestroyThreadFailed =>
14 | DestroyThreadResponse(isSuccessful = false, "", Seq(f.message))
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/grpc/presenter/JoinAdministratorIdsPresenterImpl.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.grpc.presenter
2 |
3 | import akka.NotUsed
4 | import akka.stream.scaladsl.Flow
5 | import com.github.j5ik2o.threadWeaver.adaptor.grpc.model.JoinAdministratorIdsResponse
6 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol
7 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.{
8 | JoinAdministratorIdsFailed,
9 | JoinAdministratorIdsSucceeded
10 | }
11 |
12 | class JoinAdministratorIdsPresenterImpl extends JoinAdministratorIdsPresenter {
13 | override def response
14 | : Flow[ThreadWeaverProtocol.JoinAdministratorIdsResponse, JoinAdministratorIdsResponse, NotUsed] =
15 | Flow[ThreadWeaverProtocol.JoinAdministratorIdsResponse].map {
16 | case s: JoinAdministratorIdsSucceeded =>
17 | JoinAdministratorIdsResponse(isSuccessful = true, s.threadId.value.asString, Seq.empty)
18 | case f: JoinAdministratorIdsFailed =>
19 | JoinAdministratorIdsResponse(isSuccessful = false, "", Seq(f.message))
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/grpc/presenter/JoinMemberIdsPresenterImpl.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.grpc.presenter
2 |
3 | import akka.NotUsed
4 | import akka.stream.scaladsl.Flow
5 | import com.github.j5ik2o.threadWeaver.adaptor.grpc.model.JoinMemberIdsResponse
6 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol
7 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.{ JoinMemberIdsFailed, JoinMemberIdsSucceeded }
8 |
9 | class JoinMemberIdsPresenterImpl extends JoinMemberIdsPresenter {
10 | override def response: Flow[ThreadWeaverProtocol.JoinMemberIdsResponse, JoinMemberIdsResponse, NotUsed] =
11 | Flow[ThreadWeaverProtocol.JoinMemberIdsResponse].map {
12 | case s: JoinMemberIdsSucceeded =>
13 | JoinMemberIdsResponse(isSuccessful = true, s.threadId.value.asString, Seq.empty)
14 | case f: JoinMemberIdsFailed =>
15 | JoinMemberIdsResponse(isSuccessful = false, "", Seq(f.message))
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/grpc/presenter/LeaveAdministratorIdsPresenterImpl.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.grpc.presenter
2 |
3 | import akka.NotUsed
4 | import akka.stream.scaladsl.Flow
5 | import com.github.j5ik2o.threadWeaver.adaptor.grpc.model.LeaveAdministratorIdsResponse
6 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol
7 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.{
8 | LeaveAdministratorIdsFailed,
9 | LeaveAdministratorIdsSucceeded
10 | }
11 |
12 | class LeaveAdministratorIdsPresenterImpl extends LeaveAdministratorIdsPresenter {
13 | override def response
14 | : Flow[ThreadWeaverProtocol.LeaveAdministratorIdsResponse, LeaveAdministratorIdsResponse, NotUsed] =
15 | Flow[ThreadWeaverProtocol.LeaveAdministratorIdsResponse].map {
16 | case s: LeaveAdministratorIdsSucceeded =>
17 | LeaveAdministratorIdsResponse(isSuccessful = true, s.threadId.value.asString, Seq.empty)
18 | case f: LeaveAdministratorIdsFailed =>
19 | LeaveAdministratorIdsResponse(isSuccessful = false, "", Seq(f.message))
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/grpc/presenter/LeaveMemberIdsPresenterImpl.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.grpc.presenter
2 |
3 | import akka.NotUsed
4 | import akka.stream.scaladsl.Flow
5 | import com.github.j5ik2o.threadWeaver.adaptor.grpc.model.LeaveMemberIdsResponse
6 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol
7 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.{ LeaveMemberIdsFailed, LeaveMemberIdsSucceeded }
8 |
9 | class LeaveMemberIdsPresenterImpl extends LeaveMemberIdsPresenter {
10 | override def response: Flow[ThreadWeaverProtocol.LeaveMemberIdsResponse, LeaveMemberIdsResponse, NotUsed] =
11 | Flow[ThreadWeaverProtocol.LeaveMemberIdsResponse].map {
12 | case s: LeaveMemberIdsSucceeded =>
13 | LeaveMemberIdsResponse(isSuccessful = true, s.threadId.value.asString, Seq.empty)
14 | case f: LeaveMemberIdsFailed =>
15 | LeaveMemberIdsResponse(isSuccessful = false, "", Seq(f.message))
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/grpc/presenter/RemoveMessagesPresenterImpl.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.grpc.presenter
2 | import akka.NotUsed
3 | import akka.stream.scaladsl.Flow
4 | import com.github.j5ik2o.threadWeaver.adaptor.grpc.model.RemoveMessagesResponse
5 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol
6 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.{ RemoveMessagesFailed, RemoveMessagesSucceeded }
7 |
8 | class RemoveMessagesPresenterImpl extends RemoveMessagesPresenter {
9 | override def response: Flow[ThreadWeaverProtocol.RemoveMessagesResponse, RemoveMessagesResponse, NotUsed] =
10 | Flow[ThreadWeaverProtocol.RemoveMessagesResponse].map {
11 | case s: RemoveMessagesSucceeded =>
12 | RemoveMessagesResponse(isSuccessful = true, s.messageIds.valuesAsString, Seq.empty)
13 | case f: RemoveMessagesFailed =>
14 | RemoveMessagesResponse(isSuccessful = false, Seq.empty, Seq(f.message))
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/directives/ValidationsRejection.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.directives
2 |
3 | import cats.data.NonEmptyList
4 | import com.github.j5ik2o.threadWeaver.adaptor.error.InterfaceError
5 | import com.github.j5ik2o.threadWeaver.adaptor.http.rejections.ThreadWeaverRejection
6 |
7 | case class ValidationsRejection(errors: NonEmptyList[InterfaceError]) extends ThreadWeaverRejection {
8 | override val message: String = errors.toList.map(_.message).mkString(",")
9 | override val cause: Option[InterfaceError] = None
10 | }
11 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/AddMessagesPresenterImpl.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 |
3 | import akka.NotUsed
4 | import akka.stream.scaladsl.Flow
5 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.AddMessagesResponseJson
6 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol._
7 |
8 | private[adaptor] class AddMessagesPresenterImpl extends AddMessagesPresenter {
9 |
10 | override def response: Flow[AddMessagesResponse, AddMessagesResponseJson, NotUsed] = Flow[AddMessagesResponse].map {
11 | case f: AddMessagesFailed =>
12 | AddMessagesResponseJson(
13 | messageIds = Seq.empty,
14 | error_messages = Seq(f.message)
15 | )
16 | case s: AddMessagesSucceeded =>
17 | AddMessagesResponseJson(
18 | messageIds = s.messageIds.valuesAsString,
19 | error_messages = Seq.empty
20 | )
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/CreateThreadPresenterImpl.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 |
3 | import akka.NotUsed
4 | import akka.stream.scaladsl.Flow
5 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.CreateThreadResponseJson
6 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol._
7 |
8 | private[adaptor] class CreateThreadPresenterImpl extends CreateThreadPresenter {
9 |
10 | override def response: Flow[CreateThreadResponse, CreateThreadResponseJson, NotUsed] =
11 | Flow[CreateThreadResponse].map {
12 | case f: CreateThreadFailed =>
13 | CreateThreadResponseJson(
14 | thread_id = None,
15 | error_messages = Seq(f.message)
16 | )
17 | case s: CreateThreadSucceeded =>
18 | CreateThreadResponseJson(
19 | thread_id = Some(s.threadId.value.asString),
20 | error_messages = Seq.empty
21 | )
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/DestroyThreadPresenterImpl.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 |
3 | import akka.NotUsed
4 | import akka.stream.scaladsl.Flow
5 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.DestroyThreadResponseJson
6 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol.{
7 | DestroyThreadFailed,
8 | DestroyThreadResponse,
9 | DestroyThreadSucceeded
10 | }
11 |
12 | class DestroyThreadPresenterImpl extends DestroyThreadPresenter {
13 |
14 | override def response: Flow[DestroyThreadResponse, DestroyThreadResponseJson, NotUsed] =
15 | Flow[DestroyThreadResponse].map {
16 | case f: DestroyThreadFailed =>
17 | DestroyThreadResponseJson(
18 | threadId = None,
19 | error_messages = Seq(f.message)
20 | )
21 | case s: DestroyThreadSucceeded =>
22 | DestroyThreadResponseJson(
23 | threadId = Some(s.threadId.value.asString),
24 | error_messages = Seq.empty
25 | )
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/JoinAdministratorIdsPresenterImpl.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 |
3 | import akka.NotUsed
4 | import akka.stream.scaladsl.Flow
5 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.JoinAdministratorIdsResponseJson
6 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol._
7 |
8 | private[adaptor] class JoinAdministratorIdsPresenterImpl extends JoinAdministratorIdsPresenter {
9 |
10 | override def response: Flow[JoinAdministratorIdsResponse, JoinAdministratorIdsResponseJson, NotUsed] = {
11 | Flow[JoinAdministratorIdsResponse].map {
12 | case f: JoinAdministratorIdsFailed =>
13 | JoinAdministratorIdsResponseJson(threadId = None, error_messages = Seq(f.message))
14 | case s: JoinAdministratorIdsSucceeded =>
15 | JoinAdministratorIdsResponseJson(threadId = Some(s.threadId.value.asString), error_messages = Seq.empty)
16 | }
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/JoinMemberIdsPresenterImpl.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 |
3 | import akka.NotUsed
4 | import akka.stream.scaladsl.Flow
5 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.JoinMemberIdsResponseJson
6 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol._
7 |
8 | private[adaptor] class JoinMemberIdsPresenterImpl extends JoinMemberIdsPresenter {
9 |
10 | override def response: Flow[JoinMemberIdsResponse, JoinMemberIdsResponseJson, NotUsed] = {
11 | Flow[JoinMemberIdsResponse].map {
12 | case f: JoinMemberIdsFailed =>
13 | JoinMemberIdsResponseJson(threadId = None, error_messages = Seq(f.message))
14 | case s: JoinMemberIdsSucceeded =>
15 | JoinMemberIdsResponseJson(threadId = Some(s.threadId.value.asString), error_messages = Seq.empty)
16 | }
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/LeaveAdministratorIdsPresenterImpl.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 | import akka.NotUsed
3 | import akka.stream.scaladsl.Flow
4 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.LeaveAdministratorIdsResponseJson
5 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol._
6 |
7 | private[adaptor] class LeaveAdministratorIdsPresenterImpl extends LeaveAdministratorIdsPresenter {
8 |
9 | override def response: Flow[LeaveAdministratorIdsResponse, LeaveAdministratorIdsResponseJson, NotUsed] =
10 | Flow[LeaveAdministratorIdsResponse].map {
11 | case f: LeaveAdministratorIdsFailed =>
12 | LeaveAdministratorIdsResponseJson(threadId = None, error_messages = Seq(f.message))
13 | case s: LeaveAdministratorIdsSucceeded =>
14 | LeaveAdministratorIdsResponseJson(threadId = Some(s.threadId.value.asString), error_messages = Seq.empty)
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/LeaveMemberIdsPresenterImpl.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 | import akka.NotUsed
3 | import akka.stream.scaladsl.Flow
4 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.LeaveMemberIdsResponseJson
5 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol._
6 |
7 | private[adaptor] class LeaveMemberIdsPresenterImpl extends LeaveMemberIdsPresenter {
8 |
9 | override def response: Flow[LeaveMemberIdsResponse, LeaveMemberIdsResponseJson, NotUsed] =
10 | Flow[LeaveMemberIdsResponse].map {
11 | case f: LeaveMemberIdsFailed =>
12 | LeaveMemberIdsResponseJson(threadId = None, error_messages = Seq(f.message))
13 | case s: LeaveMemberIdsSucceeded =>
14 | LeaveMemberIdsResponseJson(threadId = Some(s.threadId.value.asString), error_messages = Seq.empty)
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/RemoveMessagesPresenterImpl.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 |
3 | import akka.NotUsed
4 | import akka.stream.scaladsl.Flow
5 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.RemoveMessagesResponseJson
6 | import com.github.j5ik2o.threadWeaver.useCase.ThreadWeaverProtocol._
7 |
8 | private[adaptor] class RemoveMessagesPresenterImpl extends RemoveMessagesPresenter {
9 |
10 | override def response: Flow[RemoveMessagesResponse, RemoveMessagesResponseJson, NotUsed] =
11 | Flow[RemoveMessagesResponse].map {
12 | case f: RemoveMessagesFailed =>
13 | RemoveMessagesResponseJson(
14 | messageIds = Seq.empty,
15 | error_messages = Seq(f.message)
16 | )
17 | case s: RemoveMessagesSucceeded =>
18 | RemoveMessagesResponseJson(
19 | messageIds = s.messageIds.valuesAsString,
20 | error_messages = Seq.empty
21 | )
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/ThreadMessagePresenterImpl.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 | import akka.NotUsed
3 | import akka.stream.scaladsl.Flow
4 | import com.github.j5ik2o.threadWeaver.adaptor.dao.ThreadMessageRecord
5 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.ThreadMessageJson
6 |
7 | class ThreadMessagePresenterImpl extends ThreadMessagePresenter {
8 | override def response: Flow[ThreadMessageRecord, ThreadMessageJson, NotUsed] = Flow[ThreadMessageRecord].map {
9 | messageRecord =>
10 | ThreadMessageJson(
11 | messageRecord.id,
12 | messageRecord.threadId,
13 | messageRecord.senderId,
14 | messageRecord.`type`,
15 | messageRecord.body,
16 | messageRecord.createdAt.toEpochMilli,
17 | messageRecord.updatedAt.toEpochMilli
18 | )
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/presenter/ThreadPresenterImpl.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.presenter
2 | import akka.NotUsed
3 | import akka.stream.scaladsl.Flow
4 | import com.github.j5ik2o.threadWeaver.adaptor.dao.ThreadRecord
5 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.ThreadJson
6 |
7 | class ThreadPresenterImpl extends ThreadPresenter {
8 |
9 | override def response: Flow[ThreadRecord, ThreadJson, NotUsed] = Flow[ThreadRecord].map { threadRecord =>
10 | ThreadJson(
11 | threadRecord.id,
12 | threadRecord.creatorId,
13 | threadRecord.parentId,
14 | threadRecord.title,
15 | threadRecord.remarks,
16 | threadRecord.createdAt.toEpochMilli,
17 | threadRecord.updatedAt.toEpochMilli
18 | )
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/rejections/NotFoundRejection.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.rejections
2 | import com.github.j5ik2o.threadWeaver.adaptor.error.InterfaceError
3 |
4 | case class NotFoundRejection(override val message: String, override val cause: Option[InterfaceError])
5 | extends ThreadWeaverRejection
6 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/rejections/RejectionHandlers.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.rejections
2 |
3 | import akka.http.scaladsl.model.StatusCodes
4 | import akka.http.scaladsl.server.Directives.complete
5 | import akka.http.scaladsl.server.RejectionHandler
6 | import com.github.j5ik2o.threadWeaver.adaptor.http.directives.ValidationsRejection
7 | import com.github.j5ik2o.threadWeaver.adaptor.http.json.ErrorsResponseJson
8 | import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport._
9 | import io.circe.generic.auto._
10 |
11 | object RejectionHandlers {
12 |
13 | final val default: RejectionHandler = RejectionHandler
14 | .newBuilder()
15 | .handle {
16 | case NotFoundRejection(msg, _) =>
17 | complete((StatusCodes.NotFound, ErrorsResponseJson(Seq(msg))))
18 | case ValidationsRejection(errors) =>
19 | complete((StatusCodes.BadRequest, ErrorsResponseJson(errors.map(_.message).toList)))
20 | }
21 | .result()
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/rejections/ThreadWeaverRejection.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.rejections
2 |
3 | import akka.http.javadsl.server.CustomRejection
4 | import com.github.j5ik2o.threadWeaver.adaptor.error.InterfaceError
5 |
6 | trait ThreadWeaverRejection extends CustomRejection {
7 | val message: String
8 | val cause: Option[InterfaceError]
9 | protected def withCauseMessage = s"$message${cause.fold("")(v => s": ${v.message}")}"
10 | }
11 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/routes/RequestFormatter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.routes
2 |
3 | import akka.http.scaladsl.model.HttpRequest
4 | import akka.http.scaladsl.server.directives.LogEntry
5 |
6 | trait RequestFormatter {
7 | def formatRequest(request: HttpRequest): LogEntry
8 | }
9 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/routes/RequestResultFormatter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.routes
2 |
3 | import akka.http.scaladsl.model.HttpRequest
4 | import akka.http.scaladsl.server.RouteResult
5 | import akka.http.scaladsl.server.directives.LogEntry
6 |
7 | trait RequestResultFormatter {
8 | def formatRequestResponse(request: HttpRequest): RouteResult => Option[LogEntry]
9 | }
10 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/routes/RouteLogging.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.routes
2 |
3 | import akka.http.scaladsl.server.Directive0
4 | import akka.http.scaladsl.server.directives.DebuggingDirectives
5 |
6 | class RouteLogging()(implicit requestFormatter: RequestFormatter, requestResultFormatter: RequestResultFormatter) {
7 |
8 | val httpLogRequest: Directive0 = DebuggingDirectives.logRequest(requestFormatter.formatRequest _)
9 |
10 | val httpLogRequestResult: Directive0 =
11 | DebuggingDirectives.logRequestResult(requestResultFormatter.formatRequestResponse _)
12 |
13 | }
14 |
15 | object RouteLogging {
16 |
17 | def apply()(
18 | implicit requestFormatter: RequestFormatter,
19 | requestResultFormatter: RequestResultFormatter
20 | ): RouteLogging = new RouteLogging()
21 |
22 | val default: RouteLogging = new RouteLogging()(
23 | DefaultRequestLoggingFormatter.requestFormatter,
24 | DefaultRequestLoggingFormatter.requestResultFormatter
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/http/routes/Routes.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.http.routes
2 |
3 | import akka.actor.typed.ActorSystem
4 | import akka.http.scaladsl.model.{ ContentTypes, HttpEntity, HttpResponse }
5 | import akka.http.scaladsl.server.Directives._
6 | import akka.http.scaladsl.server.Route
7 | import akka.stream.Materializer
8 | import ch.megard.akka.http.cors.scaladsl.CorsDirectives._
9 | import com.github.j5ik2o.threadWeaver.adaptor.http.controller.{ ThreadCommandController, ThreadQueryController }
10 | import com.github.j5ik2o.threadWeaver.adaptor.http.directives.MetricsDirective
11 | import com.github.j5ik2o.threadWeaver.adaptor.swagger.SwaggerDocService
12 |
13 | class Routes(
14 | val swaggerDocService: SwaggerDocService,
15 | threadCommandController: ThreadCommandController,
16 | threadQueryController: ThreadQueryController
17 | )(
18 | implicit system: ActorSystem[Nothing],
19 | mat: Materializer
20 | ) extends MetricsDirective {
21 |
22 | def root: Route =
23 | cors() {
24 | RouteLogging.default.httpLogRequestResult {
25 | pathEndOrSingleSlash {
26 | get {
27 | index()
28 | }
29 | } ~ path("swagger") {
30 | getFromResource("swagger/index.html")
31 | } ~ getFromResourceDirectory("swagger") ~ apiMetrics { implicit traceContext =>
32 | swaggerDocService.routes ~ threadCommandController.toRoutes ~ threadQueryController.toRoutes
33 | }
34 | }
35 | }
36 |
37 | def index(): Route = complete(
38 | HttpResponse(
39 | entity = HttpEntity(
40 | ContentTypes.`text/html(UTF-8)`,
41 | """Wellcome to Thread Weaver API
swagger"""
42 | )
43 | )
44 | )
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/readModelUpdater/ShardedThreadReadModelUpdatersRegion.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.readModelUpdater
2 |
3 | import akka.actor.{ ActorRef, ActorSystem }
4 | import akka.cluster.sharding.{ ClusterSharding, ClusterShardingSettings }
5 | import com.github.j5ik2o.threadWeaver.adaptor.readModelUpdater.ThreadReadModelUpdater.ReadJournalType
6 | import slick.jdbc.JdbcProfile
7 |
8 | import scala.concurrent.duration.Duration
9 |
10 | object ShardedThreadReadModelUpdatersRegion {
11 |
12 | def startClusterSharding(
13 | receiveTimeout: Duration,
14 | nrOfShards: Int,
15 | readJournal: ReadJournalType,
16 | profile: JdbcProfile,
17 | db: JdbcProfile#Backend#Database,
18 | sqlBatchSize: Long
19 | )(implicit system: ActorSystem): ActorRef =
20 | ClusterSharding(system).start(
21 | ShardedThreadReadModelUpdaters.shardName,
22 | ShardedThreadReadModelUpdaters.props(receiveTimeout, readJournal, profile, db, sqlBatchSize),
23 | ClusterShardingSettings(system),
24 | ShardedThreadReadModelUpdaters.extractEntityId,
25 | ShardedThreadReadModelUpdaters.extractShardId(nrOfShards)
26 | )
27 |
28 | def shardRegion(implicit system: ActorSystem): ActorRef =
29 | ClusterSharding(system).shardRegion(ShardedThreadReadModelUpdaters.shardName)
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/readModelUpdater/ThreadReadModelUpdaters.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.readModelUpdater
2 |
3 | import akka.actor.{ Actor, Props }
4 | import com.github.j5ik2o.threadWeaver.adaptor.aggregates.BaseCommandRequest
5 | import com.github.j5ik2o.threadWeaver.adaptor.aggregates.untyped.ChildActorLookup
6 | import com.github.j5ik2o.threadWeaver.adaptor.readModelUpdater.ThreadReadModelUpdater.ReadJournalType
7 | import slick.jdbc.JdbcProfile
8 |
9 | class ThreadReadModelUpdaters(
10 | readJournal: ReadJournalType,
11 | profile: JdbcProfile,
12 | db: JdbcProfile#Backend#Database,
13 | sqlBatchSize: Long
14 | ) extends Actor
15 | with ChildActorLookup {
16 | override type ID = ThreadTag
17 | override def receive: Receive = forwardToActor
18 |
19 | override protected def childName(childId: ThreadTag): String = childId.value
20 |
21 | override protected def childProps(childId: ThreadTag): Props =
22 | ThreadReadModelUpdater.props(readJournal, profile, db, sqlBatchSize)
23 |
24 | override protected def toChildId(commandRequest: BaseCommandRequest): ThreadTag =
25 | commandRequest.asInstanceOf[ThreadReadModelUpdaterProtocol.CommandRequest].threadTag
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/routing/ThreadToRMURouter.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.routing
2 |
3 | import java.time.Instant
4 |
5 | import akka.actor.{ Actor, ActorRef, Props }
6 | import com.github.j5ik2o.threadWeaver.adaptor.aggregates.ThreadCommonProtocol
7 | import com.github.j5ik2o.threadWeaver.adaptor.readModelUpdater.{ ThreadReadModelUpdaterProtocol, ThreadTag }
8 | import com.github.j5ik2o.threadWeaver.infrastructure.ulid.ULID
9 |
10 | object ThreadToRMURouter {
11 |
12 | def props(rmu: ActorRef): Props = Props(new ThreadToRMURouter(rmu))
13 |
14 | }
15 |
16 | class ThreadToRMURouter(rmu: ActorRef) extends Actor {
17 | implicit val config = context.system.settings.config
18 |
19 | override def receive: Receive = {
20 | case msg: ThreadCommonProtocol.Started =>
21 | rmu ! ThreadReadModelUpdaterProtocol.Start(ULID(), ThreadTag.fromThreadId(msg.threadId), Instant.now)
22 | case msg: ThreadCommonProtocol.Stopped =>
23 | rmu ! ThreadReadModelUpdaterProtocol.Stop(ULID(), ThreadTag.fromThreadId(msg.threadId), Instant.now)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/serialization/JournalEventTagPartitioner.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.serialization
2 |
3 | import com.github.j5ik2o.threadWeaver.domain.model.threads.ThreadId
4 |
5 | import scala.util.hashing.MurmurHash3
6 |
7 | trait JournalEventTagPartitioner {
8 |
9 | val numPartition: Int
10 |
11 | def partition(threadId: ThreadId): Int = Math.abs(MurmurHash3.stringHash(threadId.value.asString) % numPartition)
12 |
13 | def partitionTagName(threadId: ThreadId, category: String): String = createTagName(partition(threadId), category)
14 |
15 | def createTagName(id: Int, category: String): String = s"${category}-${id}"
16 | }
17 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/serialization/ThreadEventJSONSerializer.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.serialization
2 |
3 | import akka.actor.ExtendedActorSystem
4 | import akka.event.{ Logging, LoggingAdapter }
5 | import akka.serialization.SerializerWithStringManifest
6 | import com.github.j5ik2o.threadWeaver.adaptor.aggregates.untyped.ThreadProtocol
7 | import com.github.j5ik2o.threadWeaver.adaptor.aggregates.untyped.ThreadProtocol.ThreadCreated
8 |
9 | object ThreadEventJSONSerializer {
10 | final val CREATE = ThreadProtocol.ThreadCreated.getClass.getName.stripSuffix("$")
11 | }
12 |
13 | class ThreadEventJSONSerializer(system: ExtendedActorSystem) extends SerializerWithStringManifest {
14 | import com.github.j5ik2o.threadWeaver.adaptor.serialization.json.ThreadCreatedJson._
15 | import io.circe.generic.auto._
16 | private implicit val log: LoggingAdapter = Logging.getLogger(system, getClass)
17 |
18 | override def identifier: Int = 50
19 |
20 | override def manifest(o: AnyRef): String = o.getClass.getName
21 |
22 | override def toBinary(o: AnyRef): Array[Byte] = o match {
23 | case orig: ThreadCreated => CirceJsonSerialization.toBinary(orig)
24 | }
25 |
26 | override def fromBinary(bytes: Array[Byte], manifest: String): AnyRef = manifest match {
27 | case ThreadEventJSONSerializer.CREATE => CirceJsonSerialization.fromBinary(bytes)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/serialization/ThreadMessageJSONSerializer.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.serialization
2 |
3 | class ThreadMessageJSONSerializer {}
4 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/serialization/ThreadTaggingEventAdaptor.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.serialization
2 |
3 | import akka.actor.ExtendedActorSystem
4 | import akka.persistence.journal.{ Tagged, WriteEventAdapter }
5 | import com.github.j5ik2o.threadWeaver.adaptor.aggregates.ThreadCommonProtocol
6 | import com.github.j5ik2o.threadWeaver.adaptor.readModelUpdater.ThreadReadModelUpdaterSettings
7 |
8 | class ThreadTaggingEventAdaptor(system: ExtendedActorSystem) extends WriteEventAdapter with JournalEventTagPartitioner {
9 | val settings = ThreadReadModelUpdaterSettings.fromConfig(system.settings.config)
10 |
11 | override val numPartition: Int = settings.numPartition
12 |
13 | override def manifest(event: Any): String = ""
14 |
15 | override def toJournal(event: Any): Any = event match {
16 | case event: ThreadCommonProtocol.Event =>
17 | val tag = partitionTagName(event.threadId, settings.category)
18 | Tagged(event, Set(tag))
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/serialization/json/CreateThreadFailedJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.serialization.json
2 |
3 | import java.time.Instant
4 |
5 | import com.github.j5ik2o.threadWeaver.adaptor.aggregates.untyped.ThreadProtocol.CreateThreadFailed
6 | import com.github.j5ik2o.threadWeaver.adaptor.serialization.DomainObjToJsonReprIso
7 | import com.github.j5ik2o.threadWeaver.domain.model.threads.ThreadId
8 | import com.github.j5ik2o.threadWeaver.infrastructure.ulid.ULID
9 |
10 | case class CreateThreadFailedJson(id: String, requestId: String, threadId: String, message: String, createAt: Long)
11 |
12 | object CreateThreadFailedJson {
13 | implicit object CreateThreadFailedIso extends DomainObjToJsonReprIso[CreateThreadFailed, CreateThreadFailedJson] {
14 | override def convertTo(domainObj: CreateThreadFailed): CreateThreadFailedJson =
15 | CreateThreadFailedJson(
16 | id = domainObj.id.asString,
17 | requestId = domainObj.requestId.asString,
18 | threadId = domainObj.threadId.value.asString,
19 | message = domainObj.message,
20 | createAt = domainObj.createAt.toEpochMilli
21 | )
22 |
23 | override def convertFrom(json: CreateThreadFailedJson): CreateThreadFailed =
24 | (for {
25 | id <- ULID.parseFromString(json.id)
26 | requestId <- ULID.parseFromString(json.requestId)
27 | threadId <- ULID.parseFromString(json.threadId)
28 | } yield (id, requestId, threadId)).fold(throw _, {
29 | case (id, requestId, threadId) =>
30 | CreateThreadFailed(
31 | id,
32 | requestId,
33 | ThreadId(threadId),
34 | json.message,
35 | Instant.ofEpochMilli(json.createAt)
36 | )
37 | })
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/serialization/json/CreateThreadSucceededJson.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.serialization.json
2 |
3 | import java.time.Instant
4 |
5 | import com.github.j5ik2o.threadWeaver.adaptor.aggregates.untyped.ThreadProtocol.CreateThreadSucceeded
6 | import com.github.j5ik2o.threadWeaver.adaptor.serialization.DomainObjToJsonReprIso
7 | import com.github.j5ik2o.threadWeaver.domain.model.threads.ThreadId
8 | import com.github.j5ik2o.threadWeaver.infrastructure.ulid.ULID
9 |
10 | case class CreateThreadSucceededJson(id: String, requestId: String, threadId: String, createAt: Long)
11 |
12 | object CreateThreadSucceededJson {
13 |
14 | implicit object CreateThreadSucceededIso
15 | extends DomainObjToJsonReprIso[CreateThreadSucceeded, CreateThreadSucceededJson] {
16 | override def convertTo(domainObj: CreateThreadSucceeded): CreateThreadSucceededJson =
17 | CreateThreadSucceededJson(
18 | id = domainObj.id.asString,
19 | requestId = domainObj.requestId.asString,
20 | threadId = domainObj.threadId.value.asString,
21 | createAt = domainObj.createAt.toEpochMilli
22 | )
23 |
24 | override def convertFrom(json: CreateThreadSucceededJson): CreateThreadSucceeded = {
25 | (for {
26 | id <- ULID.parseFromString(json.id)
27 | requestId <- ULID.parseFromString(json.requestId)
28 | threadId <- ULID.parseFromString(json.threadId)
29 | } yield (id, requestId, threadId)).fold(throw _, {
30 | case (id, requestId, threadId) =>
31 | CreateThreadSucceeded(
32 | id,
33 | requestId,
34 | ThreadId(threadId),
35 | Instant.ofEpochMilli(json.createAt)
36 | )
37 | })
38 | }
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/swagger/SwaggerDocService.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.swagger
2 |
3 | import com.github.swagger.akka.SwaggerHttpService
4 | import com.github.swagger.akka.model.Info
5 |
6 | class SwaggerDocService(hostName: String, port: Int, val apiClasses: Set[Class[_]]) extends SwaggerHttpService {
7 | override val host: String = s"127.0.0.1:$port" // the url of your api, not swagger's json endpoint
8 | override val apiDocsPath: String = "api-docs" // where you want the swagger-json endpoint exposed
9 | override val info: Info = Info() // provides license and other description details
10 | override val unwantedDefinitions: Seq[String] = Seq("Function1", "Function1RequestContextFutureRouteResult")
11 | }
12 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/util/PingPong.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.util
2 |
3 | import akka.actor.typed.{ ActorRef, ActorSystem, Behavior }
4 | import akka.actor.typed.scaladsl.Behaviors
5 |
6 | object PingPong extends App {
7 | trait Message
8 | case class Ping(reply: ActorRef[Message]) extends Message
9 | case object Pong extends Message
10 |
11 | def receiver: Behavior[Message] =
12 | Behaviors.setup[Message] { ctx =>
13 | Behaviors.receiveMessagePartial[Message] {
14 | case Ping(replyTo) =>
15 | ctx.log.info("ping")
16 | replyTo ! Pong
17 | Behaviors.same
18 | }
19 | }
20 |
21 | def main: Behavior[Message] = Behaviors.setup { ctx =>
22 | val receiverRef = ctx.spawn(receiver, name = "receiver")
23 | receiverRef ! Ping(ctx.self)
24 | Behaviors.receiveMessagePartial[Message] {
25 | case Pong =>
26 | ctx.log.info("pong")
27 | receiverRef ! Ping(ctx.self)
28 | Behaviors.same
29 | }
30 | }
31 |
32 | ActorSystem(main, "ping-pong")
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/validator/Validator.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.validator
2 |
3 | trait Validator[A, B] {
4 | def validate(value: A): ValidationResult[B]
5 | }
6 |
--------------------------------------------------------------------------------
/modules/interface/src/main/scala/com/github/j5ik2o/threadWeaver/adaptor/validator/package.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor
2 |
3 | import cats.data.ValidatedNel
4 | import com.github.j5ik2o.threadWeaver.adaptor.error.InterfaceError
5 |
6 | package object validator {
7 | type ValidationResult[A] = ValidatedNel[InterfaceError, A]
8 | }
9 |
--------------------------------------------------------------------------------
/modules/interface/src/multi-jvm/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | [%level] [%date{yyyy-MM-dd'T'HH:mm:ss.SSSZ}] [%X{sourceThread}] [%logger{36}] - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | [%level] [%date{yyyy-MM-dd'T'HH:mm:ss.SSSZ}] [%X{sourceThread}] [%logger{36}] [%X{akkaSource}] - %msg%n
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/modules/interface/src/multi-jvm/resources/reference.conf:
--------------------------------------------------------------------------------
1 | akka.test.timefactor = ${?TEST_TIME_FACTOR}
--------------------------------------------------------------------------------
/modules/interface/src/multi-jvm/scala/com/github/j5ik2o/threadWeaver/adaptor/aggregates/DynamoDbSpecSupport.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.aggregates
2 |
3 | import akka.cluster.Cluster
4 | import akka.remote.testconductor.RoleName
5 | import akka.remote.testkit.{ MultiNodeSpec, MultiNodeSpecCallbacks }
6 | import com.github.j5ik2o.threadWeaver.adaptor.util.DynamoDBSpecSupport
7 | import org.scalatest.{ BeforeAndAfterAll, FreeSpecLike, Matchers }
8 |
9 | trait DynamoDbSpecSupport
10 | extends MultiNodeSpecCallbacks
11 | with FreeSpecLike
12 | with Matchers
13 | with BeforeAndAfterAll
14 | with DynamoDBSpecSupport {
15 | this: MultiNodeSpec =>
16 |
17 | import DynamoDbConfig._
18 |
19 | override protected lazy val dynamoDBPort: Int = 8000
20 |
21 | override def beforeAll(): Unit = multiNodeSpecBeforeAll()
22 |
23 | override def afterAll(): Unit = multiNodeSpecAfterAll()
24 |
25 | def join(from: RoleName, to: RoleName)(f: => Unit): Unit = {
26 | runOn(from) {
27 | Cluster(system) join node(to).address
28 | f
29 | }
30 | enterBarrier(from.name + "-joined")
31 | }
32 |
33 | override protected def atStartup(): Unit = {}
34 |
35 | override protected def afterTermination(): Unit = {
36 | runOn(controller) {
37 | shutdownDynamoDBLocal()
38 | }
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/modules/interface/src/multi-jvm/scala/com/github/j5ik2o/threadWeaver/adaptor/aggregates/LevelDbConfig.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.aggregates
2 |
3 | import akka.remote.testkit.MultiNodeConfig
4 | import com.typesafe.config.ConfigFactory
5 |
6 | object LevelDbConfig extends MultiNodeConfig {
7 | val controller = role("controller")
8 | val node1 = role("node1")
9 | val node2 = role("node2")
10 |
11 | testTransport(on = true)
12 |
13 | commonConfig(
14 | ConfigFactory
15 | .parseString(
16 | """
17 | |akka.loggers = ["akka.event.slf4j.Slf4jLogger"]
18 | |akka.loglevel = "DEBUG"
19 | |akka.logging-filter = "akka.event.slf4j.Slf4jLoggingFilter"
20 | |akka.actor.debug.receive = on
21 | |
22 | |akka.cluster.metrics.enabled=off
23 | |akka.actor.provider = "cluster"
24 | |
25 | |thread-weaver {
26 | | read-model-updater.thread {
27 | | shard-name = "thread"
28 | | category = "thread"
29 | | num-partition = 1
30 | | }
31 | |}
32 | |
33 | |akka.persistence.journal.plugin = "akka.persistence.journal.leveldb-shared"
34 | |akka.persistence.journal.leveldb-shared.store {
35 | | native = off
36 | | dir = "target/test-shared-journal"
37 | |}
38 | |
39 | |akka.persistence.snapshot-store.plugin = "akka.persistence.snapshot-store.local"
40 | |akka.persistence.snapshot-store.local.dir = "target/test-snapshots"
41 | |
42 | |passivate-timeout = 60 seconds
43 | """.stripMargin
44 | )
45 | )
46 | }
47 |
--------------------------------------------------------------------------------
/modules/interface/src/multi-jvm/scala/com/github/j5ik2o/threadWeaver/adaptor/aggregates/LevelDbSpecSupport.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.aggregates
2 |
3 | import java.io.File
4 |
5 | import akka.cluster.Cluster
6 | import akka.remote.testconductor.RoleName
7 | import akka.remote.testkit.{ MultiNodeSpec, MultiNodeSpecCallbacks }
8 | import org.apache.commons.io.FileUtils
9 | import org.scalatest.{ BeforeAndAfterAll, FreeSpecLike, Matchers }
10 |
11 | trait LevelDbSpecSupport extends MultiNodeSpecCallbacks with FreeSpecLike with Matchers with BeforeAndAfterAll {
12 | this: MultiNodeSpec =>
13 |
14 | import DynamoDbConfig._
15 |
16 | val storageLocations: List[File] =
17 | List("akka.persistence.journal.leveldb.dir",
18 | "akka.persistence.journal.leveldb-shared.store.dir",
19 | "akka.persistence.snapshot-store.local.dir").map(s => new File(system.settings.config.getString(s)))
20 |
21 | override def beforeAll(): Unit = multiNodeSpecBeforeAll()
22 |
23 | override def afterAll(): Unit = multiNodeSpecAfterAll()
24 |
25 | def join(from: RoleName, to: RoleName)(f: => Unit): Unit = {
26 | runOn(from) {
27 | Cluster(system) join node(to).address
28 | f
29 | }
30 | enterBarrier(from.name + "-joined")
31 | }
32 |
33 | override protected def atStartup(): Unit = {
34 | runOn(controller) {
35 | storageLocations.foreach(dir => FileUtils.deleteDirectory(dir))
36 | }
37 | }
38 |
39 | override protected def afterTermination(): Unit = {
40 | runOn(controller) {
41 | storageLocations.foreach(dir => FileUtils.deleteDirectory(dir))
42 | }
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/modules/interface/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | [%level] [%date{yyyy-MM-dd'T'HH:mm:ss.SSSZ}] [%X{sourceThread}] [%logger{36}] - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | [%level] [%date{yyyy-MM-dd'T'HH:mm:ss.SSSZ}] [%X{sourceThread}] [%logger{36}] [%X{akkaSource}] - %msg%n
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/modules/interface/src/test/resources/reference.conf:
--------------------------------------------------------------------------------
1 | akka {
2 | test.timefactor = ${?TEST_TIME_FACTOR}
3 | loglevel = DEBUG
4 | stdout-loglevel = INFO
5 | loggers = ["akka.event.slf4j.Slf4jLogger"]
6 | logging-filter = "akka.event.slf4j.Slf4jLoggingFilter"
7 |
8 | log-dead-letters = 10
9 | log-dead-letters-during-shutdown = on
10 |
11 | actor {
12 | debug {
13 | # receive = on
14 | # lifecycle = on
15 | }
16 | }
17 |
18 | }
--------------------------------------------------------------------------------
/modules/interface/src/test/scala/com/github/j5ik2o/threadWeaver/adaptor/aggregates/PersistenceCleanup.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.aggregates
2 |
3 | import java.io.File
4 |
5 | import akka.actor.ActorSystem
6 | import org.apache.commons.io.FileUtils
7 |
8 | import scala.util.Try
9 |
10 | trait PersistenceCleanup {
11 |
12 | def storageLocations(implicit system: ActorSystem): Seq[File] =
13 | List(
14 | "akka.persistence.journal.leveldb.dir",
15 | "akka.persistence.journal.leveldb-shared.store.dir",
16 | "akka.persistence.snapshot-store.local.dir"
17 | ).map { s =>
18 | new File(system.settings.config.getString(s))
19 | }
20 |
21 | def deleteStorageLocations(implicit system: ActorSystem): Unit = {
22 | storageLocations.foreach(dir => Try(FileUtils.deleteDirectory(dir)))
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/modules/interface/src/test/scala/com/github/j5ik2o/threadWeaver/adaptor/aggregates/typed/ThreadAggregatesSpec.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.aggregates.typed
2 |
3 | import akka.actor.typed.ActorRef
4 | import com.github.j5ik2o.threadWeaver.domain.model.threads.ThreadId
5 |
6 | class ThreadAggregatesSpec extends ThreadAggregateSpec {
7 |
8 | override def newThreadRef(threadId: ThreadId): ActorRef[ThreadProtocol.CommandRequest] =
9 | spawn(ThreadAggregates.behavior(Seq.empty, ThreadAggregate.name)(ThreadAggregate.behavior))
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/modules/interface/src/test/scala/com/github/j5ik2o/threadWeaver/adaptor/aggregates/typed/TypedActorSpecSupport.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.aggregates.typed
2 |
3 | import akka.actor.testkit.typed.scaladsl.ActorTestKit
4 | import akka.actor.typed.ActorRef
5 |
6 | trait TypedActorSpecSupport {
7 |
8 | val testKit: ActorTestKit
9 |
10 | def killActors(actors: ActorRef[_]*): Unit = {
11 | actors.foreach { actor =>
12 | testKit.stop(actor)
13 | Thread.sleep(1000) // the actor name is not unique intermittently on travis when creating it again after killActors, this is ducktape.
14 | }
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/modules/interface/src/test/scala/com/github/j5ik2o/threadWeaver/adaptor/aggregates/untyped/ActorSpecSupport.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.aggregates.untyped
2 |
3 | import akka.actor.ActorRef
4 | import akka.testkit.TestKit
5 |
6 | trait ActorSpecSupport { this: TestKit =>
7 |
8 | def killActors(actors: ActorRef*): Unit = {
9 | actors.foreach { actor =>
10 | watch(actor)
11 | system.stop(actor)
12 | expectTerminated(actor)
13 | Thread.sleep(1000) // the actor name is not unique intermittently on travis when creating it again after killActors, this is ducktape.
14 | }
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/modules/interface/src/test/scala/com/github/j5ik2o/threadWeaver/adaptor/aggregates/untyped/ThreadAggregatesSpec.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.aggregates.untyped
2 | import akka.actor.ActorRef
3 | import com.github.j5ik2o.threadWeaver.domain.model.threads.ThreadId
4 |
5 | class ThreadAggregatesSpec extends ThreadAggregateSpec {
6 | override def newThreadRef(threadId: ThreadId): ActorRef =
7 | system.actorOf(ThreadAggregates.props(Seq.empty, (ThreadAggregate.props _).curried))
8 | }
9 |
--------------------------------------------------------------------------------
/modules/interface/src/test/scala/com/github/j5ik2o/threadWeaver/adaptor/util/FlywayWithMySQLSpecSupport.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.util
2 |
3 | import java.io.File
4 |
5 | import com.github.j5ik2o.scalatestplus.db._
6 | import com.wix.mysql.distribution.Version._
7 | import org.scalatest.TestSuite
8 | import org.seasar.util.io.ResourceUtil
9 |
10 | import scala.concurrent.duration._
11 |
12 | trait FlywayWithMySQLSpecSupport extends FlywayWithMySQLdOneInstancePerSuite {
13 | this: TestSuite =>
14 |
15 | override protected lazy val mySQLdConfig: MySQLdConfig = MySQLdConfig(
16 | version = v5_6_21,
17 | port = Some(RandomPortSupport.temporaryServerPort()),
18 | userWithPassword = Some(UserWithPassword("tw", "passwd")),
19 | timeout = Some((30 seconds) * sys.env.getOrElse("SBT_TEST_TIME_FACTOR", "1").toDouble)
20 | )
21 |
22 | override protected lazy val downloadConfig: DownloadConfig =
23 | super.downloadConfig.copy(cacheDir = new File(sys.env("HOME") + "/.wixMySQL/downloads"))
24 |
25 | override protected lazy val schemaConfigs: Seq[SchemaConfig] = Seq(SchemaConfig(name = "tw"))
26 |
27 | override protected def flywayConfig(jdbcUrl: String): FlywayConfig = {
28 | val buildDir = ResourceUtil.getBuildDir(getClass)
29 | val file = new File(buildDir, "/../../../../../tools/flyway/src/test/resources/db-migration")
30 | FlywayConfig(
31 | locations = Seq(
32 | s"filesystem:${file.getAbsolutePath}"
33 | ),
34 | placeholderConfig = Some(
35 | PlaceholderConfig(
36 | placeholderReplacement = true,
37 | placeholders = Map("engineName" -> "MEMORY", "idSequenceNumberEngineName" -> "MyISAM")
38 | )
39 | )
40 | )
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/modules/interface/src/test/scala/com/github/j5ik2o/threadWeaver/adaptor/util/JdbcSpecSupport.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.util
2 |
3 | import org.scalatest.concurrent.ScalaFutures
4 |
5 | trait JdbcSpecSupport extends ScalaFutures {
6 | this: FlywayWithMySQLSpecSupport =>
7 | val tables: Seq[String]
8 |
9 | def jdbcPort: Int = mySQLdConfig.port.get
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/modules/interface/src/test/scala/com/github/j5ik2o/threadWeaver/adaptor/util/RandomPortSupport.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.util
2 |
3 | import java.net.InetSocketAddress
4 | import java.nio.channels.ServerSocketChannel
5 |
6 | /**
7 | * This code is originated from Spray.
8 | * https://github.com/spray/spray/blob/b473d9e8ce503bafc72825914f46ae6be1588ce7/spray-util/src/main/scala/spray/util/Utils.scala#L35-L47
9 | */
10 | trait RandomPortSupport {
11 |
12 | def temporaryServerAddress(interface: String = "127.0.0.1"): InetSocketAddress = {
13 | val serverSocket = ServerSocketChannel.open()
14 | try {
15 | serverSocket.socket.bind(new InetSocketAddress(interface, 0))
16 | val port = serverSocket.socket.getLocalPort
17 | new InetSocketAddress(interface, port)
18 | } finally serverSocket.close()
19 | }
20 |
21 | def temporaryServerHostnameAndPort(interface: String = "127.0.0.1"): (String, Int) = {
22 | val socketAddress = temporaryServerAddress(interface)
23 | socketAddress.getHostName -> socketAddress.getPort
24 | }
25 |
26 | def temporaryServerPort(interface: String = "127.0.0.1"): Int =
27 | temporaryServerHostnameAndPort(interface)._2
28 | }
29 |
30 | object RandomPortSupport extends RandomPortSupport
31 |
--------------------------------------------------------------------------------
/modules/interface/src/test/scala/com/github/j5ik2o/threadWeaver/adaptor/util/ScalaCheckSupport.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.util
2 |
3 | import org.scalacheck.Gen
4 | import org.scalatest.prop.PropertyChecks
5 |
6 | trait ScalaCheckSupport extends PropertyChecks {
7 |
8 | def sameAs[A](c: Traversable[A], d: Traversable[A]): Boolean = {
9 | def counts(e: Traversable[A]) = e groupBy identity mapValues (_.size)
10 | counts(c) == counts(d)
11 | }
12 |
13 | val timestampGen: Gen[Long] =
14 | Gen.choose(-24 * 60 * 60 * 1000, 24 * 60 * 60 * 1000).map(_ + System.currentTimeMillis())
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/modules/interface/src/test/scala/com/github/j5ik2o/threadWeaver/adaptor/util/ScalaFuturesSpecSupport.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.adaptor.util
2 |
3 | import org.scalatest.concurrent.ScalaFutures
4 | import org.scalatest.time.{ Seconds, Span }
5 |
6 | trait ScalaFuturesSpecSupport {
7 | this: ScalaFutures =>
8 | override implicit def patienceConfig: PatienceConfig =
9 | PatienceConfig(timeout = scaled(Span(180, Seconds)), interval = scaled(Span(3, Seconds)))
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/modules/use-case/src/main/scala/com/github/j5ik2o/threadWeaver/useCase/untyped/UseCaseSupport.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.useCase.untyped
2 |
3 | import monix.eval.Task
4 | import org.slf4j.LoggerFactory
5 |
6 | import scala.concurrent.Future
7 | import scala.concurrent.duration._
8 |
9 | trait UseCaseSupport {
10 |
11 | val maxRetries = 3
12 | val firstDelay = 300 milliseconds
13 |
14 | private val logger = LoggerFactory.getLogger(getClass)
15 |
16 | def retryBackoff[A](
17 | sourceFuture: Future[A],
18 | maxRetries: Int,
19 | firstDelay: FiniteDuration,
20 | retryMessage: String = ""
21 | ): Task[A] = {
22 | Task.fromFuture(sourceFuture).onErrorHandleWith {
23 | case ex: akka.pattern.AskTimeoutException =>
24 | if (maxRetries > 0) {
25 | // Recursive call, it's OK as Monix is stack-safe
26 | logger.info(s"Retrying($maxRetries)...: $retryMessage")
27 | retryBackoff(sourceFuture, maxRetries - 1, firstDelay * 2, retryMessage)
28 | .delayExecution(firstDelay)
29 | } else
30 | Task.raiseError(ex)
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/project/Utils.scala:
--------------------------------------------------------------------------------
1 | object Utils {
2 | import java.net.InetSocketAddress
3 | import java.nio.channels.ServerSocketChannel
4 |
5 | trait RandomPortSupport {
6 |
7 | def temporaryServerAddress(interface: String = "127.0.0.1"): InetSocketAddress = {
8 | val serverSocket = ServerSocketChannel.open()
9 | try {
10 | serverSocket.socket.bind(new InetSocketAddress(interface, 0))
11 | val port = serverSocket.socket.getLocalPort
12 | new InetSocketAddress(interface, port)
13 | } finally serverSocket.close()
14 | }
15 |
16 | def temporaryServerHostnameAndPort(interface: String = "127.0.0.1"): (String, Int) = {
17 | val socketAddress = temporaryServerAddress(interface)
18 | socketAddress.getHostName -> socketAddress.getPort
19 | }
20 |
21 | def temporaryServerPort(interface: String = "127.0.0.1"): Int =
22 | temporaryServerHostnameAndPort(interface)._2
23 | }
24 |
25 | object RandomPortSupport extends RandomPortSupport
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version = 1.3.8
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | resolvers ++= Seq(
2 | "Sonatype OSS Snapshot Repository" at "https://oss.sonatype.org/content/repositories/snapshots/",
3 | "Sonatype OSS Release Repository" at "https://oss.sonatype.org/content/repositories/releases/",
4 | "Seasar Repository" at "https://maven.seasar.org/maven2/",
5 | Resolver.bintrayRepo("kamon-io", "sbt-plugins")
6 | )
7 |
8 | libraryDependencies ++= Seq(
9 | "com.h2database" % "h2" % "1.4.195",
10 | "commons-io" % "commons-io" % "2.5",
11 | "org.seasar.util" % "s2util" % "0.0.1",
12 | "com.github.j5ik2o" %% "reactive-aws-ecs-core" % "1.1.3"
13 | )
14 |
15 | addSbtPlugin("io.kamon" % "sbt-aspectj-runner" % "1.1.0")
16 |
17 | addSbtPlugin("com.lightbend.sbt" % "sbt-javaagent" % "0.1.4")
18 |
19 | addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0")
20 |
21 | addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.0.0")
22 |
23 | addSbtPlugin("org.wartremover" % "sbt-wartremover" % "2.4.1")
24 |
25 | addSbtPlugin("com.typesafe.sbt" % "sbt-multi-jvm" % "0.4.0")
26 |
27 | addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.10")
28 |
29 | addSbtPlugin("io.gatling" % "gatling-sbt" % "2.2.2")
30 |
31 | addSbtPlugin("io.spray" % "sbt-revolver" % "0.9.1")
32 |
33 | addSbtPlugin("org.lyranthe.sbt" % "partial-unification" % "1.1.2")
34 |
35 | addSbtPlugin("io.github.davidmweber" % "flyway-sbt" % "5.2.0")
36 |
37 | addSbtPlugin("jp.co.septeni-original" % "sbt-dao-generator" % "1.0.8")
38 |
39 | addSbtPlugin("com.chatwork" % "sbt-wix-embedded-mysql" % "1.0.9")
40 |
41 | addSbtPlugin("com.lightbend.akka.grpc" % "sbt-akka-grpc" % "0.6.1")
42 |
43 | addSbtPlugin("com.mintbeans" % "sbt-ecr" % "0.14.1")
44 |
45 | addSbtPlugin("org.ensime" % "sbt-ensime" % "2.5.1")
46 |
--------------------------------------------------------------------------------
/read-model-updater/src/main/resources/application.conf:
--------------------------------------------------------------------------------
1 | include classpath("common/akka.conf")
2 | include classpath("local-machine/akka.conf")
3 | include classpath("common/kamon.conf")
4 | include classpath("common/thread-weaver.conf")
5 | include classpath("common/k8s_probe.conf")
6 | include classpath("common/kamon.conf")
7 |
8 |
--------------------------------------------------------------------------------
/read-model-updater/src/main/resources/common/k8s_probe.conf:
--------------------------------------------------------------------------------
1 | k8s_probe {
2 | host = "localhost"
3 | port = "18080"
4 | port = ${?THREAD_WEAVER_API_SERVER_HTTP_PORT}
5 | path {
6 | liveness = "/live"
7 | readiness = "/ready"
8 | }
9 | }
--------------------------------------------------------------------------------
/read-model-updater/src/main/resources/common/thread-weaver.conf:
--------------------------------------------------------------------------------
1 | thread-weaver {
2 | api-server {
3 | host = "0.0.0.0"
4 | http {
5 | port = 8080
6 | port = ${?THREAD_WEAVER_API_SERVER_HTTP_PORT}
7 | }
8 | terminate.duration = 3s
9 | }
10 |
11 | read-model-updater.sql-batch-count = 1
12 | read-model-updater.sql-batch-count = ${?THREAD_WEAVER_READ_MODEL_UPDATER_SQL_BATCH_COUNT}
13 | read-model-updater {
14 | nr-of-shards = 30
15 | nr-of-shards = ${?THREAD_WEAVER_READ_MODEL_UPDATER_NR_OF_SHARDS}
16 | }
17 | read-model-updater.room {
18 | shard-name = "room"
19 | category = "room"
20 | num-partition = 1
21 | num-partition = ${?THREAD_WEAVER_READ_MODEL_UPDATER_ROOM_NUM_PARTITION}
22 | }
23 |
24 | }
25 |
26 | slick {
27 | profile = "slick.jdbc.MySQLProfile$"
28 | db {
29 | driver = "com.mysql.jdbc.Driver"
30 | url = "jdbc:mysql://localhost:3306/tw?useSSL=false"
31 | url = ${?THREAD_WEAVER_SLICK_URL}
32 | user = "tw"
33 | user = ${?THREAD_WEAVER_SLICK_USER}
34 | password = "passwd"
35 | password = ${?THREAD_WEAVER_SLICK_PASSWORD}
36 | connectionPool = "HikariCP"
37 | keepAliveConnection = true
38 | properties = {
39 | maximumPoolSize = 64
40 | maximumPoolSize = ${?THREAD_WEAVER_SLICK_MAX_POOL_SIZE}
41 | minimumIdle = 64
42 | minimumIdle = ${?THREAD_WEAVER_SLICK_MIN_IDLE_SIZE}
43 | connectionTimeout = 30
44 | connectionTimeout = ${?THREAD_WEAVER_SLICK_CONNECTION_TIMEOUT}
45 | idleTimeout = 30
46 | idleTimeout = ${?THREAD_WEAVER_SLICK_IDLE_TIMEOUT}
47 | }
48 | poolName = "slick-pool"
49 | poolName = ${?THREAD_WEAVER_SLICK_POOL_NAME}
50 | numThreads = 64
51 | numThreads = ${?THREAD_WEAVER_SLICK_NUM_THREADS}
52 | queueSize = 1000
53 | queueSize = ${?THREAD_WEAVER_SLICK_QUEUE_SIZE}
54 | registerMbeans=true
55 | }
56 | }
--------------------------------------------------------------------------------
/read-model-updater/src/main/resources/local-cluster.conf:
--------------------------------------------------------------------------------
1 | include classpath("common/akka.conf")
2 | include classpath("local-cluster/akka.conf")
3 | include classpath("common/kamon.conf")
4 | include classpath("common/thread-weaver.conf")
5 | include classpath("common/k8s_probe.conf")
6 | include classpath("common/kamon.conf")
7 |
--------------------------------------------------------------------------------
/read-model-updater/src/main/resources/production.conf:
--------------------------------------------------------------------------------
1 | include classpath("common/akka.conf")
2 | include classpath("production/akka.conf")
3 | include classpath("common/kamon.conf")
4 | include classpath("common/thread-weaver.conf")
5 | include classpath("common/k8s_probe.conf")
6 | include classpath("common/kamon.conf")
7 |
--------------------------------------------------------------------------------
/read-model-updater/src/main/scala/com/github/j5ik2o/threadWeaver/rmu/ClusterWatcher.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.rmu
2 |
3 | import akka.actor.{ Actor, ActorLogging }
4 | import akka.cluster.Cluster
5 |
6 | class ClusterWatcher extends Actor with ActorLogging {
7 | private val cluster = Cluster(context.system)
8 |
9 | override def receive: PartialFunction[Any, Unit] = {
10 | case msg => log.info(s"Cluster ${cluster.selfAddress} >>> " + msg)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/read-model-updater/src/main/scala/com/github/j5ik2o/threadWeaver/rmu/HealthCheck.scala:
--------------------------------------------------------------------------------
1 | package com.github.j5ik2o.threadWeaver.rmu
2 |
3 | import akka.actor.ActorSystem
4 | import akka.cluster.{ Cluster, MemberStatus }
5 | import cats.syntax.validated._
6 | import com.github.everpeace.healthchecks._
7 |
8 | import scala.concurrent.Future
9 |
10 | object HealthCheck {
11 |
12 | def akka(host: String, port: Int)(implicit system: ActorSystem): HealthCheck = {
13 | import system.dispatcher
14 | asyncHealthCheck("akka-cluster") {
15 | Future {
16 | val cluster = Cluster(system)
17 | val status = cluster.selfMember.status
18 | val result = status == MemberStatus.Up || status == MemberStatus.WeaklyUp
19 | if (result)
20 | healthy
21 | else
22 | "Not Found".invalidNel
23 |
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/slide/.gitignore:
--------------------------------------------------------------------------------
1 | .tmp
--------------------------------------------------------------------------------
/slide/domain-models.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 |
3 | class Thread {
4 | + type Result[A] = Either[ThreadError, A]
5 | + id: ThreadId
6 |
7 | + def joinAdministratorIds(value: AdministratorIds, senderId: AccountId, at: Instant): Result[Thread]
8 | + def leaveAdministratorIds(value: AdministratorIds, senderId: AccountId, at: Instant): Result[Thread]
9 | + def getAdministratorIds(senderId: AccountId): Result[AdministratorIds]
10 |
11 | + def joinMemberIds(value: MemberIds, senderId: AccountId, at: Instant): Result[Thread]
12 | + def leaveMemberIds(value: MemberIds, senderId: AccountId, at: Instant): Result[Thread]
13 | + def getMemberIds(senderId: AccountId): Result[MemberIds]
14 |
15 | + def addMessages(values: Messages, at: Instant): Result[Thread]
16 | + def removeMessages(values: MessageIds, removerId: AccountId, at: Instant): Result[(Thread, MessageIds)]
17 | + def getMessages(senderId: AccountId): Result[Messages]
18 |
19 | + def destroy(senderId: AccountId, at: Instant): Result[Thread]
20 | }
21 |
22 | abstract class ThreadEvent {
23 | + id: ThreadEventId
24 | + threadId: ThreadId
25 | + createAt: Instant
26 | }
27 |
28 | class ThreadCreated extends ThreadEvent
29 | class ThreadDestroyed extends ThreadEvent
30 | class AdministratorIdsJoined extends ThreadEvent
31 | class AdministratorIdsLeft extends ThreadEvent
32 | class MemberIdsJoined extends ThreadEvent
33 | class MemberIdsLeft extends ThreadEvent
34 | class MessagesAdded extends ThreadEvent
35 | class MessagesRemoved extends ThreadEvent
36 |
37 | @enduml
--------------------------------------------------------------------------------
/slide/images/K0001062281.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/K0001062281.jpg
--------------------------------------------------------------------------------
/slide/images/arete_files/celebrating-50-years-of-pride-6537357791592448.3-s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/arete_files/celebrating-50-years-of-pride-6537357791592448.3-s.png
--------------------------------------------------------------------------------
/slide/images/arete_files/free_transration_online.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/arete_files/free_transration_online.html
--------------------------------------------------------------------------------
/slide/images/arete_files/id414706506:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/arete_files/id414706506
--------------------------------------------------------------------------------
/slide/images/arete_files/saved_resource:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/arete_files/saved_resource
--------------------------------------------------------------------------------
/slide/images/arete_files/saved_resource(1):
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/arete_files/saved_resource(1)
--------------------------------------------------------------------------------
/slide/images/arete_files/saved_resource(2):
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/arete_files/saved_resource(2)
--------------------------------------------------------------------------------
/slide/images/arete_files/saved_resource(3):
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/arete_files/saved_resource(3)
--------------------------------------------------------------------------------
/slide/images/arete_files/saved_resource(4):
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/arete_files/saved_resource(4)
--------------------------------------------------------------------------------
/slide/images/arete_files/saved_resource(5):
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/arete_files/saved_resource(5)
--------------------------------------------------------------------------------
/slide/images/arete_files/saved_resource(6):
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/arete_files/saved_resource(6)
--------------------------------------------------------------------------------
/slide/images/arete_files/search:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/arete_files/search
--------------------------------------------------------------------------------
/slide/images/arete_files/翻訳:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/arete_files/翻訳
--------------------------------------------------------------------------------
/slide/images/clean-architecture.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/clean-architecture.jpeg
--------------------------------------------------------------------------------
/slide/images/event-stream.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/event-stream.png
--------------------------------------------------------------------------------
/slide/images/helm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/helm.png
--------------------------------------------------------------------------------
/slide/images/logo-hz.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/logo-hz.png
--------------------------------------------------------------------------------
/slide/images/logo-vt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/logo-vt.png
--------------------------------------------------------------------------------
/slide/images/real-events.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/real-events.png
--------------------------------------------------------------------------------
/slide/images/self-prof.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/images/self-prof.png
--------------------------------------------------------------------------------
/slide/modules.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | [modules/infrastructure]
3 |
4 | [modules/domain] -right-> [modules/infrastructure]
5 | [modules/use-case] -right-> [modules/infrastructure]
6 | [modules/interface] -right-> [modules/infrastructure]
7 | [applications/api-server] -right-> [modules/infrastructure]
8 |
9 | [modules/use-case] .up.> [contracts/contract-use-case]
10 | [modules/use-case] .up.> [contracts/contract-interface]
11 | [modules/use-case] -up-> [modules/domain]
12 | [modules/interface] -up-> [modules/domain]
13 | [applications/api-server] -up-> [modules/domain]
14 | [contracts/contract-interface] .up.> [contracts/contract-use-case]
15 | [contracts/contract-interface] .up.> [contracts/contract-http-proto-interface]
16 | [modules/interface] .up.> [contracts/contract-interface]
17 | [modules/interface] -up-> [modules/use-case]
18 | [applications/api-server] -up-> [modules/interface]
19 | [modules/api-client] .up.> [contracts/contract-http-proto-interface]
20 |
21 |
22 | @enduml
--------------------------------------------------------------------------------
/slide/pdf/presentation.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/j5ik2o/thread-weaver/39896c6e9ff05a279b3eb300965cd0e560c3150f/slide/pdf/presentation.pdf
--------------------------------------------------------------------------------
/slide/template/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{title}}
6 | {{{style}}}
7 |
8 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/slide/thread-aggregates.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 |
3 | class Thread
4 |
5 | class ThreadAggregate {
6 | val state: Thread
7 | }
8 |
9 | class PersistentThreadAggregate {
10 | val childRef: ActorRef
11 | }
12 |
13 |
14 | class ThreadAggregates {
15 | val childRef: ActorRef
16 | }
17 |
18 | class ShardedThreadAggregates{
19 | }
20 |
21 | ShardedThreadAggregates -right-|> ThreadAggregates: <>
22 | ThreadAggregate .down.> Thread: <