전체 글

초보 개발자의 지식 공유의 장
시스템 간 강결합은 어떻게 없앨 수 있을까? 쇼핑몰에서 구매 취소를 하면 환불 처리가 되어야 한다. 이때 환불기능을 실행하는 주체는 주문 도메인이 될 수 있으며, 환불 기능을 실행하기 위해서 환불 도메인 서비스를 파라미터로 전달받아 실행할 수 있다. class Order { // 외부 서비스 실행을 위해 도메인 서비스를 파라미터로 전달 받음 fun cancel(refundService: RefundService) { verifyNotYetShipped() this.state = OrderState.CANCELED this.refundStatus = State.REFUND_STARTED try { refundService.refund(getPaymentId()) this.refundStatus = Stat..
도메인 모델과 경계 도메인을 완벽하게 표현하는 단일 모델을 만드려는 시도를 하지 말자!! 한 도메인은 다시 여러 하위 도메인으로 구분되기 때문에 여러 하위 도메인을 모두 표현하려고하면 오히려 모든 하위 도메인에 맞지 않는 모델을 만들게 된다. 하위 도메인마다 같은 용어라도 의미가 다를 수 있음 하위 도메인마다 같은 대상이라도 지칭하는 용어가 다를 수 있음 따라서, 올바른 도메인 모델을 개발하려면 하위 도메인마다 모델을 만들어야한다. case1. 같은 용어라도 의미가 다른 경우 (feat. 상품 모델) 카탈로그 도메인에서의 상품 상품 이미지, 상품명, 상품 가격, 상세 설명 과 같은 상품 정보 위주 재고 관리 도메인에서의 상품 실존하는 개별 객체를 추적하기 위한 목적으로 사용 주문 도메인에서의 상품 배송 ..
도메인 영역의 코드를 작성하다 보면 한 애그리거트로 기능 구현이 불가능할 때가 존재한다. 바로 결제 금액 계산 로직인데, 실 결제 금액을 계산할 때는 아래 정보들이 필요하다. 상품 애그리거트 : 구매하는 상품의 가격 필요 주문 애그리거트 : 상품별 구매 개수 필요 할인 쿠폰 애그리거트 : 쿠푠별로 지정한 할인 금액이나 비율에 따라 주문 총 금액을 할인하는 정보 필요 회원 애그리거트 : 회원 등급에 따른 추가 할인 정보 필요 그렇다면 실제 결제 금액을 계산해야하는 주체는 어떤 애그리거트 일까? 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]..
깡냉쓰
평범한 개발자 노트