Git Product home page Git Product logo

first-ddd-study's People

Contributors

codeleesh avatar dldydtjs2965 avatar jongwans avatar rein-us avatar sjhello avatar

first-ddd-study's Issues

애그리거트의 정의

애그리거트에 대해 잘 이해가 되지 않아서 추가적으로 정리하고 공유드립니다.

마틴 파울러는 애그리거트를 다음과 같이 정의하는데

애그리거트는 데이터 저장소에 전송하기 위한 기본요소 입니다. 트랜잭션은 애그리거트의 경계를 벗어나지 못합니다.

DDD를 능숙하게 사용해본 사람은 잘 이해하겠지만 저에게는 무슨 의미인지 크게 와닿지 못했습니다. 그래서 더 추가적으로 찾아보고 정리했습니다.

애그리거트가 아닌 것

  • 엔티티의 그래프
  • 단순히 많은 역활을 가진 개체
  • 데이터베이스 테이블의 덤프할 수 있는 컬렉션 형태의 엔티티들이나 엔티티

버블

소프트웨어를 작은 버블의 집합체로 보면 알기 쉽게 이해할 수 있다. 각 버블은 독립적으로 작업할 수 있다. 어떤 순간 버블이 무엇을 하는지 생각하면 된다. 애그리거트도 마찬가지로 더 작은 규모의 버블이다.

Use Case: Team

시스템을 운영하기 위한 새로운 기능을 구축한다고 상상하고 새로운 기능에는 프로젝트, 팀, 구성원의 개념이 포함됩니다.
각 팀에는 관련된 여러 직원이 있을 수 있고 직원은 각 팀의 일원이 될 수 있고 팀은 여러 프로젝트를 가질 수 있습니다.
image

여기에서 엔티티 마다 존재할 수 있을만 한 필드를 추가하면 아래와 같다.
image

엔티티를 좀 더 비지니스의 개념으로 이해하고 엔티티가 무엇을 할 수 있을지 정의하면 아래와 같다.

  • 팀을 만들 수 있고
  • 팀을 편집 할 수 있다.
  • 팀을 삭제할 수 있다.
    여기에서 프로젝트를 참여하고 있는 팀을 삭제하면 무슨 일이 일어나야할까? 관련된 프로젝트도 삭제해야되고 참여하고 있던 팀원들도 삭제하게되고 아래와 같은 구조를 가지게 된다.
    image

실제 비지니스 정책 적용

하지만 비지니스의 로직은 그렇게 쉽게 풀리지 않습니다. 아래와 같은 문제가 발생할 수 도 있습니다.
image

  • 프로젝트가 다른팀으로 이동한다.
  • 팀원들이 다른 팀으로 모두 이동할 수 도 있다.
    그렇다면 우리는 어떻게 설계해야 할까?
  • 엔티티를 불러올 때 전체 객체 그래프를 메모리에 로드해야될까?
  • 프로젝트가 분리되면, 팀은 null 값의 프로젝틀르 참조만 가지게 되나?
    여기서 더 복잡한 비지니스 로직을 추가하게 되면 어떻게 봐야할까?
    image
  • 팀 구성원의 역활은 프로젝트별로 변경될 수 있다.
    그렇다면 우리는 어떻게 대응해야 될까?
  • 팀원을 프로젝트의 역활과 일치시키기 위해 복합테이블을 설계해야되나?
    새로운 요구사항이 추가될 때 마다 모델은 더 복잡해지고 시간이 지날수록 유지보수하기가 힘들어지고 복잡해집니다. 만약 팀원이 500명이 있는 프로젝트를 불러온다면 관련된 모든 엔티티도 메모리에 로드하기 때문에 성능적으로도 이슈가 생길 수 있다.

애그리거트의 역활

애그리거트는 이러한 종료의 문제를 해결하기 위한 것이다.

  • 통제할 수 없던 모델을 단순화하고 알아보기 쉽게 볼 수 있다.
  • 복잡한 비지니스 규칙을 분리한다.
  • 큰 엔티티의 그래프를 메모리에 로드할 때 성능 문제를 해결할 수 있다.
  • 미래의 비지니스 요구 사항에 대해 유연함을 허용할 수 있습니다.
    단순화된 엔티티의 구조
    image
  • 팀, 팀원
  • 프로젝트, 프로젝트 팀원,
  • 구성원

