- docker run
docker run --name mysql5.7.38 -e MYSQL_ROOT_PASSWORD=root -p 3308:3306 -d mysql:5.7.38 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
- 현재
3306
포트 점유 중이므로, 3307
로 변경
- ddl.sql 실행
- init.sql 실행
- application 실행
- server.port : 8081 변경
- datasource의 url 변경
@EmbeddedId
@Embedded
- 참고1
- 참고2
- 클래스로 그 내용을 entity에 끼어넣기 위함.
- Embed...들은 Entity의 가독성을 높히기 위함인듯 (복잡도를 낮추기 위함)
@Access
- JPA가 엔티티 데이터에 접근하는 방식을 정의함.
- 참고1
- 참고2
- AttributeConverter
@SecondaryTable
- 일반적인 애플리케이션의 구조는 [표현 - 응용 - 도메인 - 인프라스트럭처]
- 표현은 UI
- 응용은 서비스, 뭔가 조합해서 처리하는 거
- 도메인은 말그대로 도메인. 도메인에는 규칙이 있음.
- 인프라스트럭처는 데이터베이스 혹은 외부시스템과의 연계
- 비즈니스 구현에 있어서, 중요한 규칙은 도메인에 구현되어야함.
- 예를 들면 '배송 전에만 배송지를 변경할 수 있다' 라는 검증 로직은 도메인이 담당해야한다.
- 왜?
- 그게 도메인 주도..?
- 생성자에는 반드시 필요한 것을 넘긴다.
- 엔티티 식별자(id)를 가짐, 식별자로 구별함.
- 밸류는 식별자 대신 다른 걸 통해, 동일성을 판단.
- 유비쿼터스 언어. 전문가, 관계자, 개발자가 공통된 언어를 사용해서 소통과정에서의 모호함을 줄일 수 있도록..
- 표현 계층은 응용 계층에 의존하고 응용 계층이 도메인 계층에 의존하지만, 반대로 인프라스트럭처 계층이 도메인에 의존하거나, 도메인이 응용 계층에 의존하지는 않는다.
- 표현 -> 인프라스트럭처 이런식으로 의존하게 만들면 ,테스트 작성하거나 기능확장하기가 어렵다.
- DIP 의존 역전 원치
- 저순 모듈이, 고수준 모듈에 의존한다.
- 고수준, 저수준은 상대적인 것. 만약에 도메인 - 인프라가 있으면 도메인은 고수준, 인프라는 저수준이다.
- 구현 객체가 (JPA로 데이터를 가져올지 혹은 Drool로 가져올 지, 구체적으로 코드로 구현된 저수준 객체) 가 인터페이스에 의존하고 있는 현상 (고수준, 추상화 되어있음.) 이 DIP
- 인터페이스를 통해 기능이 구현된 응용 계층에서는, 어떤 저수준 모듈을 사용하는 지 알 필요 없게됨.
- 생성자로 인터페이스를 받아라 하는게 DIP를 지키고 나아가 테스트나 기능 확장을 쉽게하려는 목적
- 엔티티
- 고유 식별자를 갖는 객체
- e.g 주문, 회원, 상품..
- 단순히 데이터를 담고 있기 보다는, 데이터와 함께 기능을 제공함.
- 밸류의 집합으로 구성될 수도 있음.
- 밸류
- 고육 식별자를 갖지 않는 객체
- e.g Address, Money...
- 불변으로 구현할 것을 권장
- 애그리거트 aggregate
- 엔티티와 밸류를 개념적으로 하나로 묶은 것.
- 주문 애그리거트 = Order(도메인), OrderLine(밸류), Orderer(밸류)
- 리포지터리 repository
- 도메인 모델의 영속성을 처리하는 객체
- 즉 물리적인 저장소에 도메인을 저장하기 위한 계층, 저수준 모듈로 인프라스트럭처 영역에 해당함.
- DBMS에 저장 혹은 로딩...
- 도메인 서비스 domain service
- 특정 엔티티에 속하지 않은 도메인 로직을 제공.
- 할인 금액 계산을 하기 위해선 도메인 서비스가 필요함.
- 응용 서비스는 의존 주입등을 통해, 실제 리포지터리 구현 객체에 접근함.
- 도메인, 응용영역에서 인프라스트럭처의 기능을 직접 사용하는 것보다, 이 두 영역에 정의한 인터페이스를 인프라스트럭처 영역에서 구현하는 것이 시스템을 더 유연하고 테스트하기 쉽게 만든다.
- 무조건 그런것은 아님.
- 예를 들면
@Transactional
이나 영속성을 위한 @Entity
, @Table
같은 어노테이션은 응용, 도메인 객체에서 사용하는 것이 낫다.
- ui -> application -> domain <- infrastructure
- 패키지를 나누는 것도, 정해진 건 없다. 다만 합의가 필요한 부분.
- 그리고 한 패키지에 너무 많은 정보를 담고 있지 않게 하는게 좋을듯. 많아지면 나누는 걸 시도.
- 애그리거트는 복잡한 모델을 관리하는 기준을 제공.
- 주문 애그리거트는 배송지르르 변경하거나 주문 상품 개수를 변경하는 등 자기 자신을 관리하지만, 주문 애그리거트에서 회원의 비밀번호를 변경하거나 상품의 가격을 변경하지는 않는다.
- 'A가 B를 갖는다' 는 요구사항은 반드시 A와 B가 한 애그리거트에 속하는 것을 으미하는 건 아니다.
- 다수의 애그리거트가 한 개의 엔티티 객체만 갖는 경우가 많았으며, 두 개 이상의 엔티티로 구성되는 애그리거트는 드물었다.
- 주문 애그리거트는 Order 엔티티와, OrderLine 밸류로 구성된다.
- 애그리거트 루트.
- 애그리거트에 속한 모든 객체의 일관된 상태를 유지하려면, 관리할 주체가 필요한데 이러한 책임을 지는 것이 루트 엔티티
- 주문 애그리거트 에서 루트는 Order 엔티티.
- 애그리거트 외부에서, 속한 객체를 직접 변경하면 안됨.
- 그러면 애그리거트에 속한 객체는 루트만이 관리한다는 규칙을 어기게 되는 셈.
- 트랜잭션의 범위는 작을수록 좋다.
- 한 트랜잭션에서는 한 개의 애그리거트만 수정해야한다.
- 왜? 그렇게 되면, 애그리거트간의 결합도가 높아진다.
- 결합도가 놓아지면, 향후의 수정 비용이 증가한다.
- 필드(
@ManyToOne
, @OneToOne
..)를 이용한 애그리거트 참조는 아래 문제를 유발할 수 있다.
- 편한 탐색 오용, 즉 다른 애그리거트의 상태를 쉽게 변경할 수 있게 됨.
- 성능에 대한 고민, Lazy, eager loading
- 확장 어려움
- 그래서 Id 참조를 고려하자
- ID를 이용한 애그리거트 참조는 지연로딩과 같은 효과를 만든다. (N+1 문제)
- 목록이나, 조회 기능은 조회 전용 모델을 이용해서 구현하는 것이 좋다.
- 애그리거트가 갖고 있는 데이터를 이용해서, 다른 애그리거트를 생성해야한다면 애그리거트에 팩토리 메서드를 구현하는 것을 고려해보자
- store의 데이터를 이용해서 product을 생성한다면,
- store에서 product을 생성하는 팩토리 메서드를 추가하면... 좋음.
- 리포지터리 구현 클래스는 인프라스트럭쳐 영역
- Spring Data JPA를 사용하면 인터페이스만 구현하면 구현 객체는 스프링에서 알아서 만들어줌.
- JPA에서 @Entity 나 @Embeddable 로 클래스를 매칭하려면 기본 생성자를 제공해야함 (public or protected..)
- protected로 선언하면 다른 코드에서 기본 생성자를 이용하지 못하므로 권장되는 설정
- 엔티티에 프로퍼티를 위한 공개 get/set 메서드를 추가하면, 도메인의 의도가 사라지고 객체가 아닌 데이터 기반으로 엔티티를 구현할 가능성이 높아짐.
- setter 대신, changeStatus.. 와 같이 의도가 잘 나타나는 이름명을 사용할 수 있도록 해야함.
- 두 개 이상의 프로퍼티를 가진 밸류 타입을 한 개 커럽에 매핑하려면..
AttributeConverter
를 이용
- 밸류 컬렉션을 별도 테이블에 매핑할 떄는
@ElementCollection
, @CollectionTable
을 같이 이용함.
- 밸류 컬렉션을 한 개 컬럼에 매핑할 때는?
- 식별자라는 의미를 부각시키기 위해서, 식별자 자체를 밸류 타입으로 만들 수도 있음.
- JPA에서 식별자 타입은 Serializable 이어야 하므로, 밸류 타입으로 만들 때 해당 인터페이스를 상속 받아야함.
- 애그리거트에서 루트 엔티티를 뺀 나머지 구성 요소는 대부분 밸류.
- 루트 엔티티 이외에 또 엔티티가 존재하게 되는 경우, 진짜 엔티티로 필요한지 확인이 필요함.
- 밸류를 매칭한 테이블을 지정하기 위해
@SecondaryTable
, @AttributeOverride
를 이용
- 계층 구조를 가진 밸류 타입을 구현하기 위해?
- abstract 클래스
@Inheritance
, @DiscriminatorColumn
이용
- 루트 엔티티를 로딩하는 시점에 애그리거트에 속한 객체를 모두 로딩해야 하는 것은 아니다.
- 저장, 삭제 메서드는 애그리거트 루트 뿐만 아니라, 속한 모든 객체에게 전파되어야함
- 리포지터리와, 도메인 모델의 구현 기술은 거의 바뀌지 않는다.
- 그러므로 도메인 모델이 구현 기술에 의존해도 괜찮음