02. 도메인 헥사곤으로 비즈니스 규칙 감싸기

김민규·2023년 6월 8일
0
post-thumbnail

값 객체에서 찾을 수 없는 엔티티의 주요 속성은 무엇인가?

  • 엔티티는 헥사고날 아키텍처의 일급 객체(first-class citizen)다.
  • 고유한 식별자를 갖는다.

값 객체는 변경할 수 있는가?

  • 값 객체는 불변(Immutable)이다.
  • 값 객체는 폐기할 수 있어야 하고 엔티티나 다른 객체 타입을 구성하는 데 사용할 수 있는 쉽게 교체 가능한 객체여야 한다.

모든 애그리거트는 제어하는 다른 객체와 통신하기 위해 반드시 진입점 객체를 가져야 한다. 이러한 진입점 객체의 이름은 무엇인가?

  • 애그리거트 루트
    • 애그리거트의 일부인 엔티티와 값 객체들에 대한 참조를 유지한다.
    • 애그리거트 루트에서 해당 컨텍스트 하위의 모든 객체를 처리하는 책임을 위임한다.
    • networkSwitch 엔티티는 이 라우터에 직접 연결된 스위치를 나타내고, 두 개의 메서드를 추가했다.
      • addNetworkToSwitch()
      • createNetwork()
  public class Router {
	private final RouterType routerType;
    private final RouterId routerId;
    private Switch networkSwitch;
    
    public Router(RouterType routerType, RouterId routerId) {
    	this.routerType = routerType;
        this.routerId = routerId;
    }
    
    public static Predicate<Router> filterRouterByType(RouterType routerType) {
    	return routerType.equals(RouterType.CORE) ? Router.isCore() : Router.isEdge();
    }
    
    public static Predicate<Router> isCore() {
    	return p -> p.getRouterType() == RouterType.CORE;
    }
    
    public static Predicate<Router> isEdge() {
    	return p -> p.getRouterType() == RouterType.EDGE;
    }
    
    public void addNetworkToSwitch(Network network) {
    	this.networkSwitch = networkSwitch.addNetwork(network);
    }
    
    public Network createNetwork(IP address, String name, int cidr) {
    	return new Network(address, name, cidr);
    }
    
    public List<Network> retrieveNetworks() {
    	return networkSwitch.getNetworks();
    }
    
    public RouterType getRouterType() {
    	return routerType;
    }
}

도메인 서비스는 다른 헥사곤에 있는 객체들을 호출할 수 있는가?

  • 도메인 서비스가 애플리케이션 핵사곤이나 프레임워크 헥사곤에서 동작하는 서비스나 다른 객체를 호출해서는 안된다.
  • 반면 애플리케이션 핵사곤이나 프레임워크 헥사곤의 객체들은 도메인 서비스를 호출하는 클라이언트들이다.
public class NetworkOperation {

	private final int MINIMUM_ALLOWED_CIDR = 8;
    
    public void createNewNetwork(Router router, IP address, String name, int cidr) {
    	if (cidr < MINIMUM_ALLOWED_CIDR) {
        	throw new IllegalArgumentException("CIDR is below " + MINIMUM_ALLOWED_CIDR);
        }
        
        if (isNetworkdAvalilable(router, address)) {
        	throw new IllegalArgumentException("Address already exist);
        }
        
        Network network = router.createNetwork(address, name, cidr);
        router.addNetworkToSwitch(netwokr);
    }
    
    private boolean isNetworkAvailable(Router router, IP address) {
    	var availability = true;
        for (Network network : router.retrieveNetworks()) {
        	if (network.getAddress().equals(address) && network.getCidr() == cidr) {
            	availability = false;
                break;
            }
        }
        return availability;
    }
}
  • 새로운 네트워크 객체를 생성하고, 해당 객체를 라우터에 연결된 스위치에 추가하는 것을 담당한다.
  • 네트워크 생성시 제약사항을 검사하는 등 엔티티와 값 객체에 잘 어울리지 않는 작업을 처리한다.
  • 엔티티와 값 객체 클래스가 문제 영역을 따라 필요 이상으로 많은 기능을 가지고 너무 커지는 것을 방지하기 위해서다.

정책과 명세의 차이점은 무엇인가?

정책

  • 정책(Policy)은 전략(Straegy)으로도 알려져 있으며, 코드 블록으로 문제 영역의 일부를 캡슐화하는 패턴이다.
  • 정책의 특성은 제공된 데이터에 대해 어떤 작업이나 처리를 한다는 점이다.
  • 정책은 커플링을 피하기 위해 의도적으로 엔티티와 값 객체를 분리해 유지한다.

명세

  • 명세(Specification)는 객체의 특성을 보장하는 데 사용되는 조건(condition)이나 프레디케이트(predicate)와 같다.
  • 명세의 특징은 단순한 논리적인 연산자보다는 더 표현적인 방법으로 프레디케이트를 캡슐화한다.
public interface Specification<T> {
	boolean isSatisfiedBy(T t);
    Specification<T> and(Specification<T> specification);
}

POJO로 비즈니스 규칙을 정의하면 어떤 장점이 있는가?

  • POJO를 사용해 비즈니스 규칙을 모델링함으로써 POJO가 제공하는 재사용성과 단순성에 관련된 모든 이점을 활용할 수 있다.
  • POJO는 다른 기술적인 세부사항으로부터 도메인 객체를 보호하는 중요한 목표와도 관련이 있다.
  • 과도하지 않은 설계를 지원하는 데 필수적인 관심사의 분리에 기여할 것이다.
profile
Backend Engineer, Vim User

0개의 댓글