네이버 블로그 주소 변경 - ID 말고 원하는 URL로 도메인 바꾸기
도메인 도메인변경 모델 패턴 실습 프로젝트 (2) - Member 관련 코드 개발이전 포스팅 내용에 이어서 하도록 하겠습니다.1. 도메인 계층(Domain Layer)BaseDomainModel 추상 클래스BaseDomainModel 추상 클래스를 별도로 만드는 것이 좋은 이유는, 재사용성을 높일 수 있고, 이를 통해 공통 필드(createdAt, updatedAt) 및 공통 메서드 같은 것들을 상속받아 중복 코드를 방지하며, 여러 도메인 클래스에서 동일한 엔티티 속성을 관리할 수 있습니다.Member 클래스Member 클래스에 Setter 메서드를 사용하지 않는 이유는 불변성을 통해 코드 안정성을 확보하기 위해서입니다. 데이터를 외부에서 수정하지 도메인변경 못하도록 방지하고, 생성 및 변경은 명시적 메서드를 통해서만 제어합니다. 이렇게 하면 엔티티의 상태 변경을 관리하는 데 혼란을 줄이고, 디버깅 및 유지보수가 쉬워집니다.불변 데이터와 상태 변경을 명시적으로 관리하는 것이 좋은 이유는 객체의 email을 변경할 때는 updateEmail 메서드로만 가능하게 하여 상태 변경을 명시적으로 관리할 수 있고, updateEmail 메서드에서 검증 로직을 호출해 잘못된 상태 변경을 방지할 수 있습니다. 이렇게 하면 상태 변경이 발생하는 모든 시점을 제어할 수 있어 디버깅과 데이터 무결성 유지에 유리합니다.create 도메인변경 팩토리 메서드를 통해 객체를 생성하는 것이 좋은 이유는 생성 로직과 검증 로직(validateUsername, validateEmail)을 한 곳에 집중시켜 객체의 생성 흐름을 명확하게 관리할 수 있고, 객체 생성 시 불완전하거나 잘못된 데이터를 차단할 수 있습니다. 이렇게 하면 생성 로직 변경 시 해당 부분만 수정하면 됩니다.유효성 검사 로직을 캡슐화하면 좋은 이유는 입력 데이터에 대한 유효성 검사를 별도 메서드로 분리(validateUsername, validateEmail)해 가독성을 높일 수 있고, 재사용이 가능하며, 검증 로직의 변경이 필요할 때도 쉽게 확장할 수 도메인변경 있습니다. 이렇게 하면 코드 중복을 방지하고, 검증 로직이 분산되지 않아 관리가 수월합니다.MemberCommandService 클래스Member 클래스는 도메인 로직을 자체적으로 처리(create, updateEmail)하고, MemberCommandService 클래스는 이를 호출하여 비즈니스 로직을 조율합니다. 도메인 모델에서 데이터 무결성을 보장하므로 서비스 레이어에서는 도메인 로직에 의존하기만 하면 됩니다. 서비스 레이어에서는 저장소의 세부 구현을 몰라도, 저장과 조회 기능만 호출할 수 있으면 됩니다.MemberMapper를 통해 Entity와 도메인 모델을 변환하여, 도메인 로직과 데이터베이스 의존성을 분리합니다. 도메인 모델은 비즈니스 로직에 집중하고, Entity는 DB 데이터 저장에만 도메인변경 집중합니다. 이렇게 하면 데이터 저장 방식(Entity)이 변경되더라도 도메인 모델은 그대로 유지 가능합니다.@Transactional을 클래스 레벨에서 선언하여 서비스 내 모든 메서드가 데이터 무결성을 유지합니다. 트랜잭션 설정 덕분에 데이터 저장과 조회가 하나의 논리적 작업으로 처리됩니다.MemberQueryService 클래스JPA는 읽기 전용 트랜잭션 @Transactional(readOnly =true) 에서 플러시를 생략하고 더티 체킹을 하지 않으므로 성능이 향상됩니다.MemberQueryService는 데이터를 조회하는 서비스이므로 불필요한 쓰기 작업을 방지하고 읽기 작업만 명확히 수행합니다.MemberCommandService는 쓰기 작업, MemberQueryService는 읽기 작업을 담당합니다. 이렇게 나누면 서비스의 책임이 분리되어 코드가 도메인변경 더 간단하고 이해하기 쉽습니다. 그리고 나중에 비즈니스 요구 사항이 변경되어 조회와 쓰기 작업이 각각 복잡해져도 별도로 관리하기 때문에 유지보수가 편합니다.Optional;를 반환하여 조회 결과가 없을 때 null 대신 명시적으로 처리 가능합니다. 해당 API를 호출하는 측에서 결과를 더 명확히 다룰 수 있습니다.(예: orElse, ifPresent, map 등을 활용)2. 인프라 계층(Infrastructure Layer)BaseEntity 추상 클래스MemberEntity 클래스MemberJpaRepository 인터페이스MemberMapper 클래스MemberMapper 클래스는 Entity ↔ Domain Model 간의 변환 책임을 담당합니다. 비즈니스 로직과 데이터 변환 로직을 분리하여 가독성과 유지보수성을 도메인변경 높일 수 있습니다. 그리고 변환 작업을 정적 메서드로 구현하여 객체 생성 없이 직접 호출이 가능합니다.3. 애플리케이션 계층(Application Layer)MemberFacade 클래스Facade 패턴을 적용한 MemberFacade 클래스는 서비스 레이어를 한 단계 더 추상화합니다. Presentation Layer에서 Command와 Query 서비스를 직접 호출하지 않도록 중개 역할을 수행합니다. Presentation Layer에서는 복잡한 내부 비즈니스 로직 대신 MemberFacade에서 제공되는 API만 사용하면됩니다.Command/Query 서비스의 역할을 이해하지 못해도, MemberFacade만 사용하면 쉽게 기능 호출 가능합니다. 내부 Command/Query 서비스의 구현이 변경되어도 Facade 인터페이스는 그대로 유지되므로 도메인변경 Presentation Layer 코드에 영향이 없습니다.도메인 모델 Member 대신 DTO(MemberDto)를 반환하면 Presentation Layer와의 데이터 교환에 적합한 데이터 구조를 제공합니다. 도메인 모델의 불필요한 내부 상태를 노출하지 않아 데이터 캡슐화 및 보안을 유지하며, API 응답 형식이 고정되어 있어 Presentation Layer에 제공하는 명세가 명확합니다.내부적으로 Command/Query 서비스에 작업을 위임하여 CQRS 패턴을 준수하며, 쓰기 작업은 Command 서비스에 위임하고, 읽기 작업은 Query 서비스에 위임합니다. 이렇게 하면 서비스의 책임이 명확히 분리되고, 확장성 및 유지보수성이 높아집니다.MemberDto 클래스다음 포스팅에서 계속 도메인변경 이어가겠습니다.