본문 바로가기

DDD/도메인 주도 설계 철저 입문

Entity Factory 패턴 - 「도메인 주도 설계 철저 입문」 11장 (2)

 

팩토리 패턴 1. 기본 생성자는 private

기본 생성자를 private으로 선언한다. 이렇게 하는 이유는 객체 생성을 팩토리를 통해서만 하도록 통제하기 위해서이다. 이때 주의해야 할 점은 기본 생성자를 아예 만들지 않으면 자바 컴파일러가 자동으로 기본 생성자를 생성하여 누구나 기본 생성자를 통한 User 생성할 수 있게 된다는 것이다. 따라서 생성자를 반드시 만들고, private으로 설정하는 것이 바람직하다.

package com.woojoovove.ddd11.user.domain;

public class User {
    private final UserId id;
    private UserName name;

    private User(UserId id, UserName name) {
        if (id == null) throw new IllegalArgumentException("id cannot be null");
        if (name == null) throw new IllegalArgumentException("name cannot be null");
        this.id = id;
        this.name = name;
    }
}

 

팩토리 패턴 2. 팩토리 인터페이스 선언

팩토리를 다음`IUserFactory`와 같이 인터페이스로 선언한다. 이렇게 하는 이유는

 

1. 객체를 생성할 때 디폴트로 전/후 처리가 필요한 경우에 인터페이스의 디폴트 메서드에 구현이 가능함.
2. 객체를 생성하는 여러 가지 방법을 인터페이스의 구현 클래스가 자유롭게 정의할 수 있게 함으로써 확장성을 높이기 위함이다.

 

 

예를 들어 객체 하나를 생성하기 위해 DB에 접근하는 팩토리와, 비즈니스 로직 테스트를 위해 DB를 띄우지 않는 `InMemoryFactory`가 둘 다 필요할 때는 각각 인터페이스의 구현 클래스를 선언하면 된다. 

package com.woojoovove.ddd11.user.domain;

public interface IUserFactory {
    User create(UserName userName);
}

 

 

 

팩토리 패턴 3. Entity에 정적 팩토리 메소드를 구현

다음 `create()`와 같이 Entity를 생성하는 정적 팩토리 메소드를 구현한다. Entity의 인스턴스가 없어도 사용할 수 있도록 정적(`static`) 메소드로 선언하며, 외부에서 사용할 수 있도록 `public`으로 열어둔다. 마지막으로 객체를 immutable하게 관리하기 위해서 `final` 키워드를 붙인다. 

 

package com.woojoovove.ddd11.user.domain;

public class User {
    private final UserId id;
    private UserName name;

    private User(UserId id, UserName name) {
        if (id == null) throw new IllegalArgumentException("id cannot be null");
        if (name == null) throw new IllegalArgumentException("name cannot be null");
        this.id = id;
        this.name = name;
    }

    public static final User create(UserId id, UserName name) {
        return new User(id, name);
    }
}

 

 

 

팩토리 패턴 4. 팩토리 인터페이스의 구현 클래스를 선언

다음은 `InMemoryUserFactory`이다. Entity의 `create()`를 사용하여 객체를 생성하고 반환한다. 테스트를 위한 InMemory 생성자로서 `UUID.randomUUID()`를 활용하여 id를 생성할 수 있게 구현하였다. 운영에서 사용할 RealUserFactory는 동일한 방식으로 `IUserFactory`를 다르게 구현할 수 있다. 

package com.woojoovove.ddd11.user.domain;

import java.util.UUID;

public class InMemoryUserFactory implements IUserFactory {

    @Override
    public User create(UserName userName) {
        UserId id = new UserId(UUID.randomUUID().toString());
        return User.create(id, userName);
    }
}