From 23dbe6e47fe324a582735383eec548ee39307e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=98=81?= <80161826+dolong2@users.noreply.github.com> Date: Sat, 11 Jul 2026 04:39:10 +0000 Subject: [PATCH 1/4] =?UTF-8?q?[feat]=20=EC=95=A0=ED=94=8C=EB=A6=AC?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=85=98=20=EC=BB=A8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=84=88=20=EC=9E=AC=EC=83=9D=EC=84=B1=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=EC=9D=84=20=EC=84=9C=EB=B9=84=EC=8A=A4=EB=A1=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/RefreshApplicationService.kt | 7 ++++ .../impl/RefreshApplicationServiceImpl.kt | 40 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/main/kotlin/com/dcd/server/core/domain/application/service/RefreshApplicationService.kt create mode 100644 src/main/kotlin/com/dcd/server/core/domain/application/service/impl/RefreshApplicationServiceImpl.kt diff --git a/src/main/kotlin/com/dcd/server/core/domain/application/service/RefreshApplicationService.kt b/src/main/kotlin/com/dcd/server/core/domain/application/service/RefreshApplicationService.kt new file mode 100644 index 00000000..b63d32b4 --- /dev/null +++ b/src/main/kotlin/com/dcd/server/core/domain/application/service/RefreshApplicationService.kt @@ -0,0 +1,7 @@ +package com.dcd.server.core.domain.application.service + +import com.dcd.server.core.domain.application.model.Application + +interface RefreshApplicationService { + suspend fun refresh(application: Application) +} \ No newline at end of file diff --git a/src/main/kotlin/com/dcd/server/core/domain/application/service/impl/RefreshApplicationServiceImpl.kt b/src/main/kotlin/com/dcd/server/core/domain/application/service/impl/RefreshApplicationServiceImpl.kt new file mode 100644 index 00000000..dd36aa3c --- /dev/null +++ b/src/main/kotlin/com/dcd/server/core/domain/application/service/impl/RefreshApplicationServiceImpl.kt @@ -0,0 +1,40 @@ +package com.dcd.server.core.domain.application.service.impl + +import com.dcd.server.core.common.spi.ContainerPort +import com.dcd.server.core.domain.application.model.Application +import com.dcd.server.core.domain.application.model.enums.ApplicationType +import com.dcd.server.core.domain.application.spi.ApplicationImageFilePort +import com.dcd.server.core.domain.application.spi.ApplicationRemoteRepoPort +import com.dcd.server.core.domain.application.service.DeleteApplicationDirectoryService +import com.dcd.server.core.domain.application.service.RefreshApplicationService +import com.dcd.server.core.domain.volume.spi.QueryVolumePort +import org.springframework.stereotype.Service + +@Service +class RefreshApplicationServiceImpl( + private val containerPort: ContainerPort, + private val applicationRemoteRepoPort: ApplicationRemoteRepoPort, + private val applicationImageFilePort: ApplicationImageFilePort, + private val deleteApplicationDirectoryService: DeleteApplicationDirectoryService, + private val queryVolumePort: QueryVolumePort +) : RefreshApplicationService { + override suspend fun refresh(application: Application) { + val applicationType = application.applicationType + when(applicationType) { + ApplicationType.SPRING_BOOT, ApplicationType.NEST_JS, ApplicationType.GIN -> { + applicationRemoteRepoPort.cloneApplicationRemoteRepo(application) + } + else -> {} + } + + applicationImageFilePort.createImageFile(application) + + containerPort.execute { + buildImage(application) + val volumeMounts = queryVolumePort.findAllMountByApplication(application) + createContainer(application, volumeMounts) + } + + deleteApplicationDirectoryService.deleteApplicationDirectory(application) + } +} \ No newline at end of file From 6e239cfcac6577983242783915c05c170fe4a942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=98=81?= <80161826+dolong2@users.noreply.github.com> Date: Sat, 11 Jul 2026 04:40:14 +0000 Subject: [PATCH 2/4] =?UTF-8?q?[refac]=20=EC=95=A0=ED=94=8C=EB=A6=AC?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=85=98=20=EC=BB=A8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=84=88=20=EC=83=9D=EC=84=B1=20=EB=A1=9C=EC=A7=81=EC=9D=84=20?= =?UTF-8?q?RefreshApplicationService=EB=A1=9C=20=EB=A6=AC=ED=8E=99?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../listener/ApplicationEventListener.kt | 28 +++------------ .../usecase/CreateApplicationUseCase.kt | 29 ++-------------- .../usecase/DeployApplicationUseCase.kt | 34 ++++--------------- .../usecase/UpdateApplicationUseCase.kt | 30 ++-------------- 4 files changed, 17 insertions(+), 104 deletions(-) diff --git a/src/main/kotlin/com/dcd/server/core/domain/application/event/listener/ApplicationEventListener.kt b/src/main/kotlin/com/dcd/server/core/domain/application/event/listener/ApplicationEventListener.kt index 6df9cf58..7887428c 100644 --- a/src/main/kotlin/com/dcd/server/core/domain/application/event/listener/ApplicationEventListener.kt +++ b/src/main/kotlin/com/dcd/server/core/domain/application/event/listener/ApplicationEventListener.kt @@ -5,13 +5,9 @@ import com.dcd.server.core.domain.application.event.ChangeApplicationStatusEvent import com.dcd.server.core.domain.application.event.DeployApplicationEvent import com.dcd.server.core.domain.application.model.DeploymentResult import com.dcd.server.core.domain.application.model.enums.ApplicationStatus -import com.dcd.server.core.domain.application.model.enums.ApplicationType -import com.dcd.server.core.domain.application.service.DeleteApplicationDirectoryService -import com.dcd.server.core.domain.application.spi.ApplicationRemoteRepoPort -import com.dcd.server.core.domain.application.spi.ApplicationImageFilePort +import com.dcd.server.core.domain.application.service.RefreshApplicationService import com.dcd.server.core.domain.application.spi.CommandApplicationPort import com.dcd.server.core.domain.application.spi.QueryApplicationPort -import com.dcd.server.core.domain.volume.spi.QueryVolumePort import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -23,13 +19,10 @@ import org.springframework.transaction.event.TransactionalEventListener @Component class ApplicationEventListener( + private val refreshApplicationService: RefreshApplicationService, private val commandApplicationPort: CommandApplicationPort, - private val applicationRemoteRepoPort: ApplicationRemoteRepoPort, - private val applicationImageFilePort: ApplicationImageFilePort, - private val deleteApplicationDirectoryService: DeleteApplicationDirectoryService, private val containerPort: ContainerPort, - private val queryApplicationPort: QueryApplicationPort, - private val queryVolumePort: QueryVolumePort + private val queryApplicationPort: QueryApplicationPort ) { @EventListener @Transactional(rollbackFor = [Exception::class]) @@ -53,20 +46,7 @@ class ApplicationEventListener( } CoroutineScope(Dispatchers.IO).launch { - val applicationType = application.applicationType - when(applicationType) { - ApplicationType.SPRING_BOOT, ApplicationType.NEST_JS, ApplicationType.GIN -> { - applicationRemoteRepoPort.cloneApplicationRemoteRepo(application) - } - else -> {} - } - applicationImageFilePort.createImageFile(application) - - containerPort.execute { - buildImage(application) - val volumeMounts = queryVolumePort.findAllMountByApplication(application) - createContainer(application, volumeMounts) - } + refreshApplicationService.refresh(application) val updatedApplication = application.copy(status = ApplicationStatus.STOPPED) commandApplicationPort.save(updatedApplication) diff --git a/src/main/kotlin/com/dcd/server/core/domain/application/usecase/CreateApplicationUseCase.kt b/src/main/kotlin/com/dcd/server/core/domain/application/usecase/CreateApplicationUseCase.kt index 3641b74f..08d9edc8 100644 --- a/src/main/kotlin/com/dcd/server/core/domain/application/usecase/CreateApplicationUseCase.kt +++ b/src/main/kotlin/com/dcd/server/core/domain/application/usecase/CreateApplicationUseCase.kt @@ -8,15 +8,12 @@ import com.dcd.server.core.domain.application.dto.request.CreateApplicationReqDt import com.dcd.server.core.domain.application.dto.response.CreateApplicationResDto import com.dcd.server.core.domain.application.exception.AlreadyExistsApplicationException import com.dcd.server.core.domain.application.model.enums.ApplicationType -import com.dcd.server.core.domain.application.service.DeleteApplicationDirectoryService import com.dcd.server.core.domain.application.service.GetExternalPortService +import com.dcd.server.core.domain.application.service.RefreshApplicationService import com.dcd.server.core.domain.application.service.InitialScriptService -import com.dcd.server.core.domain.application.spi.ApplicationRemoteRepoPort -import com.dcd.server.core.domain.application.spi.ApplicationImageFilePort import com.dcd.server.core.domain.application.spi.CommandApplicationPort import com.dcd.server.core.domain.application.spi.QueryApplicationPort import com.dcd.server.core.domain.env.service.EnvAutoMatchService -import com.dcd.server.core.domain.volume.spi.QueryVolumePort import com.dcd.server.core.domain.workspace.exception.WorkspaceNotFoundException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -27,12 +24,8 @@ class CreateApplicationUseCase( private val commandApplicationPort: CommandApplicationPort, private val queryApplicationPort: QueryApplicationPort, private val workspaceInfo: WorkspaceInfo, - private val containerPort: ContainerPort, - private val queryVolumePort: QueryVolumePort, - private val applicationRemoteRepoPort: ApplicationRemoteRepoPort, - private val applicationImageFilePort: ApplicationImageFilePort, private val getExternalPortService: GetExternalPortService, - private val deleteApplicationDirectoryService: DeleteApplicationDirectoryService, + private val refreshApplicationService: RefreshApplicationService, private val envAutoMatchService: EnvAutoMatchService, private val initialScriptService: InitialScriptService ) : CoroutineScope by CoroutineScope(Dispatchers.IO) { @@ -52,23 +45,7 @@ class CreateApplicationUseCase( initialScriptService.write(application, createApplicationReqDto.initialScripts) launch { - val applicationType = application.applicationType - when(applicationType) { - ApplicationType.SPRING_BOOT, ApplicationType.NEST_JS, ApplicationType.GIN -> { - applicationRemoteRepoPort.cloneApplicationRemoteRepo(application) - } - else -> {} - } - - applicationImageFilePort.createImageFile(application) - - containerPort.execute { - buildImage(application) - val volumeMounts = queryVolumePort.findAllMountByApplication(application) - createContainer(application, volumeMounts) - } - - deleteApplicationDirectoryService.deleteApplicationDirectory(application) + refreshApplicationService.refresh(application) } return CreateApplicationResDto(application.id) diff --git a/src/main/kotlin/com/dcd/server/core/domain/application/usecase/DeployApplicationUseCase.kt b/src/main/kotlin/com/dcd/server/core/domain/application/usecase/DeployApplicationUseCase.kt index c62953da..0259068c 100644 --- a/src/main/kotlin/com/dcd/server/core/domain/application/usecase/DeployApplicationUseCase.kt +++ b/src/main/kotlin/com/dcd/server/core/domain/application/usecase/DeployApplicationUseCase.kt @@ -10,11 +10,8 @@ import com.dcd.server.core.domain.application.exception.CanNotDeployApplicationE import com.dcd.server.core.domain.application.model.Application import com.dcd.server.core.domain.application.model.enums.ApplicationStatus import com.dcd.server.core.domain.application.model.enums.ApplicationType -import com.dcd.server.core.domain.application.service.DeleteApplicationDirectoryService -import com.dcd.server.core.domain.application.spi.ApplicationRemoteRepoPort -import com.dcd.server.core.domain.application.spi.ApplicationImageFilePort +import com.dcd.server.core.domain.application.service.RefreshApplicationService import com.dcd.server.core.domain.application.spi.QueryApplicationPort -import com.dcd.server.core.domain.volume.spi.QueryVolumePort import com.dcd.server.core.domain.workspace.exception.WorkspaceNotFoundException import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel @@ -24,10 +21,7 @@ import org.springframework.context.ApplicationEventPublisher class DeployApplicationUseCase( private val queryApplicationPort: QueryApplicationPort, private val containerPort: ContainerPort, - private val queryVolumePort: QueryVolumePort, - private val applicationRemoteRepoPort: ApplicationRemoteRepoPort, - private val applicationImageFilePort: ApplicationImageFilePort, - private val deleteApplicationDirectoryService: DeleteApplicationDirectoryService, + private val refreshApplicationService: RefreshApplicationService, private val lockPort: LockPort, private val eventPublisher: ApplicationEventPublisher, private val workspaceInfo: WorkspaceInfo @@ -83,28 +77,14 @@ class DeployApplicationUseCase( } private suspend fun deployApplication(application: Application) { - containerPort.execute { - deleteContainer(application) - deleteImage(application) - - runBlocking { - val applicationType = application.applicationType - when(applicationType) { - ApplicationType.SPRING_BOOT, ApplicationType.NEST_JS, ApplicationType.GIN -> { - applicationRemoteRepoPort.cloneApplicationRemoteRepo(application) - } - else -> {} - } - applicationImageFilePort.createImageFile(application) + runBlocking { + containerPort.execute { + deleteContainer(application) + deleteImage(application) } - buildImage(application) - val volumeMounts = queryVolumePort.findAllMountByApplication(application) - createContainer(application, volumeMounts) + refreshApplicationService.refresh(application) - runBlocking { - deleteApplicationDirectoryService.deleteApplicationDirectory(application) - } eventPublisher.publishEvent(ChangeApplicationStatusEvent(ApplicationStatus.STOPPED, application)) } } diff --git a/src/main/kotlin/com/dcd/server/core/domain/application/usecase/UpdateApplicationUseCase.kt b/src/main/kotlin/com/dcd/server/core/domain/application/usecase/UpdateApplicationUseCase.kt index 94c0832b..587feb7c 100644 --- a/src/main/kotlin/com/dcd/server/core/domain/application/usecase/UpdateApplicationUseCase.kt +++ b/src/main/kotlin/com/dcd/server/core/domain/application/usecase/UpdateApplicationUseCase.kt @@ -8,14 +8,10 @@ import com.dcd.server.core.domain.application.event.ChangeApplicationStatusEvent import com.dcd.server.core.domain.application.exception.AlreadyRunningException import com.dcd.server.core.domain.application.exception.ApplicationNotFoundException import com.dcd.server.core.domain.application.model.enums.ApplicationStatus -import com.dcd.server.core.domain.application.model.enums.ApplicationType import com.dcd.server.core.domain.application.service.InitialScriptService -import com.dcd.server.core.domain.application.service.DeleteApplicationDirectoryService -import com.dcd.server.core.domain.application.spi.ApplicationImageFilePort -import com.dcd.server.core.domain.application.spi.ApplicationRemoteRepoPort +import com.dcd.server.core.domain.application.service.RefreshApplicationService import com.dcd.server.core.domain.application.spi.CommandApplicationPort import com.dcd.server.core.domain.application.spi.QueryApplicationPort -import com.dcd.server.core.domain.volume.spi.QueryVolumePort import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking @@ -26,10 +22,7 @@ import org.springframework.context.ApplicationEventPublisher class UpdateApplicationUseCase( private val queryApplicationPort: QueryApplicationPort, private val commandApplicationPort: CommandApplicationPort, - private val applicationRemoteRepoPort: ApplicationRemoteRepoPort, - private val applicationImageFilePort: ApplicationImageFilePort, - private val deleteApplicationDirectoryService: DeleteApplicationDirectoryService, - private val queryVolumePort: QueryVolumePort, + private val refreshApplicationService: RefreshApplicationService, private val containerPort: ContainerPort, private val eventPublisher: ApplicationEventPublisher, private val initialScriptService: InitialScriptService @@ -59,24 +52,7 @@ class UpdateApplicationUseCase( launch { eventPublisher.publishEvent(ChangeApplicationStatusEvent(ApplicationStatus.PENDING, updatedApplication)) - // 이름이 변경된 애플리케이션의 이미지및, 컨테이너 생성 - val applicationType = updatedApplication.applicationType - when(applicationType) { - ApplicationType.SPRING_BOOT, ApplicationType.NEST_JS, ApplicationType.GIN -> { - applicationRemoteRepoPort.cloneApplicationRemoteRepo(updatedApplication) - } - else -> {} - } - - applicationImageFilePort.createImageFile(updatedApplication) - - containerPort.execute { - buildImage(updatedApplication) - val volumeMounts = queryVolumePort.findAllMountByApplication(updatedApplication) - createContainer(updatedApplication, volumeMounts) - } - - deleteApplicationDirectoryService.deleteApplicationDirectory(updatedApplication) + refreshApplicationService.refresh(updatedApplication) // 이름이 변경되기 전 애플리케이션의 이미지및, 컨테이너 제거 containerPort.execute { From 7fdb8cd75ba3cdbe1f4701a73b0fcdf106758c67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=98=81?= <80161826+dolong2@users.noreply.github.com> Date: Sat, 11 Jul 2026 04:45:01 +0000 Subject: [PATCH 3/4] =?UTF-8?q?[feat]=20RefreshApplicationService=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=EC=B2=B4=EC=97=90=20Lock=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/service/impl/RefreshApplicationServiceImpl.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/kotlin/com/dcd/server/core/domain/application/service/impl/RefreshApplicationServiceImpl.kt b/src/main/kotlin/com/dcd/server/core/domain/application/service/impl/RefreshApplicationServiceImpl.kt index dd36aa3c..e5d99960 100644 --- a/src/main/kotlin/com/dcd/server/core/domain/application/service/impl/RefreshApplicationServiceImpl.kt +++ b/src/main/kotlin/com/dcd/server/core/domain/application/service/impl/RefreshApplicationServiceImpl.kt @@ -1,5 +1,6 @@ package com.dcd.server.core.domain.application.service.impl +import com.dcd.server.core.common.annotation.Lock import com.dcd.server.core.common.spi.ContainerPort import com.dcd.server.core.domain.application.model.Application import com.dcd.server.core.domain.application.model.enums.ApplicationType @@ -18,6 +19,7 @@ class RefreshApplicationServiceImpl( private val deleteApplicationDirectoryService: DeleteApplicationDirectoryService, private val queryVolumePort: QueryVolumePort ) : RefreshApplicationService { + @Lock("#application.id", waitTime = 1000 * 10, leaseTime = 1000 * 60 * 3) override suspend fun refresh(application: Application) { val applicationType = application.applicationType when(applicationType) { From a1ae48c3ad708be576b34250127e49b2111dddad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=98=81?= <80161826+dolong2@users.noreply.github.com> Date: Sat, 11 Jul 2026 05:42:27 +0000 Subject: [PATCH 4/4] =?UTF-8?q?[refac]=20=EC=95=A0=ED=94=8C=EB=A6=AC?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=85=98=20=EC=88=98=EC=A0=95=EC=8B=9C=20?= =?UTF-8?q?=EC=BB=A8=ED=85=8C=EC=9D=B4=EB=84=88=20=EC=9E=AC=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=9D=B4=20=ED=95=84=EC=9A=94=ED=95=98=EB=8B=A4?= =?UTF-8?q?=EB=A9=B4=20PENDING=20=EC=83=81=ED=83=9C=EB=A1=9C=20=EC=BD=94?= =?UTF-8?q?=EB=A3=A8=ED=8B=B4=20=EC=A7=84=EC=9E=85=EC=A0=84=EC=97=90=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/application/usecase/UpdateApplicationUseCase.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/kotlin/com/dcd/server/core/domain/application/usecase/UpdateApplicationUseCase.kt b/src/main/kotlin/com/dcd/server/core/domain/application/usecase/UpdateApplicationUseCase.kt index 587feb7c..bc1ae6c4 100644 --- a/src/main/kotlin/com/dcd/server/core/domain/application/usecase/UpdateApplicationUseCase.kt +++ b/src/main/kotlin/com/dcd/server/core/domain/application/usecase/UpdateApplicationUseCase.kt @@ -49,9 +49,8 @@ class UpdateApplicationUseCase( initialScriptService.write(updatedApplication, updateApplicationReqDto.initialScripts) if (application.name != updateApplicationReqDto.name) { + eventPublisher.publishEvent(ChangeApplicationStatusEvent(ApplicationStatus.PENDING, updatedApplication)) launch { - eventPublisher.publishEvent(ChangeApplicationStatusEvent(ApplicationStatus.PENDING, updatedApplication)) - refreshApplicationService.refresh(updatedApplication) // 이름이 변경되기 전 애플리케이션의 이미지및, 컨테이너 제거