반응형
도메인과 도메인 모델
도메인(Domain)
- 우리가 소프트웨어로 해결하고자 하는 대상이다. 예를 들어 화상 채팅 서비스를 구현하고자 하는 경우에는 화상 채팅이 도메인이 된다
- 도메인은 하위 도메인으로 나뉠 수도 있다. 예를 들어, 화상 채팅 도메인은 화면 공유, 텍스트 채팅, 친구 목록 등의 하위 도메인으로 나뉜다.
도메인 모델(Domain Model)
- 여러 정의가 있지만 우선 기본적으로 도메인을 개념적으로 표현한 것을 도메인 모델이라고 한다.
- 도메인을 개념적으로 표현하는 방법은 다양하기 때문에, 예를 들어 클래스 다이어그램이나 상태 다이어그램, 또는 그래프나 수학 공식이 모두 도메인 모델이 될 수 있다
- 또한, 도메인 모델은 도메인 모델 패턴(Domain Model Pattern) 을 의미하기도 하는데, 도메인 모델 패턴은 아래와 같은 아키텍처 구조에서 도메인 계층을 객체 지향 기법으로 구현한 것을 의미한다.도메인(Domain)은 우리가 소프트웨어로 해결하고자 하는 문제나 주제를 의미합니다. 예를 들어, 화상 채팅 서비스를 만들고자 한다면, 화상 채팅이 바로 도메인이 됩니다.
- 도메인은 여러 부분으로 나뉠 수 있습니다. 예를 들어, 화상 채팅 도메인은 화면 공유, 텍스트 채팅, 친구 목록 등으로 세분화할 수 있습니다. 이 각각의 부분을 하위 도메인(Subdomain)이라고 부릅니다.
- 도메인 모델은 우리가 해결하고자 하는 문제를 이해하기 쉽게 구조화한 것이라 할 수 있습니다. 도메인 모델은 다양한 방식으로 표현될 수 있습니다. 예를 들어, 클래스 다이어그램, 상태 다이어그램, 그래프, 수학 공식 등이 모두 도메인 모델이 될 수 있습니다.
도메인(Domain) 소프트웨어로 해결하려는 문제나 주제.
하위 도메인(Subdomain) 도메인의 세분화된 부분들.
도메인 모델(Domain Model) 도메인을 개념적으로 표현한 것. 예를 들어, 다이어그램, 그래프, 수학 공식 등이 포함될 수 있음.
도메인 모델 패턴(Domain Model Pattern) 도메인 계층을 객체 지향 기법으로 구현한 것.
DDD란?
DDD(Domain Driven-Design : 도메인 주도 설계)란? 도메인 패턴을 중심에 놓고 설계하는 패턴, 즉 도메인간의 상호작용이 설계의 중심
특징
- 핵심 도메인과 도메인 로직에 초점을 맞춘다
- 엔티티와 도메인 개념의 일치를 지향
- 각 도메인은 서로 협력하지만 책임과 역할이 명확하여 높은응집도와 낮은 결합도를 갖는 상태를 지향
아키텍처
도메인 주도 설계(DDD, Domain-Driven Design)의 레이어드 아키텍처(Layered Architecture)는 소프트웨어 시스템을 여러 계층으로 나누어 각 계층이 특정한 역할과 책임을 가지도록 하는 구조입니다. 이러한 계층 구조는 시스템의 복잡성을 관리하고, 코드의 가독성과 유지 보수성을 높이는 데 도움을 줍니다. DDD에서 사용하는 전형적인 레이어드 아키텍처는 다음과 같은 네 가지 주요 계층으로 구성됩니다.
- 프레젠테이션 계층 (Presentation Layer)
- 역할: 사용자와 상호 작용하는 인터페이스를 담당합니다.
- 책임: 사용자의 입력을 받고, 사용자에게 출력을 제공합니다. 웹 애플리케이션의 경우, HTML 페이지나 API 엔드포인트가 이 계층에 속합니다.
- 예시: 웹 컨트롤러, 뷰 템플릿, REST API 엔드포인트 등.
@RestController
@RequestMapping("/api/v1/auth")
@RequiredArgsConstructor
public class AuthController {
private final AuthService authService;
@PostMapping("/register")
public ResponseEntity<RegisterUserResponse> register(@Valid @RequestBody RegisterUserRequest request) {
RegisterUserResponse response = authService.register(request);
return ResponseEntity.status(HttpStatus.OK).body(response);
}
- 응용 계층 (Application Layer)
- 역할: 애플리케이션의 주요 동작을 조정하고, 도메인 객체를 사용하여 비즈니스 로직을 실행합니다.
- 책임: 도메인 계층과 프레젠테이션 계층을 연결하며, 비즈니스 로직을 직접 구현하기보다는 도메인 객체에 위임합니다. 트랜잭션 관리도 이 계층에서 수행됩니다.
- 예시: 서비스 클래스, 유스케이스, 애플리케이션 서비스 등.
public LoginTokenResponse login(LoginTokenRequest request) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(request.getUserId(), request.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
String token = jwtTokenUtil.generateJwtToken(authentication);
CustomUserDetailsImpl userDetails = (CustomUserDetailsImpl) authentication.getPrincipal();
checkEncodePassword(request.getPassword(), userDetails.getPassword());
return LoginTokenResponse.fromEntity(userDetails, token);
}
- 도메인 계층 (Domain Layer)
- 역할: 핵심 비즈니스 로직과 규칙을 포함합니다.
- 책임: 비즈니스 규칙을 정의하고, 엔티티(Entity), 값 객체(Value Object), 도메인 서비스(Domain Service) 등을 포함합니다. 이 계층은 응용 계층이나 인프라스트럭처 계층에 의존하지 않도록 설계됩니다.
- 예시: 엔티티, 값 객체, 도메인 서비스, 애그리게이트(Aggregate), 리포지토리 인터페이스 등
@Getter
@Entity
@Table(name = "users")
public class User extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Comment("회원 인덱스")
private Long id;
@Comment("회원 코드")
private String code;
@Column(name = "user_id", nullable = false)
@Comment("아이디")
private String userId;
@Column(nullable = false)
@Comment("패스워드")
public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom {
User findByUserId(String userId);
}
- 인프라스트럭처 계층 (Infrastructure Layer)
- 역할: 도메인 계층과 응용 계층이 필요로 하는 기술적인 기능을 제공합니다.
- 책임: 데이터베이스 접근, 메시징, 파일 시스템 접근 등의 기술적 구현을 담당합니다. 리포지토리의 구체적인 구현이나 외부 API 호출 등이 이 계층에 포함됩니다.
- 예시: 데이터베이스 리포지토리 구현, 메시징 큐, 파일 시스템 접근, 외부 서비스 통합 등.
public interface UserRepositoryCustom {
List<User> findByBranchAndRole(Branch branch, UserRoleType role);
}
@Repository
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class UserRepositoryImpl implements UserRepositoryCustom {
private final JPAQueryFactory factory;
@Override
public List<User> findByBranchAndRole(Branch branch, UserRoleType role) {
return factory.selectFrom(QUser.user)
.join(QUser.user.roles, QRole.role)
.where(QUser.user.branch.eq(branch)
.and(QRole.role.name.eq(role)))
.orderBy(QUser.user.createdDate.desc())
.fetch();
}
}
장점
- 비즈니스 로직의 명확성 및 일관성
- DDD는 비즈니스의 핵심 개념과 용어를 정확하게 정의하고 모델링하여 개발자와 비즈니스 전문가 간의 의사소통을 개선합니다. 이를 통해 비즈니스 로직을 보다 명확하고 일관성 있게 구현할 수 있습니다.
- 유연성 및 확장성
- 도메인 모델을 중심으로 설계되므로, 시스템의 특정 부분을 변경하더라도 다른 부분에 미치는 영향을 최소화하면서 유연하게 확장하거나 수정할 수 있습니다.
- 재사용성
- DDD에서 개발된 모듈은 비즈니스 로직과 밀접하게 연결되어 있어, 비슷한 비즈니스 요구 사항을 가진 다른 프로젝트나 모듈에서 재사용하기 쉽습니다.
- 테스트와 리팩토링 용이성
- 잘 정의된 도메인 모델은 단위 테스트 및 리팩토링을 용이하게 만듭니다. 모듈성이 높아지며, 각 구성 요소의 독립성이 강화되어 테스트하기 쉬워집니다.
단점
- 복잡성 증가
- 도메인의 복잡성이 애플리케이션에 그대로 반영되어 전체적인 복잡성이 증가합니다. 개발자는 도메인을 가능한 한 간결하게 모델링해야 합니다.
- 과도한 도메인 주도
- 모든 것을 도메인 모델로 표현하려는 시도는 설계의 유연성을 저하시킬 수 있습니다. 때로는 외부 요구사항을 수용하기 위해 모델을 유연하게 조정할 필요가 있습니다.
- 높은 학습 곡선
- 도메인 주도 설계의 개념과 기법을 이해하고 적용하기까지 많은 학습이 필요하며, 도메인에 대한 깊은 이해와 경험이 요구됩니다.
- 추상화의 어려움
- 복잡한 도메인을 추상화하여 모델링하는 것은 어렵습니다. 적절한 수준의 추상화를 찾는 것이 중요하며, 너무 많거나 적은 추상화는 모델의 이해도와 복잡성 관리에 영향을 미칩니다.
- 의존성 주입의 어려움
- 도메인 객체 간의 의존성을 적절히 주입하는 것은 어려운 문제이며, 계층 간 의존성 주입은 특히 더 복잡할 수 있습니다. 의존성 주입 방식은 도메인 모델의 의미를 훼손하지 않도록 신중하게
반응형