도메인 영역의 코드를 작성하다 보면 한 애그리거트로 기능 구현이 불가능할 때가 존재한다. 바로 결제 금액 계산 로직인데, 실 결제 금액을 계산할 때는 아래 정보들이 필요하다. 상품 애그리거트 : 구매하는 상품의 가격 필요 주문 애그리거트 : 상품별 구매 개수 필요 할인 쿠폰 애그리거트 : 쿠푠별로 지정한 할인 금액이나 비율에 따라 주문 총 금액을 할인하는 정보 필요 회원 애그리거트 : 회원 등급에 따른 추가 할인 정보 필요 그렇다면 실제 결제 금액을 계산해야하는 주체는 어떤 애그리거트 일까? if. 주문 애그리거트에서 필요한 데이터를 모두 가지게 한다면? (계산 책임을 주문 애그리거트에 할당) 할인 정책이 변경되었을 때 주문 애그리거트가 갖고 있는 구성 요소와 관련이 없음에도 결제 금액 계산 책임이 주문..
분류 전체보기
표현 영역(Presentation Layer) 사용자의 요청을 해석 사용자가 실행하고 싶은 기능을 판별하고 그 기능을 제공하는 응용 서비스를 실행 응용 서비스의 메서드가 요구하는 파라미터와 표현 영역이 사용자로부터 전달받은 데이터는 형식이 일치하지 않기 때문에 표현 영역은 서비스가 요구하는 형식으로 사용자 요청을 변환 필요 응용 영역(Application Layer) 실제 사용자가 원하는 기능을 제공 @PostMapping("/member/join") fun join(req: HttpServletRequest): ModelAndView { val emal = req.getParameter("email") val password = req.getParameter("password") // 요청을 응용 서비..
애그리거트? 도메인 객체 모델이 복잡해지면 개별 구성요소 위주로 모델을 이해하게 되고 전반적인 구조나 도메인 간의 관계를 파악하기 어려워진다. 관계 파악이 어렵다? 라는 의미는? 확장하기가 어렵다. 세부적인 모델만 이해한 상태로는 코드 수정이 꺼려진다. (전체 모델이 망가질 수 있으므로) 변경을 최대한 회피하는 쪽으로 요구사항을 협의하게 된다. 장기적으로는 코드를 수정하기 더 어렵게 만든다. 위는 애그리거트 단위로 모델을 묶어서 표현한 것 애그리거트를 사용함으로써 모델 간의 관계를 개별 모델 수준과 상위 수준에서 모두 이해가 가능하다. 모델 이해도를 증가시킴 일관성을 관리하는 기준이 됨 (한 애그리거트에 속한 객체는 유사하거나 동일한 LifeCycle을 갖는다.) 이것이 애그리거트의 필요성이다. 유의할 ..
Application Layer ? Domain Layer ? 앞에서 선택한 아키텍처를 사용하게 되면 응용 영역에서 사용자에게 제공해야할 기능을 구현하게 된다. 주문이란 하위 도메인을 예로 들면 주문 등록, 주문 취소, 상품 상세 조회와 같은 기능을 제공한다. 응용 영역(Application)은 기능을 구현하기 위해 도메인 영역에 존재하는 도메인 모델을 사용한다. class CancelOrderService { @Transactional fun cancelOrder(orderId: String) { val order = findOrderById(orderId) if (order == null) throw new OrderNotFoundException(orderId) order.cancel() // 위임..
도메인이란 무엇을 뜻할까? 도메인을 한 줄로 정의하자면 소프트웨어로 해결하고자 하는 문제 영역을 뜻한다. 만약, 우리가 온라인 서점이라는 서비스를 제공한다면 온라인 서점은 도메인(domain)이 될 수 있다. 그리고 도메인은 여러 하위 도메인으로 구성된다. 온라인 서점 (도메인) 주문 (하위 도메인) 정산 (하위 도메인) 배송 (하위 도메인) 결제 (하위 도메인) 고객이 책을 주문하고 결제하면 책이 배송된다. 이렇게 하위 도메인은 다른 도메인이랑 엮이면서 완전한 기능을 제공하게 된다. 도메인 모델? 다양한 정의가 존재하는데 아래 내용만 짚고 넘어가자 특정 도메인을 개념적으로 표현한 것 도메인이 제공하는 기능(메소드)와 주요 데이터(프로퍼티)를 가지고 있는 모델 도메인 계층을 구현할 때 사용하는 객체 모델..
고차함수? 다른 함수를 인자로 받거나 함수를 반환하는 함수(파라미터 혹은 반환 값으로 람다 사용) 함수 타입이란? 함수 타입은 아래와 같이 선언 // 타입 추론 val sum = { x: Int, y: Int -> x + y } // 구체적인 타입 선언 val sum: (Int, Int) -> Int = { x, y -> x + y } 인자로 받은 함수 호출 간단한 고차 함수 정의 fun twoAndThree(operation: (Int, Int) -> Int) { val result operation(2, 3) println("The result is $result") } 함수 타입 파라미터에 디폴트 값 지정 fun Collection.joinString( separator: String = ", ",..
kotlin 데이터 클래스는 구조 분해 선언(Destructuring Declaration)이라는 특성을 갖고 있다. 구조 분해를 사용하면 복합적인 값을 분해해서 여러 다른 변수를 한꺼번에 초기화할 수 있다. val p = Point(10, 20) val (x, y) = p // x => 10 // y => 20 구조 분해 선언의 각 변수를 초기화하기 위해 componentN이라는 함수를 호출한다. data 클래스의 주 생성자에 들어있는 프로퍼티에 대해서는 컴파일러가 자동으로 componentN함수를 만들어준다. val (x, y) = p // 위 코드 컴파일 후 val x = p.component1() val y = p.component2() // data 타입이 아닌 클래스에서 구현하는 방법 clas..
인덱스로 원소에 접근 - Map 원소 접근시 괄호([]) 사용 코틀린에서는 맵의 원소에 접근할 때 자바에서 배열 원소에 접근하는 각 괄호([])를 사용할 수 있다. 인덱스 연산자는 get/set 관례를 따르며, Map과 MutableMap 인터페이스에는 이미 두 메서드가 들어가 있다. val value = map[key] mutableMap[key] = newValue 구현 예시 operator fun Point.get(index: Int): Int { return when(index) { 0 -> x 1 -> y else -> throw IndexOutOfBoundsException("Invalid coordinate $index") } } val p = Point(10, 20) println(p[1]..
산술 연산자 오버로딩 이항 산술 연산 오버로딩 (+, -, *, /) 함수 앞에 operator 키워드를 붙이면 연산자 오버로딩이 가능하다. data class Point(val x: Int, val y: Int) { operator fun plus(other: Point): Point) { // plus 이름의 연산자 함수를 정의 return Point(x + other.x, y + other.y) } } val p1 = Point(10, 20) val p2 = Point(30, 40) println(p1 + p2) >> Point(x = 40, y = 60) >> p1 + p2 는 p1.plus(p2)로 컴파일 된다. 미리 정해둔 연산자만 오버로딩 가능 식 함수 a * b times a / b div..
압축 zip -e 압축파일.zip 압축할파일 zip -e attach.zip attach.txt Enter password : 암호를 두 번 입력 (두 번째는 verify) 압축 해제 unzip 압축파일.zip 암호 입력 후 해제