도메인 헥사곤
- 도메인 헥사곤은 실 세계 문제를 이해하고 모델링하는 활동을 나타낸다.
- 도메인 헥사곤 안에는 중요한 비즈니스 데이터와 규칙에 관련된 엔티티들이 있다.
- 엔티티(Entity)와 값 객체(Value Object)를 기반으로 도메인 헥사곤을 나타낸다.
엔티티
- 엔티티는 좀 더 표현력 있는 코드를 작성하는 데 도움을 준다.
- 연속성과 정체성은 엔티티를 결정하는 요소다.
public class Router {
private final RouterType routerType;
private final RouterId routerId;
public Router(RouterType routerType, RouterId routerId) {
this.routerType = routerType;
this.routerId = routerId;
}
public static Predicate<Router> filterRouterByType(RouterType routerType) {
return routerType.equals(RouterType.CORE)
? isCore() : isEdge();
}
private static Predicate<Router> isCore() {
return p -> p.getRouterType() == RouterType.CORE;
}
private static Predicate<Router> isEdge() {
return p -> p.getRouterType() == RouterType.EDGE;
}
public static List<Router> retrieveRouter(List<Router> routers, Predicate<Router> predicate) {
return routers.stream()
.filter(predicate)
.collect(Collectors.<Router>toList());
}
public RouterType getRouterType() {
return routerType;
}
}
값 객체
- 값 객체는 무언가 고유하게 식별할 필요가 없는 경우
- 객체의 정체성보다 속성에 관심을 갖는 경우에 도움이 된다.
public enum RouterType {
EDGE,
CORE;
}
애플리케이션 헥사곤
- 애플리케이션 특화 작업을 추상적으로 처리하는 곳이다.
- 유스케이스(Use Case), 입력포트(Input Port), 출력포트(Output Port)를 기반으로 한다.
유스케이스
- 도메인 제약사항을 지원하기 위해 시스템의 동작을 소프트웨어 영역 내에 존재하는 애플리케이션 특화 오퍼레이션을 통해 나타낸다.
- 유스케이스는 엔티티 및 다른 유스케이스와 직접 상호작용하고 그것들을 유연한 컴포넌트로 만들 수 있다.
public interface RouterViewUseCase {
List<Router> getRouters(Predicate<Router> filter);
}
입력 포트
- 입력 포트의 역할: 유스케이스가 소프트웨어가 하는 일을 설명하는 인터페이스라면 유스케이스 인터페이스를 구현해야 한다.
public class RouterViewInputPort implements RouterViewUseCase {
private RouterViewOutputPort routerListOutputPort;
public RouterViewInputPort(RouterViewOutputPort routerViewOutputPort) {
this.routerViewOutputPort = routerViewOutputPort;
}
@Override
List<Router> getRouters(Predicate<Router> filter) {
var routers = routerListOutputPort.fetchRouters();
return Router.retrieveRouter(routers, filter);
}
}
출력 포트
- 출력 포트의 역할: 유스케이스가 외부 리소스에서 데이터를 가져와야하는 상황이 있다.
public interface RouterViewOutputPort {
List<Router> fetchRouters();
}
프레임워크 헥사곤
- 소프트웨어와 통신할 수 있는 기술을 결정한다.
- 드라이빙(driving): 입력 어댑터(Input Adapter)
- 드리븐(driven): 출력 어댑터(Output Adapter)
드라이빙 오퍼레이션과 입력 어댑터
- 드라이빙 오퍼레이션(Driving operation)은 소프트웨어에 동작을 요청하는 것이다.
- SOAP, GRPC, STDIN, REST
public class RouterViewCLIAdapter {
RouterViewUseCase routerViewUseCase;
public RouterViewCLIAdapter() {
setAdapters();
}
public List<Router> obtainRelatedRouters(String type) {
return routerViewUseCase.getRouters(outer.filterRouterByType(RouterType.valueOf(type)));
}
private void setAdapters() {
this.routerViewUseCase = new RouterViewInputPort(RouterViewFileAdapter.getInstance());
}
드리븐 오퍼레이션과 출력 어댑터
- 이 오퍼레이션은 애플리케이션에서 트리거되고, 외부에서 데이터를 가져온다.
- DBMS, FILE, SMTP, OAUTH2
public class RouterViewFileAdapter implements RouterViewOutputPort {
@Override
public List<Router> fetchRouters() {
return readFileAsString();
}
private static List<Router> readFileAsString() {
List<Router> routers = new ArrayList<>();
try (Stream<String> stream = new BufferedReader(
new InputStreamReader(RouterViewFileAdapter.class.getClassLoader().
getResourceAsStream("routers.txt"))).lines()) {
stream.forEach(line -> {
String[] routerEntry = line.split(";");
var id = routerEntry[0];
var type = routerEntry[1];
Router router = new Router(RouterType.valueOf(type), RouterId.of(id));
routers.add(router);
});
} catch (Exception e) {
e.printStackTrace();
}
return routers;
}
}