ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • MADII의 도메인을 모델링해보자! (1)
    Lesson-Learned/tech 2024. 1. 4. 14:56

    안녕하세요! MADII의 서버 리드 민슨입니다.

     

    오늘은 도메인 주도 개발을 위해 도메인 모델링을 진행한 과정에 대해 소개드리려고 합니다!

    기본적으로 최범균 작가님의 도메인 주도 개발 시작하기를 참고하여 진행하였습니다.

    사실 이렇게 DDD라는 새로운 아키텍처를 도입하게 된 건 최근에 클린코드, 객체지향 관련 공부를 깊게 했던 것이 계기였는데요,

    '디미터의 법칙'을 지키지 않고 지금까지 코딩을 해왔다는 생각이 들었습니다.

     

    디미터의 법칙

    Don't Talk to Strangers.

     

    디미터의 법칙은 위와 같이 '낯선 이와 대화하지 마라', 즉 친구와 대화하라는 뜻을 가지고 있는데, 사실 코딩의 세계에서 이런 말은 확 와닿지 않죠? Java 코드로 예시를 보여드리겠습니다.

     

    @Getter
    public class MadiiUser {
        private String loginId;
        private String password;
        private OnboardingInfo onboardingInfo;
        
        @Getter
    	public static class OnboardingInfo {
            private String nickname;
            private String image;
        }
    }

     

    이렇게 간단하게 마디 사용자를 코드로 구현했습니다.

    마디 사용자는 로그인을 위해 아이디와 비밀번호를 가지고 있고, 온보딩할 때 사용하는 정보를 OnboardingInfo 클래스를 통해 캡슐화하여 저장한게 보이네요! 그리고 Lombok을 통해 @Getter 어노테이션을 선언하였는데, Getter를 통해 정보를 가져와서 비교할 수 있겠죠?

     

    이제 Service에서 이 MadiiUser를 사용하는 예시를 보여드릴게요!

    public class NicknameValidateService {
    	public void validateNicknameLength(final MadiiUser madiiUser) {
        		if (madiiUser.getOnboardingInfo.getNickname().length() > 10) {
            		throw new IllegalArgumentException("닉네임은 10자 이하여야 합니다.");
            	}
        }
    }

     

    NicknameValidateService에서 닉네임을 검증하고 있는 . 것가죠? madiiUser 객체를 받아 내부의 OnboardingInfo 클래스에 접근하고, 그 클래스 내부의 nickname에 접근해 직접 길이를 비교한 모습이 보여요. 

    저는 지금까지 이런 식으로 코드를 작성해왔는데, 전혀 문제점을 못 느꼈습니다. 실제로 작동도 너무 잘 되는 코드고, 짜기도 편하니까요. 여기서 디미터의 법칙을 다시 떠올려 보면, 낯선 이와 대화하지 말라고 했었죠?

    여기서 낯선 이는 친구의 친구, 즉 나(NicknameValidateService)는 모르지만 친구(MadiiUser)는 알고 있는 OnboardingInfo 클래스입니다. 

     

    객체지향의 가장 중요한 것 중 하나는, 객체의 데이터를 직접 조회하지 말고 객체에 메시지를 보내서 응답을 받는 것입니다. 이렇게 Getter를 중첩해서 사용해서 정보를 가져오는 것은, private으로 선언했다 뿐이지 사실은 멤버 변수가 public으로 그대로 노출되어 있는 것이나 다름 없어요.

    NicknameValidateService가 닉네임의 길이를 검증할 때 MadiiUser의 내부 구조에 대해 알 필요는 전혀 없습니다. MadiiUser한테 너 닉네임 검증 결과가 어떻게 되니? 라는 메시지를 던지는 것으로 충분하겠죠? 이렇게 코드를 한번 리팩토링해볼게요.

    public class MadiiUser {
        private String loginId;
        private String password;
        private OnboardingInfo onboardingInfo;
        
    	public static class OnboardingInfo {
            private String nickname;
            private String image;
        }
        
        public boolean isNicknameShorterThan(final int length) {
        	if (this.onboardingInfo == null) {
            	throw new RuntimeException("온보딩하지 않은 유저입니다.");
            }
        
        	return this.onboardingInfo.nickname.length() < length;
        }
    }

     

    MadiiUser의 isNicknameShorterThan() 이라는 메서드를 구현했어요. length를 받아 더 짧은 지 리턴하는 간단한 메서드입니다.

    public class NicknameValidateService {
    	private static final NICKNAME_LENGTH_LIMIT = 10;
    	
    	public void validateNicknameLength(final MadiiUser madiiUser) {
        		if (!madiiUser.isNicknameShorterThan(NICKNAME_LENGTH_LIMIT) {
            		throw new IllegalArgumentException(String.format("닉네임은 %d자 이하여야 합니다.", NICKNAME_LENGTH_LIMIT);
            	}
        }
    }

     

    NicknameValidateService에서 이렇게 사용할 수 있겠죠? 아까와는 다르게, 디미터의 법칙을 준수한 코드라고 볼 수 있을 것 같아요.

    디미터의 법칙에 대해 설명할 때, 쉽게 한 줄에 하나의 점(.)만 찍어야 한다고 많이 설명하기도 해요. 객체와 객체 간의 관계에서는 하나의 점만 찍는 것이 맞지만, Stream API, 혹은 DTO나 자료구조를 사용할 때에는 해당되지 않으므로 이런 부분은 신경쓰지 않으시면 될 것 같습니다.

     

    다시 도메인 모델링으로

    도메인 모델링으로 글을 시작해서, 디미터의 법칙 관련 얘기가 좀 길었네요! 서론에서도 말씀 드렸듯, 더욱 객체지향적인 클린코드를 작성하기 위해 디미터의 법칙을 준수하고자 노력했지만, 기존의 방식으로는 좀 어려웠어요.

    기존에는 ERD를 설계한 뒤 JPA Entity로 옮겼고, 그 과정에서 필요한 간단한 함수만 도메인 로직으로 생각하고 구현했습니다.

    도메인에 대한 깊은 이해 없이 DB Query 기반으로 코드를 짰으니 당연히 객체지향과 같은 복잡한 개념을 반영하긴 어려웠습니다.

    JPA라는 아주 좋은 ORM을 사용하면서도 ORM의 가장 큰 장점인 RDBMS와 객체지향 사이의 매핑을 활용하지 않았습니다.

     

    그래서, 객체지향 기반으로 도메인 모델링을 꼼꼼히 진행한 뒤, 이를 JPA Entity에 반영함으로써 RDBMS로는 알아서 매핑될 수 있도록, 역순으로 진행해보고자 했어요.

     

    먼저 도메인 주도 개발 시작하기의 1장에 따라, MADII의 상위 수준 도메인 모델을 그려보았습니다.

    피그잼으로 그린 상위 수준 도메인 모델

    • 보라색 원: 소프트웨어를 통해 구현할 것
    • 하늘색 둥글이: 외부 시스템 혹은 인적 자원

    유비쿼터스 언어 사용을 지향하기 위해, 도메인 용어가 명확한 의미를 담도록 작명하려고 노력했어요.

    특히 영문으로 표기했을 때 너무 어려운 단어도, 너무 긴 단어도 부적절하다고 생각해서 고민을 많이 해서 작성했고, 타 파트 팀원들과도 용어에 대한 토의를 거쳤습니다.

     

    유비쿼터스 언어는 책에 다음과 같이 정의되어 있습니다.

    에릭 에반스는 도메인 주도 설계에서 언어의 중요함을 강조하기 위해 유비쿼터스 언어라는 용어를 사용했다. 전문가, 관계자, 개발자가 도메인과 관련된 공통의 언어를 만들고 이를 대화, 문서, 도메인 모델, 코드, 테스트 등 모든 곳에서 같은 용어를 사용한다. 이렇게 하면 소통 과정에서 발생하는 용어의 모호함을 줄일 수 있고 개발자는 도메인과 코드 사이에서 불필요한 해석 과정을 줄일 수 있다.

     

    먼저 위와 같이 상위 수준 도메인 모델을 그리고, 검증을 위해 요구사항, 제약사항을 줄글로 간단히 정리해보았습니다.

    개발자는 개발 프로세스가 진행되며 도메인을 더 잘 이해하게 된다라는 말이 더욱 와닿는 순간이었습니다.

    도메인 줄글로 정리해보기!

     

    이를 회의 시간에 팀원들과 공유하며 유비쿼터스 언어를 찾고자 노력해보았습니다!

    제가 이해한 것이 맞는지 팀원들과 얘기하며 수정되거나, 토의했던 흔적들이 보이네요 ㅎㅎ

     

    다음 글에는 이를 바탕으로 도메인 개념적 모델을 그려보고, JPA Entity로 이 모델을 표현하는 과정을 써보려고 해요.

    읽느라 수고하셨습니다! 

Designed by Tistory.