개발/고민과 생각

레이어 분리 (deprecated)

hojak99 2020. 8. 22. 17:25

지금 보니 좀 문제가 있어 보인다. DTO 로 convert 하는 식으로 하면 JPA 를 사용하는 이유가 없어진다. 애초에 서로 도메인의 경계를 잘 나누면 된다. 그래도 옛날의 나의 생각이었기에 글은 냅두도록 한다.

 

 

개발을 하다 보면 레이어 분리를 어떻게 해야 잘 하는지에 대한 고민을 하게 된다. 

모놀리틱에서 개발을 한다고 했을 때 보통 나는 다음과 같은 레이어로 분리를 하여 개발을 하게 된다.

- serivce
- repository
- domain
- controller

일반적인 jpa + spring 사용 시에 나오는 패키지 구조이다.
presentation, business, persistence 레이어로 분리했다고 볼 수도 있을 것 같다.

 


 

service 클래스에서는 repository 나 다른 service 클래스를 DI 받아서 사용한다고 했을 때 각각 다음과 같은 repository 와 service 클래스가 존재한다고 해보자.

- A repository
- B repository 
- Q service
- W service
- E service

@Service
class QService(
    private val aRepository: ARepository
) {

	fun ... () {   ...   }	
}

 

@Service
class WService(
    private val bRepository: BRepository,
    private val aRepository: ARepository
) {

	fun ...() {  ...  }
}

 

@Service
class EService(
    private val aRepository: ARepository,
    private val bRepository: BRepository,
    private val wService: WService
) {

	fun ...() {		...		}
}

 

간단하게 이야기하자면 이미 W Service 에서 a Repository 를 DI 받아 의존관계가 생긴 상태에서 E Service 에서도 a Repository 와 b Repository, w Service 까지 DI 받아 의존관계가 생겼다는 것이다.

나는 이렇게 의존 관계가 설정되는 것이 좋지 않다고 생각한다.
우선 트랜잭션이 걸린 상태라고 했을 때 어쩌다가 entity 가 우연찮게 변경이 된다면 하이버네이트 dirty checking 으로 인해서 업데이트가 될 수도 있기 때문이다. 

여러 사람이 개발하게 될 수록 이런 실수가 생길 수도 있을 가능성이 높을 것 같다고 생각한다.

그리고 entity 를 관리하게 되는 곳이 너무 많아진다. 
a Repository, b Repository 에 정의된 메소드를 사용해서 entity 를 조회한다고 했을 때 w Service 에서도 값 변경, 업데이트, 조회를 구현할 수도 있고, e Service 에서도 구현할 수 있다. 

그만큼 entity 클래스를 변경했을 때 영향이 가는 부분이 많아진다는 것이다. 


나도 요즘 빨리 개발만 한다고 좀 생각하지 않고 개발을 하다 보니 위에서 얘기한 부분을 좀 놓친 것 같다.

그래서 우선 위와 같이 의존관계가 복잡해지고 여러 곳에서 entity 를 관리할 수 있게 하지 않으려고 아래와 같이 개발을 했다.

- A repository
- B repository 
- Q service
- W service
- E service

@Service
class QService(
    private val aRepository: ARepository
) {

    fun getA(id: Int): AResult {
    	val entity = aRepository.findById(id) ?: throw ...
       	return AResult.convert(entity)
    }
    
    fun updateA(updateARequest: a) {
    	val entity = getA(a.id)
        entity.update(updateARequest)
       
        aRepository.save(entity)
    }
}

 

@Service
class WService(
    private val bRepository: BRepository 
) {

    fun getB(id: Int): BResult {
    	val entity = bRepository.findById(id) ?: throw ...
       	return BResult.convert(entity)
    }
    
    fun updateB(updateBRequest: a) {
    	val entity = getB(a.id)
        entity.update(updateBRequest)
       
        bRepository.save(entity)
    }

}


 

@Service
class EService(
    private val qService: QService,
    private val wService: WService
) {

    fun calc(request: Request) {
		...
    }    
}

 

물론 상황마다 다르겠지만 정리하자면 dto 를 활용해서 entity 를 관리하는 곳이 많아지게 하지 않고, 복잡한 의존관계가 설정되지 않도록 해서 추후 변경이나 추가가 됐을 때 영향이 많이 가지 않도록 했다.

물론 DTO 도 그만큼 많이 생기지만 패키지로 잘 분리를 해 놓으면 또 보기 좋은 것 같기도 하다.

물론 저런 식이 아니라 다른 식으로 해도 되지만 나는 보통 저런 방식으로 한다.
다른 책들을 읽고 개발 방식이 변경되면 또 글을 쓰도록 하겠다.

반응형