[Index]
▷ 내가 아는 방식
▷ Entity
▷ Domain
▷ DTO
▷ VO
▷ DAO
▷ Repository
▷ Entity, Domain, DTO, VO, Repository 비교
내가 아는 방식
RequestDto, ResponseDto, ModelDto 이렇게 설정해놓고
아주 간단한 crud api만 만드는 작업을 해봤다.
RequestDto는 클라이언트로부터 받은 json형태의 값을 자바로 저장해 놓는 공간.
ResponseDto는 DB 조회로 부터 받은 값을 저장해 놓는 공간.
ModelDto는 Mapper 인터페이스와 sql.xml과의 소통을 위해 쓰는 공간. RequestDto 즉, 클라이언트로부터 받은 값을 놓는 공간에 잇는 값들을 ModelDto로 넣어주고, 그 ModelDto에 있는 값을 Mapper인터페이스에서 매개변수 값으로 넣어주었다.
그리고 sql.xml에서 insert나 update 구문에서 ModelDto의 데이터들을 매개변수타입으로 활용하여 DB에 넣어주도록 하였다.
이렇게 해서 기본 중의 기본인 CRUD를 이용해 생성, 전체조회, 단건조회, 수정, 삭제 api만을 구현해보았던 것이다.
이번에 DDD를 공부하며
Entity, Domain, DTO, VO, DAO, Repository의 단어가 주는 정확한 의미와 차이를 알고 싶었다.
실제 프로젝트 진행 시, 정확한 의미를 알고 써야지 프로젝트에 대한 이해도가 향상될 수 있다고 본다.
그리고 SpringBoot 기반에 JPA를 사용한다면
Entity, Domain, DTO, VO, Repository를 사용하게 된다고 하는데... 알아봐야지~
Entity
데이터베이스의 테이블을 Java 객체로 표현하는 클래스이다.
데이터베이스의 한 행을 나타내는 객체.
JPA와 같은 ORM 프레임워크에서 사용된다.
주로 데이터베이스와의 상호작용을 나타낸다.
고유 식별자를 가지며, 식별자가 동일한 경우 같은 객체로 취급된다.
//"사용자"를 나타내는 테이블이 있다고 가정 //이 테이블에는 "아이디", "이름", "이메일" 등의 컬럼이 있을 것이다. //Entity클래스를 작성하면 이러한 테이블 구조를 자바 객체로 나타낼 수 있다. //이 "User" Entity는 데이터베이스의 "사용자"테이블과 매핑이 된다. //이러한 Entity를 사용하면 데이터베이스의 행과 자바 객체를 쉽게 매핑할 수 있으며, //CRUD 작업을 훨씬 간결하게 수행할 수 있다. import javax.persistence.*; @Entity //해당클래스가 Entity임을 나타낸다. public class User { @Id //Primary Key 임을 나타낸다. @GeneratedValue(strategy = GenerationType.IDENTITY) //Primary Key 생성 전략을 지정한다. private Long id; private String name; private String email; //생성자, getter, setter }
Spring Boot 프로젝트에서 JPA를 사용하여 데이터베이스와 매핑하려는 클래스에는 @Entity 어노테이션을 사용해야 한다.
이 어노테이션은 해당 클래스가 데이터베이스의 테이블과 직접 연결될 수 있는 엔터티임을 JPA에게 알려준다.
@Entity 어노테이션을 사용하면, 해당 클래스의 인스턴스는 데이터베이스 행과 일대일로 매핑된다.
따라서 해당 클래스와 연결된 테이블에 대한 CRUD(Create, Read, Update, Delete) 작업을 수행하려면
@Entity 어노테이션을 반드시 포함해야 하며, 이를 통해 데이터베이스 작업을 매우 간결하고 객체지향적인 방식으로 수행할 수 있다.
*** 참고사항
Entity라는 용어는 주로 2가지 다른 컨텍스트에서 사용되며,
그 의미는 해당 컨텍스트에 따라 다르다.
1. 데이터베이스/ORM 컨텍스트에서의 Entity
데이터베이스의 테이블을 나타내는 클래스를 의미한다.
ORM 프레임워크(ex:Hibernate, JPA, Entity Framework)에서, 테이블과 클래스 간의 매핑을 돕는다.
각 인스턴스는 테이블의 행을 나타내며,
클래스의 속성은 열과 매핑된다.
2. 도메인 주도 설계 (DDD) 컨텍스트에서의 Entity
도메인 모델의 핵심 객체를 의미한다.
고유한 ID를 가진 지속적인 생명주기를 갖는다.
도메인 로직을 포함하며,
도메인의 핵심 비즈니스 규칙을 나타내는 데 중요한 역할을 한다.
도메인의 실세계 엔터티(ex:사용자, 주문, 제품등)를 모델링하며,
시간이 지나도 지속적으로 변할 수 있는 상태를 가진다.
두 컨텍스트에서의 Entity는 목적과 사용 방법이 다르지만,
공통점도 있다.
둘 다 어떤 실체를 나타내는 객체이며,
그 객체는 고유한 식별자를 가진다.
그러나 주요한 차이는
데이터베이스/ORM 컨텍스트에서의 Entity는 주로 데이터 저장과 관련된 작업을 위한 것이며,
DDD에서의 Entity는 도메인 로직과 비즈니스 규칙을 나타내기 위한 것이다.
"Entity"라는 용어를 사용할 때는 해당하는 컨텍스트와 의도를 명확히 하는 것이 중요하다.
Domain
비즈니스 문제를 표현하는 영역을 의미한다.
Entity, Value Object, Aggregate Root 등 도메인 주도 설계에서 사용되는 모든 객체와 규칙을 포함할 수 있다.
비즈니스 로직과 규칙이 구현되는 곳이다.
예제
온라인 서점
온라인 서점의 주요 도메인 개념
책 (Book)
- 제목 (Title) 저자 (Author) 가격 (Price)
주문 (Order)
- 주문자 (Customer) 주문 책목록 (Ordered Books) 주문 상태 (Order Status)
회원 (Customer)
- 이름 (Name) 이메일 (Email) 주소 (Address)
// 각 클래스는 서점 도메인의 중요한 개념을 나타내며, // 각 클래스의 속성과 메서드는 해당 도메인의 특성과 로직을 표현합니다. public class Book { private String title; private String author; private double price; // 기타 메서드... } public class Order { private Customer customer; private List<Book> books; private OrderStatus status; // 기타 메서드... } public class Customer { private String name; private String email; private String address; // 기타 메서드... }
DTO
Data Transfer Object. 계층 간의 데이터 전달을 담당하는 객체이다.
주로 사용자 입력을 받아 처리하거나, 서비스 계층에서 표현 계층으로 데이터를 전달할 때 사용된다.
로직을 가지지 않으며, 단순히 데이터를 담는 역할을 한다.
일반적으로 계층간에 데이터를 안전하게 전달하기 위해 사용되며, 필요한 정보만 담고 있어서 가볍고 효율적으로 사용할 수 있습니다.
중복을 줄이고 유지 보수를 용이하게 하며, 데이터의 안정성을 높이는 데 유용합니다.
예제
사용자 정보를 클라이언트에게 전달 할 때 사용하는 DTO.
public class UserDTO { private String username; private String email; // 생성자 public UserDTO(String username, String email) { this.username = username; this.email = email; } // 게터와 세터 public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
UserDTO 클래스는 데이터베이스로부터 가져온 사용자 정보를 클라이언트에게 전달하기 위해 사용된다.
이 예시에서 비밀번호와 같은 민감한 정보를 제외하고
클라이언트에게 필요한 정보만 포함되어 있다.
서비스 레이어에서는 이 DTO를 사용해
데이터를 컨트롤러로 전달하고
컨트롤러는 이 DTO를 사용해
데이터를 클라이언트로 응답할 수 있다.
@RestController public class UserController { @GetMapping("/user/{id}") public UserDTO getUser(@PathVariable Long id) { User user = userService.findById(id); // DB로부터 사용자 정보를 가져옴 return new UserDTO(user.getUsername(), user.getEmail()); // 사용자 정보를 DTO로 변환하여 반환 } }
VO
A Value Object. 고정된 데이터 집합을 나타낸다. Java에서 Enum과 유사하다.
값 자체가 동일성을 나타내는 객체이다.
예를들어, 주소나 날짜와 같이 값이 동일하면 같은 객체로 취급된다.
변경 불가능해야하며, 로직을 가질 수 있다.
예제
주소정보를 표현하는 클래스를 만든다.
public class Address { private final String street; private final String city; private final String state; private final String zipCode; public Address(String street, String city, String state, String zipCode) { this.street = street; this.city = city; this.state = state; this.zipCode = zipCode; } // getters @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Address address = (Address) o; return street.equals(address.street) && city.equals(address.city) && state.equals(address.state) && zipCode.equals(address.zipCode); } @Override public int hashCode() { return Objects.hash(street, city, state, zipCode); } }
위의 Address 클래스는 각각의 속성을 final로 선언하여 불변성을 보장하고 있으며,
주소를 표현하는 데 필요한 모든 데이터를 포함하고 있다.
또한 equals, hashCode 메서드를 오버라이드하여 객체의 모든 속성을 비교하게 되어 있다.
이렇게 객체의 모든 값을 비교하는 특징은 VO의 중요한 특성 중 하나이다.
DAO
Data Access Object. 실제로 DB와 접근하여 Data를 CRUD하는 객체. Service와 DB를 연결해주는 역할을 한다.
데이터 액세스 로직을 캡슐화하여 CRUD 연산을 수행.
데이터베이스와 상호작용하는 코드를 캡슐화하는 역할을 한다.
이 친구가 내가 하던 방식에서 ModelDto에 해당하는 것 같다.
도메인 로직과 데이터 액세스 로직의 분리는 DAO를 사용하면 가능하며,
이를 통해 코드의 유지보수와 테스트가 용이해진다.
그러나 DDD에서는 Repository 패턴이 더 선호된다. (하단 Reposoitory 카테고리에서 이유 확인)
예제
public interface BookDAO { //내가 썼던 Mappepr인터페이스랑 동일한듯. //Mapper는 Mybatis에서 쓰는 DAO이다. //DAO는 기술과 상관없는 개념이다. Book findById(int id); List<Book> findAll(); void save(Book book); void delete(int id); } public class BookDAOImpl implements BookDAO { // 데이터베이스 연결 및 쿼리 실행 로직 // 실제로는 구현하는 클래스를 만들지 않고 // 데이터베이스 연결은 yml에서 설정한 값 토대로 스프링부트가 열일한다. // 인터페이스에 연결된 쿼리 실행로직인 sql.xml을 탄다. 그리고 sql에 의해서 일이 일어난다. } public class BookService { private BookDAO bookDAO; public Book getBookById(int id) { return bookDAO.findById(id); } }
Repository
Repository는 도메인 객체를 저장하고 검색하는 방법을 추상화하고,
도메인 로직과 데이터 액세스 로직 사이의 결합도를 더욱 낮춘다.
도메인 객체의 컬렉션처럼 작동하여 도메인 로직에 더 초점을 맞춘다.
Repository 패턴은 도메인 중심 설계에서 DAO보다 더 적합하며,
도메인 객체와 데이터베이스 간의 상호 작용을 관리하는 데 더 직관적이다.
DAO도 도메인 로직과 데이터 액세스 로직을 분리할 수 있지만
DDD에서는 Repository 패턴이 더 자연스럽고 일관된 방식으로 분리를 지원하게 된다.
예제
public interface BookRepository { Book getById(int id); List<Book> getAllBooks(); void addBook(Book book); void removeBook(int id); } public class BookRepositoryImpl implements BookRepository { // 도메인 중심의 쿼리 실행 로직 } public class BookService { private BookRepository bookRepository; public Book findBook(int id) { return bookRepository.getById(id); } }
Entity, VO, DTO, Domain, Repository 를 비교하며 이해해보자면,
Entitiy 와 VO
Entity는 고유 식별자를 통한 동일성을
VO는 값의 동일성을 나타낸다.
DTO와 VO
DTO는 데이터 전송을 위한 객체
VO는 값의 동일성을 나타내는 객체
Domain과 Entity
Domain은 비즈니스 문제 영역을 나타내고
Entity는 그 중 데이터베이스 테이블과 매핑되는 부분이다.
Entity와 Repository
Entity는 데이터 구조를 표현하고
Repository는 데이터 액세스 로직을 추상화한다.
나는 마치 교수님이 될 것 마냥 이론을 파는 습관이 있다...
근데 정확히 모르니 이렇게 알아가야지...
개발 공부에서는 메타인지가 없는 나... 내가 뭘 모르고 뭘 아는지 나도 알고싶다~
* 언그래머와 함께 공부하고 있습니다~
'개인 공부 (23.07~' 카테고리의 다른 글
MVC 패턴 쉽게 이해하기 (0) | 2023.08.15 |
---|---|
Controller ▷ Service ▷ Repository 단계 이해하기 (0) | 2023.08.11 |
[ DDD & SQL 중심 설계(SQL-DD) ] 정의와 비교 (1) | 2023.08.09 |
@RequestParam @PathVariable @RequestBody @ModelAttribute 간단 정리 (0) | 2023.08.06 |
Spring Bean 등록 방법 (@Component, @Configuration + @Bean) (2) | 2023.08.05 |