팀에게는 구성원에대한 이름 수정이나 경력수정이 필요하지 않습니다. 마찬가지로 프로젝트도 필요 없습니다. 하지만 서로 구성원을 참조하여 팀원과 프로젝트 팀원이 필요하니 개별적으로 참조하고 있는 구조로 변경하여 하나의 분기처럼 동작하도록 분할한 것입니다.

마틴파울러가 정의하는 애그리거트

Aggregate는 Domain-Driven Design의 패턴입니다. DDD 애그리거트는 단일 단위로 처리할 수 있는 도메인 개체의 클러스터입니다. 예를 들어 주문과 해당 라인 항목이 있을 수 있으며 이들은 별도의 개체가 되지만 주문(라인 항목과 함께)을 단일 애그리거트로 처리하는 것이 유용합니다.

애그리거트에는 구성 요소 개체 중 하나가 애그리거트 루트가 됩니다. 애그리거트 외부의 모든 참조는 애그리거트 루트로만 이동해야 합니다. 따라서 루트는 전체적으로 애그리거트의 무결성을 보장할 수 있습니다.

애그리거트는 데이터 저장소 전송의 기본 요소입니다. 전체 애그리거트를 로드하거나 저장하도록 요청합니다. 트랜잭션은 애그리거트 경계를 넘어서는 안 됩니다.

요약

애그리거트는 한마디로 도메인의 클러스터 역활을 하여 여러 도메인이 연관되어 하나의 도메인이 동작하는 것처럼 해주는 묶음인 것이다. 그렇기 때문에 하나의 트랜잭션은 애그리거트의 경계를 넘지 못하는 것이다.

도메인 규칙의 책임

도메인 규칙은 도메인안에서 정의하고 응용계층은 도메인의 흐름만 제어한다가 전제가 되었을 때
만약 도메인 규칙이 인프라스트럭처에 의존해야되는 상황일 때에도 무조건 도메인에 의존하도록 해야될지 궁금해서 이슈를 남깁니다.

처음에는 현재 비밀번호와 변경하고자하는 비밀번호가 같으면 안된다라는 도메인 규칙을 정의했을 때에는 아래와 같습니다.

    public void changePassword(String email, String newPassword) {
        Member member = memberRepository.findByEmail(email);
        existsMember(member);
        
        if (!member.matchPassword(newPassword)) {
            throw new WrongPasswordException();
        }
        
        member.changePassword(oldPassword, newPassword);
        memberRepository.save(member);
    }

추후에는 보안 정책이 강화됨에 따라 한달내에 변경했던 패스워드들은 변경하지 못한다고 도메인 규칙이 변경되고 한달동안 변경되었던 패스워드들은 Nosql형태의 다른 데이터 베이스에 저장한다고 가정 했을 때에는 서비스로직을 구현한다고 가정한다면 아래처럼 구현할 것 같습니다.

public void changePassword(String email, String newPassword) {
        Member member = memberRepository.findByEmail(email);
        existsMember(member);
        
        if (memberPasswordHistoryRepository.existsByEmailAndPassword(email, newPassword)) {
            throw new WrongPasswordException();
        }
        
        member.changePassword(oldPassword, newPassword);
        memberRepository.save(member);
    }

이러한 로직을 도메인으로 구현하다면 아래와 같은 구조로 구현이 된다면 아래처럼 구현이 될 것 같습니다.

public void changePassword(String email, String oldPassword, String newPassword) {
        Member member = memberRepository.findByEmail(email);
        existsMember(member);
        
        List<String> usedPasswords = memberPasswordHistoryRepository.findUsedPasswordsByEmail(email);

        if(member.isUsedPassword(usedPasswords, newPassword)) {
             throw new WrongPasswordException();
        }

        member.changePassword(oldPassword, newPassword);
        memberRepository.save(member);
}
  • 위에처럼 구현되어지면 결국 인프라스트럭처에서 한달동안 사용된 패스워드들을 가져와서 검증해야되기 때문에 응용 계층에서 사용한 것과 별차이가 없다고 생각을 합니다.
  • 중복코드를 줄이고 의존성을 줄이는 방법은 헬퍼 클래스를 통해 검증하도록 하도록 하거나 AOP를 통해 처리하는 방법이 있을 것 같은데 이렇게 되면 도메인에 대한 규칙이 결국 도메인이 아닌 외부로 분리되어 사용되어지기 때문에 한눈에 도메인이 어떤 규칙을 가지고 있는지 이해하기 어렵다. 라는 단점이 존재하게 되는데 이러한 상황에는 여러분들은 어떻게 생각하시는지 궁금합니다.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.