기존의 legacy는 h2Database로 되어있었다. 따라서 DB적용은 반드시 필요했다. 처음에 ottsharing의 구현 계획 당시 MariaDB를 적용하기로 결정했기 때문에 그대로 MariaDB를 적용하기로 결정했다. 또한, 쿼리를 직접 작성하고 객체와 쿼리문 모두 관리하고 CRUD 메소드를 직접 다 구현하고 싶지 않았기 때문에 Mybatis를 적용하기 보다는 JPA를 이용하기로 했다. 기존 legacy에서 JPA을 적용했다는 이유도 있다.
🔔 MariaDB vs MySQL
둘 중 무엇을 쓸까?에 고민이 사실 약간 있었다. 가장 대중적인 것이 MySQL인데 MariaDB를 선택한 이유들 중 가장 큰 이유는 " MariaDB는 MySQL을 포크한 서비스 "라는 점이었다. 이 말은 즉, MySQL에서 좀 더 개선된 버전이 MariaDB라는 점과 동일해 보였다.이 외에도 MariaDB의 장점은 5가지나 더 있다.
- 동일 하드웨어 사양으로 MySQL보다 향상된 성능
- 좀 더 활성화된 커뮤니티
- 다양한 기능
- 다양한 스토리지 엔진
- 빠르고 투명한 보안패치 릴리즈
참고)
https://loosie.tistory.com/366
https://mariadb.com/kb/ko/mariadb-vs-mysql-features/ (MariaDB 공식문서)
https://linuxnatives.net/2015/10-reasons-to-migrate-to-mariadb-if-still-using-mysql
프로젝트 구성 및 시작이라는 이전 게시물에서 말했던 것과 같이, 도메인층에 객체를 저장하는 Repository의 인터페이스가 존재하고 인프라층에는 객체를 구체화하는 UserV1RepositoryAdaptor 클래스가 존재한다. 처음에 ddd프로젝트 구성 당시 난 InMemory를 이용해 구성하였다. 하지만 원래의 계획은 MariaDB를 적용하는 것이기 때문에 MariaDB로 전환을 다시 했고 매핑을 위해서는 JPA도 꼭 필요했다.
DDD 구조의 장점은 의존성이 적다는 점이다. InMemory에서 JPA, MariaDB를 적용하는 과정이 DDD 구조의 장점이 가장 와닿는 과정이었다. InMemory가 적용되었던 UserV1RepositoryAdaptor에서 단순히 호출하는 메모리만 JPA로 바꿔주기만 하면 되었기 때문이다. 즉, 한 클래스에서 몇줄만 바꾸면 된다!
위의 그림은 본래 InMemory에서 JPA로 바꾸었을때 바뀐 코드를 나타낸 그림이다. 정말 간단하지 않은가? 만약 MySQL를 적용하고 싶으면 또 해당 코드를 단순히 바꾸기만 하면 된다.
이렇게 infrastructure에 JPA도, InMemory도 존재한다. 그래서 상황에 따라 원하는 메모리를 넣고 적용하기만 하면 된다.
JPA를 적용하고 DB 연결을 위해 application.yml을 적용하는 것은 당연한 절차이기 때문에 이에 대한 내용은 생략하겠다. 다만, 내가 여기서 잠시 고민했던 점은 application.yml은 어디에 위치해야 하는 가였다. 결론은 server모듈이었다. 모든 api를 조율해야하는 것이기 때문에 server모듈에 두는 것이 옳다고 생각했다.
legacy버전에서 회원가입 로직은 UserService에 모두 몰아져있다.
회원가입로직에는
userId의 쓰임새가 email가 동일하지 않나? 라는 생각이 로직을 살펴보면서 든 처음 생각이다. 습관적으로 어떤 사이트에 회원 가입을 할 때, 아이디와 비밀번호를 만드는 것이 통상이니 당연히 아이디는 필요하다는 생각으로 이전에 ottsharing을 만들었었다. 그런데 지금 생각해보니 '구글도 처음에 회원가입하거나 로그인할때 아이디가 아닌 이메일을 이용하지 않나?' 'userId는 사용자를 식별하기 위한 정보 중 하나인데 이메일로도 확인할 수 있지 않나?'라는 생각이 났고 이러한 생각으로 인해 userId를 과감히 빼기로 결정하였다. 만약 이렇게 된다면, 만약 사용자가 유효하지 않는 이메일을 이용해 회원가입을 하면 안되니, 이메일의 유효성이 굉장히 중요해져서 이메일 검증 절차가 도입되어야할 것으로 보인다. 후에 도입될 과제가 추가되긴 하겠지만 이메일 인증 절차를 적용한다면, 이 또한 공부가 되리라 생각된다.
처음에 언급했듯이 legacy버전의 UserService에는 4가지의 흐름의 회원 가입 로직이 한데 모여있다. 나는 이것을 개선을 하여 분리하고 싶었다. 이때 ddd가 도움이 되었다.
ddd설계를 할때 중요한 것 중 하나가 어떤 것이 어떤 책임을 갖고 있는지 정확히 파악하는 것이다.
즉, user 엔티티에 있는 email과 password를 각각 객체화 함으로써 책임분리를 하는 것이다.
회원가입로직에 있는 로직이
이렇게 있는데
2번째인 'email의 패턴이 제대로 된 패턴인지 검사하는 패턴 검사'는 email객체에 책임을 부여하고, 3번째인 'password가 숫자와 알파벳을 섞어서 쓰였고, 길이가 8이상인지의 유효성검사'는 password 객체에게 책임을 부여하여 email 또는 password를 생성할 때 이 책임을 다하면 된다.
이렇게 책임을 분리하면 processor단계에서는 단순히 email의 중복 확인과 새로운 회원의 정보를 DB에 저장하는 일만 해주면 된다. 코드가 엄청 정리되는 느낌이었다.
왼쪽은 legacy버전의 userService에 해당하고, 오른쪽은 현재 버전의 user-application에 해당한다. 코드가 확연히 짧아진 것을 볼 수 있다. 이렇게 적용하면서 내가 배운것은 @Embedded, @Embeddable이다.
"자바 ORM 표준 JPA 프로그래밍"에서는 '잘 설계된 ORM 애플리케이션은 매핑한 테이블의 수보다 클래스의 수가 더 많다'라고 적어져있다. 난 @Embedded, @Embeddable가 있음으로써 해당 말이 실현된다고 생각한다. 현재 프로젝트는 User엔티티에서 Email과 Password를 임베디드 타입으로 구현하였다.
본래는 한 클래스로만 이루어졌던것이 임베디드타입을 적용함으로써 클래스가 2개 더 늘게 되었다. 하지만 테이블은 분리되지 않고 오직 한개만 존재하는 것을 볼 수 있었다.
임베디드 타입은 엔티티의 값일 뿐이라서 값이 속한 엔티티의 테이블에 매핑하기 때문에 테이블이 하나뿐인 것이다. 이로써 객체지향적임을 추구하면서 책임은 분리되고 원래의 테이블은 유지할 수 있는 형태가 구현됨을 확인할 수 있었다.
에그리게잇에 대해 이론적인 관점에서 먼저 말하자면, 에그리게잇은 엔터티의 트랜잭션 단위, 연관된 객체의 묶음을 의미한다. 따라서, 에그리게잇은 1개 이상의 Entity로 구성된다. 그 중 한 Entity는 에그리게잇 루트로 정의하며 구성에는 값 객체를 포함할 수 있다.
'도메인 주도 설계 구현(Implementing Domain-Driven Design)'라는 책에서는 위의 그림을 통해 에그리게잇루트를 표현하고 있다. 말그대로 에그리게잇은 엔티티와 값객체들의 모임이고 에그리게잇루트를 통해 뻗어나가고 있음을 확인할 수 있다.
이를 통해 user, email, password를 바라보면,
user는 에그리게잇 루트에 해당한다. 즉, user를 통해서만 email과 password가 변경이 가능하고 user 내에서만 트랜잭션이 보장됨을 의미한다.